Skip to content

Commit 13694f0

Browse files
committed
vfs: link_path_walk: move more of the name hashing into hash_name()
This avoids having to return the length of the component entirely by just doing all of the name processing in hash_name(). We can just return the end of the path component, and a flag for the DOT and DOTDOT cases. Signed-off-by: Linus Torvalds <[email protected]>
1 parent 58b0afa commit 13694f0

File tree

1 file changed

+32
-19
lines changed

1 file changed

+32
-19
lines changed

fs/namei.c

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2174,21 +2174,37 @@ EXPORT_SYMBOL(hashlen_string);
21742174
* Calculate the length and hash of the path component, and
21752175
* return the length as the result.
21762176
*/
2177-
static inline unsigned long hash_name(struct nameidata *nd,
2178-
const char *name,
2179-
unsigned long *lastword)
2177+
static inline const char *hash_name(struct nameidata *nd,
2178+
const char *name,
2179+
unsigned long *lastword)
21802180
{
2181-
unsigned long a = 0, b, x = 0, y = (unsigned long)nd->path.dentry;
2181+
unsigned long a, b, x, y = (unsigned long)nd->path.dentry;
21822182
unsigned long adata, bdata, mask, len;
21832183
const struct word_at_a_time constants = WORD_AT_A_TIME_CONSTANTS;
21842184

2185-
len = 0;
2186-
goto inside;
2185+
/*
2186+
* The first iteration is special, because it can result in
2187+
* '.' and '..' and has no mixing other than the final fold.
2188+
*/
2189+
a = load_unaligned_zeropad(name);
2190+
b = a ^ REPEAT_BYTE('/');
2191+
if (has_zero(a, &adata, &constants) | has_zero(b, &bdata, &constants)) {
2192+
adata = prep_zero_mask(a, adata, &constants);
2193+
bdata = prep_zero_mask(b, bdata, &constants);
2194+
mask = create_zero_mask(adata | bdata);
2195+
a &= zero_bytemask(mask);
2196+
*lastword = a;
2197+
len = find_zero(mask);
2198+
nd->last.hash = fold_hash(a, y);
2199+
nd->last.len = len;
2200+
return name + len;
2201+
}
21872202

2203+
len = 0;
2204+
x = 0;
21882205
do {
21892206
HASH_MIX(x, y, a);
21902207
len += sizeof(unsigned long);
2191-
inside:
21922208
a = load_unaligned_zeropad(name+len);
21932209
b = a ^ REPEAT_BYTE('/');
21942210
} while (!(has_zero(a, &adata, &constants) | has_zero(b, &bdata, &constants)));
@@ -2197,13 +2213,13 @@ static inline unsigned long hash_name(struct nameidata *nd,
21972213
bdata = prep_zero_mask(b, bdata, &constants);
21982214
mask = create_zero_mask(adata | bdata);
21992215
a &= zero_bytemask(mask);
2200-
*lastword = a;
22012216
x ^= a;
22022217
len += find_zero(mask);
2218+
*lastword = 0; // Multi-word components cannot be DOT or DOTDOT
22032219

22042220
nd->last.hash = fold_hash(x, y);
22052221
nd->last.len = len;
2206-
return len;
2222+
return name + len;
22072223
}
22082224

22092225
/*
@@ -2247,7 +2263,7 @@ EXPORT_SYMBOL(hashlen_string);
22472263
* We know there's a real path component here of at least
22482264
* one character.
22492265
*/
2250-
static inline unsigned long hash_name(struct nameidata *nd, const char *name, unsigned long *lastword)
2266+
static inline const char *hash_name(struct nameidata *nd, const char *name, unsigned long *lastword)
22512267
{
22522268
unsigned long hash = init_name_hash(nd->path.dentry);
22532269
unsigned long len = 0, c, last = 0;
@@ -2259,10 +2275,14 @@ static inline unsigned long hash_name(struct nameidata *nd, const char *name, un
22592275
hash = partial_name_hash(c, hash);
22602276
c = (unsigned char)name[len];
22612277
} while (c && c != '/');
2278+
2279+
// This is reliable for DOT or DOTDOT, since the component
2280+
// cannot contain NUL characters - top bits being zero means
2281+
// we cannot have had any other pathnames.
22622282
*lastword = last;
22632283
nd->last.hash = end_name_hash(hash);
22642284
nd->last.len = len;
2265-
return len;
2285+
return name + len;
22662286
}
22672287

22682288
#endif
@@ -2301,33 +2321,26 @@ static int link_path_walk(const char *name, struct nameidata *nd)
23012321
struct mnt_idmap *idmap;
23022322
const char *link;
23032323
unsigned long lastword;
2304-
unsigned int len;
23052324

23062325
idmap = mnt_idmap(nd->path.mnt);
23072326
err = may_lookup(idmap, nd);
23082327
if (err)
23092328
return err;
23102329

23112330
nd->last.name = name;
2312-
len = hash_name(nd, name, &lastword);
2313-
name += len;
2331+
name = hash_name(nd, name, &lastword);
23142332

23152333
switch(lastword) {
23162334
case LAST_WORD_IS_DOTDOT:
2317-
if (len != 2)
2318-
goto normal;
23192335
nd->last_type = LAST_DOTDOT;
23202336
nd->state |= ND_JUMPED;
23212337
break;
23222338

23232339
case LAST_WORD_IS_DOT:
2324-
if (len != 1)
2325-
goto normal;
23262340
nd->last_type = LAST_DOT;
23272341
break;
23282342

23292343
default:
2330-
normal:
23312344
nd->last_type = LAST_NORM;
23322345
nd->state &= ~ND_JUMPED;
23332346

0 commit comments

Comments
 (0)