Skip to content

Commit ba848a7

Browse files
committed
vfs: link_path_walk: do '.' and '..' detection while hashing
Instead of loading the name again to detect '.' and '..', just use the fact that we already had the masked last word available when as we created the name hash. Which is exactly what we'd then test for. Dealing with big-endian word ordering needs a bit of care, particularly since we have the byte-at-a-time loop as a fallback that doesn't do BE word loads. But not a big deal. Signed-off-by: Linus Torvalds <[email protected]>
1 parent 631e1a7 commit ba848a7

File tree

1 file changed

+46
-22
lines changed

1 file changed

+46
-22
lines changed

fs/namei.c

Lines changed: 46 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2163,9 +2163,11 @@ EXPORT_SYMBOL(hashlen_string);
21632163

21642164
/*
21652165
* Calculate the length and hash of the path component, and
2166-
* return the "hash_len" as the result.
2166+
* return the length as the result.
21672167
*/
2168-
static inline unsigned long hash_name(struct nameidata *nd, const char *name)
2168+
static inline unsigned long hash_name(struct nameidata *nd,
2169+
const char *name,
2170+
unsigned long *lastword)
21692171
{
21702172
unsigned long a = 0, b, x = 0, y = (unsigned long)nd->path.dentry;
21712173
unsigned long adata, bdata, mask, len;
@@ -2185,14 +2187,25 @@ static inline unsigned long hash_name(struct nameidata *nd, const char *name)
21852187
adata = prep_zero_mask(a, adata, &constants);
21862188
bdata = prep_zero_mask(b, bdata, &constants);
21872189
mask = create_zero_mask(adata | bdata);
2188-
x ^= a & zero_bytemask(mask);
2190+
a &= zero_bytemask(mask);
2191+
*lastword = a;
2192+
x ^= a;
21892193
len += find_zero(mask);
21902194

21912195
nd->last.hash = fold_hash(x, y);
21922196
nd->last.len = len;
21932197
return len;
21942198
}
21952199

2200+
/*
2201+
* Note that the 'last' word is always zero-masked, but
2202+
* was loaded as a possibly big-endian word.
2203+
*/
2204+
#ifdef __BIG_ENDIAN
2205+
#define LAST_WORD_IS_DOT (0x2eul << (BITS_PER_LONG-8))
2206+
#define LAST_WORD_IS_DOTDOT (0x2e2eul << (BITS_PER_LONG-16))
2207+
#endif
2208+
21962209
#else /* !CONFIG_DCACHE_WORD_ACCESS: Slow, byte-at-a-time version */
21972210

21982211
/* Return the hash of a string of known length */
@@ -2225,24 +2238,31 @@ EXPORT_SYMBOL(hashlen_string);
22252238
* We know there's a real path component here of at least
22262239
* one character.
22272240
*/
2228-
static inline unsigned long hash_name(struct nameidata *nd, const char *name)
2241+
static inline unsigned long hash_name(struct nameidata *nd, const char *name, unsigned long *lastword)
22292242
{
22302243
unsigned long hash = init_name_hash(nd->path.dentry);
2231-
unsigned long len = 0, c;
2244+
unsigned long len = 0, c, last = 0;
22322245

22332246
c = (unsigned char)*name;
22342247
do {
2248+
last = (last << 8) + c;
22352249
len++;
22362250
hash = partial_name_hash(c, hash);
22372251
c = (unsigned char)name[len];
22382252
} while (c && c != '/');
2253+
*lastword = last;
22392254
nd->last.hash = end_name_hash(hash);
22402255
nd->last.len = len;
22412256
return len;
22422257
}
22432258

22442259
#endif
22452260

2261+
#ifndef LAST_WORD_IS_DOT
2262+
#define LAST_WORD_IS_DOT 0x2e
2263+
#define LAST_WORD_IS_DOTDOT 0x2e2e
2264+
#endif
2265+
22462266
/*
22472267
* Name resolution.
22482268
* This is the basic name resolution function, turning a pathname into
@@ -2271,34 +2291,38 @@ static int link_path_walk(const char *name, struct nameidata *nd)
22712291
for(;;) {
22722292
struct mnt_idmap *idmap;
22732293
const char *link;
2294+
unsigned long lastword;
22742295
unsigned int len;
2275-
int type;
22762296

22772297
idmap = mnt_idmap(nd->path.mnt);
22782298
err = may_lookup(idmap, nd);
22792299
if (err)
22802300
return err;
22812301

22822302
nd->last.name = name;
2283-
len = hash_name(nd, name);
2303+
len = hash_name(nd, name, &lastword);
22842304
name += len;
22852305

2286-
type = LAST_NORM;
2287-
/* We know len is at least 1, so compare against 2 */
2288-
if (len <= 2 && name[-1] == '.') {
2289-
if (len == 2) {
2290-
if (name[-2] == '.') {
2291-
type = LAST_DOTDOT;
2292-
nd->state |= ND_JUMPED;
2293-
}
2294-
} else {
2295-
type = LAST_DOT;
2296-
}
2297-
}
2298-
nd->last_type = type;
2299-
if (likely(type == LAST_NORM)) {
2300-
struct dentry *parent = nd->path.dentry;
2306+
switch(lastword) {
2307+
case LAST_WORD_IS_DOTDOT:
2308+
if (len != 2)
2309+
goto normal;
2310+
nd->last_type = LAST_DOTDOT;
2311+
nd->state |= ND_JUMPED;
2312+
break;
2313+
2314+
case LAST_WORD_IS_DOT:
2315+
if (len != 1)
2316+
goto normal;
2317+
nd->last_type = LAST_DOT;
2318+
break;
2319+
2320+
default:
2321+
normal:
2322+
nd->last_type = LAST_NORM;
23012323
nd->state &= ~ND_JUMPED;
2324+
2325+
struct dentry *parent = nd->path.dentry;
23022326
if (unlikely(parent->d_flags & DCACHE_OP_HASH)) {
23032327
err = parent->d_op->d_hash(parent, &nd->last);
23042328
if (err < 0)

0 commit comments

Comments
 (0)