diff options
Diffstat (limited to 'fs/namei.c')
-rw-r--r-- | fs/namei.c | 82 |
1 files changed, 33 insertions, 49 deletions
diff --git a/fs/namei.c b/fs/namei.c index 70580ab1445c..68a896c804b7 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1449,9 +1449,8 @@ static int follow_dotdot(struct nameidata *nd) } /* - * This looks up the name in dcache, possibly revalidates the old dentry and - * allocates a new one if not found or not valid. In the need_lookup argument - * returns whether i_op->lookup is necessary. + * This looks up the name in dcache and possibly revalidates the found dentry. + * NULL is returned if the dentry does not exist in the cache. */ static struct dentry *lookup_dcache(const struct qstr *name, struct dentry *dir, @@ -1890,9 +1889,9 @@ static inline unsigned int fold_hash(unsigned long x, unsigned long y) * payload bytes, to match the way that hash_name() iterates until it * finds the delimiter after the name. */ -unsigned int full_name_hash(const char *name, unsigned int len) +unsigned int full_name_hash(const void *salt, const char *name, unsigned int len) { - unsigned long a, x = 0, y = 0; + unsigned long a, x = 0, y = (unsigned long)salt; for (;;) { if (!len) @@ -1911,15 +1910,19 @@ done: EXPORT_SYMBOL(full_name_hash); /* Return the "hash_len" (hash and length) of a null-terminated string */ -u64 hashlen_string(const char *name) +u64 hashlen_string(const void *salt, const char *name) { - unsigned long a = 0, x = 0, y = 0, adata, mask, len; + unsigned long a = 0, x = 0, y = (unsigned long)salt; + unsigned long adata, mask, len; const struct word_at_a_time constants = WORD_AT_A_TIME_CONSTANTS; - len = -sizeof(unsigned long); + len = 0; + goto inside; + do { HASH_MIX(x, y, a); len += sizeof(unsigned long); +inside: a = load_unaligned_zeropad(name+len); } while (!has_zero(a, &adata, &constants)); @@ -1935,15 +1938,19 @@ EXPORT_SYMBOL(hashlen_string); * Calculate the length and hash of the path component, and * return the "hash_len" as the result. */ -static inline u64 hash_name(const char *name) +static inline u64 hash_name(const void *salt, const char *name) { - unsigned long a = 0, b, x = 0, y = 0, adata, bdata, mask, len; + unsigned long a = 0, b, x = 0, y = (unsigned long)salt; + unsigned long adata, bdata, mask, len; const struct word_at_a_time constants = WORD_AT_A_TIME_CONSTANTS; - len = -sizeof(unsigned long); + len = 0; + goto inside; + do { HASH_MIX(x, y, a); len += sizeof(unsigned long); +inside: a = load_unaligned_zeropad(name+len); b = a ^ REPEAT_BYTE('/'); } while (!(has_zero(a, &adata, &constants) | has_zero(b, &bdata, &constants))); @@ -1959,9 +1966,9 @@ static inline u64 hash_name(const char *name) #else /* !CONFIG_DCACHE_WORD_ACCESS: Slow, byte-at-a-time version */ /* Return the hash of a string of known length */ -unsigned int full_name_hash(const char *name, unsigned int len) +unsigned int full_name_hash(const void *salt, const char *name, unsigned int len) { - unsigned long hash = init_name_hash(); + unsigned long hash = init_name_hash(salt); while (len--) hash = partial_name_hash((unsigned char)*name++, hash); return end_name_hash(hash); @@ -1969,9 +1976,9 @@ unsigned int full_name_hash(const char *name, unsigned int len) EXPORT_SYMBOL(full_name_hash); /* Return the "hash_len" (hash and length) of a null-terminated string */ -u64 hashlen_string(const char *name) +u64 hashlen_string(const void *salt, const char *name) { - unsigned long hash = init_name_hash(); + unsigned long hash = init_name_hash(salt); unsigned long len = 0, c; c = (unsigned char)*name; @@ -1988,9 +1995,9 @@ EXPORT_SYMBOL(hashlen_string); * We know there's a real path component here of at least * one character. */ -static inline u64 hash_name(const char *name) +static inline u64 hash_name(const void *salt, const char *name) { - unsigned long hash = init_name_hash(); + unsigned long hash = init_name_hash(salt); unsigned long len = 0, c; c = (unsigned char)*name; @@ -2030,7 +2037,7 @@ static int link_path_walk(const char *name, struct nameidata *nd) if (err) return err; - hash_len = hash_name(name); + hash_len = hash_name(nd->path.dentry, name); type = LAST_NORM; if (name[0] == '.') switch (hashlen_len(hash_len)) { @@ -2389,33 +2396,6 @@ int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt, EXPORT_SYMBOL(vfs_path_lookup); /** - * lookup_hash - lookup single pathname component on already hashed name - * @name: name and hash to lookup - * @base: base directory to lookup from - * - * The name must have been verified and hashed (see lookup_one_len()). Using - * this after just full_name_hash() is unsafe. - * - * This function also doesn't check for search permission on base directory. - * - * Use lookup_one_len_unlocked() instead, unless you really know what you are - * doing. - * - * Do not hold i_mutex; this helper takes i_mutex if necessary. - */ -struct dentry *lookup_hash(const struct qstr *name, struct dentry *base) -{ - struct dentry *ret; - - ret = lookup_dcache(name, base, 0); - if (!ret) - ret = lookup_slow(name, base, 0); - - return ret; -} -EXPORT_SYMBOL(lookup_hash); - -/** * lookup_one_len - filesystem helper to lookup single pathname component * @name: pathname component to lookup * @base: base directory to lookup from @@ -2436,7 +2416,7 @@ struct dentry *lookup_one_len(const char *name, struct dentry *base, int len) this.name = name; this.len = len; - this.hash = full_name_hash(name, len); + this.hash = full_name_hash(base, name, len); if (!len) return ERR_PTR(-EACCES); @@ -2486,10 +2466,11 @@ struct dentry *lookup_one_len_unlocked(const char *name, struct qstr this; unsigned int c; int err; + struct dentry *ret; this.name = name; this.len = len; - this.hash = full_name_hash(name, len); + this.hash = full_name_hash(base, name, len); if (!len) return ERR_PTR(-EACCES); @@ -2517,7 +2498,10 @@ struct dentry *lookup_one_len_unlocked(const char *name, if (err) return ERR_PTR(err); - return lookup_hash(&this, base); + ret = lookup_dcache(&this, base, 0); + if (!ret) + ret = lookup_slow(&this, base, 0); + return ret; } EXPORT_SYMBOL(lookup_one_len_unlocked); @@ -4328,7 +4312,7 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry, * Check source == target. * On overlayfs need to look at underlying inodes. */ - if (vfs_select_inode(old_dentry, 0) == vfs_select_inode(new_dentry, 0)) + if (d_real_inode(old_dentry) == d_real_inode(new_dentry)) return 0; error = may_delete(old_dir, old_dentry, is_dir); |