From 8cef9c67356eca3e6502444c8075a06c86872abf Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Tue, 22 Jun 2010 11:15:01 +1000 Subject: [PATCH 01/13] v9fs: fixup for inode_setattr being removed Signed-off-by: Stephen Rothwell Signed-off-by: Al Viro --- fs/9p/vfs_inode.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index d97c34a24f7a..c7c23eab9440 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -1263,10 +1263,19 @@ static int v9fs_vfs_setattr_dotl(struct dentry *dentry, struct iattr *iattr) return PTR_ERR(fid); retval = p9_client_setattr(fid, &p9attr); - if (retval >= 0) - retval = inode_setattr(dentry->d_inode, iattr); + if (retval < 0) + return retval; - return retval; + if ((iattr->ia_valid & ATTR_SIZE) && + iattr->ia_size != i_size_read(dentry->d_inode)) { + retval = vmtruncate(dentry->d_inode, iattr->ia_size); + if (retval) + return retval; + } + + setattr_copy(dentry->d_inode, iattr); + mark_inode_dirty(dentry->d_inode); + return 0; } /** From b76212d7f19420ab29d86e9d17d1ff36cfe0f922 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Mon, 19 Jul 2010 19:16:40 +0200 Subject: [PATCH 02/13] Add v7 alias So that the module gets autoloaded when a v7 filesystem is mounted. Cc: Christoph Hellwig Signed-off-by: Lubomir Rintel Signed-off-by: Al Viro --- fs/sysv/super.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/sysv/super.c b/fs/sysv/super.c index 0e44a6253352..2da3075aff78 100644 --- a/fs/sysv/super.c +++ b/fs/sysv/super.c @@ -559,4 +559,5 @@ static void __exit exit_sysv_fs(void) module_init(init_sysv_fs) module_exit(exit_sysv_fs) +MODULE_ALIAS("v7"); MODULE_LICENSE("GPL"); From 496ee9b8f349a8ae2065114c414a47e89bdeb930 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Thu, 22 Jul 2010 03:11:48 +0200 Subject: [PATCH 03/13] V7: Adjust sanity checks for some volumes Newly mkfs-ed filesystems from Seventh Edition have last modification time set to zero, but are otherwise perfectly valid. Also, tighten up other sanity checks to filter out most filesystems with different bytesex than we're using. Cc: Christoph Hellwig Signed-off-by: Lubomir Rintel Signed-off-by: Al Viro --- fs/sysv/super.c | 6 ++++-- include/linux/sysv_fs.h | 11 +++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/fs/sysv/super.c b/fs/sysv/super.c index 2da3075aff78..5c0aab0b7e18 100644 --- a/fs/sysv/super.c +++ b/fs/sysv/super.c @@ -469,7 +469,7 @@ static int v7_fill_super(struct super_block *sb, void *data, int silent) v7sb = (struct v7_super_block *) bh->b_data; if (fs16_to_cpu(sbi, v7sb->s_nfree) > V7_NICFREE || fs16_to_cpu(sbi, v7sb->s_ninode) > V7_NICINOD || - fs32_to_cpu(sbi, v7sb->s_time) == 0) + fs32_to_cpu(sbi, v7sb->s_fsize) > V7_MAXSIZE) goto failed; /* plausibility check on root inode: it is a directory, @@ -479,7 +479,9 @@ static int v7_fill_super(struct super_block *sb, void *data, int silent) v7i = (struct sysv_inode *)(bh2->b_data + 64); if ((fs16_to_cpu(sbi, v7i->i_mode) & ~0777) != S_IFDIR || (fs32_to_cpu(sbi, v7i->i_size) == 0) || - (fs32_to_cpu(sbi, v7i->i_size) & 017) != 0) + (fs32_to_cpu(sbi, v7i->i_size) & 017) || + (fs32_to_cpu(sbi, v7i->i_size) > V7_NFILES * + sizeof (struct sysv_dir_entry))) goto failed; brelse(bh2); bh2 = NULL; diff --git a/include/linux/sysv_fs.h b/include/linux/sysv_fs.h index 96411306eec6..e47d6d90023d 100644 --- a/include/linux/sysv_fs.h +++ b/include/linux/sysv_fs.h @@ -148,6 +148,17 @@ struct v7_super_block { char s_fname[6]; /* file system name */ char s_fpack[6]; /* file system pack name */ }; +/* Constants to aid sanity checking */ +/* This is not a hard limit, nor enforced by v7 kernel. It's actually just + * the limit used by Seventh Edition's ls, though is high enough to assume + * that no reasonable file system would have that much entries in root + * directory. Thus, if we see anything higher, we just probably got the + * endiannes wrong. */ +#define V7_NFILES 1024 +/* The disk addresses are three-byte (despite direct block addresses being + * aligned word-wise in inode). If the most significant byte is non-zero, + * something is most likely wrong (not a filesystem, bad bytesex). */ +#define V7_MAXSIZE 0x00ffffff /* Coherent super-block data on disk */ #define COH_NICINOD 100 /* number of inode cache entries */ From 6d0b5456e14ec19edae7c18de4d355c58b133bd6 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Tue, 10 Aug 2010 18:03:34 -0700 Subject: [PATCH 04/13] fs/sysv/super.c: add support for non-PDP11 v7 filesystems This adds byte order autodetection (of PDP-11 and LE filesystems). No attempt is made to detect big-endian filesystems -- were there any? Tested with PDP-11 v7 filesystems and PC-IX maintenance floppy. [akpm@linux-foundation.org: coding-style fixes] [AV: parser.h inclusion was a rudiment of discarded stuff] Signed-off-by: Lubomir Rintel Cc: Christoph Hellwig Cc: Al Viro Signed-off-by: Andrew Morton Signed-off-by: Al Viro --- fs/sysv/super.c | 74 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 50 insertions(+), 24 deletions(-) diff --git a/fs/sysv/super.c b/fs/sysv/super.c index 5c0aab0b7e18..a0b0cda6927e 100644 --- a/fs/sysv/super.c +++ b/fs/sysv/super.c @@ -434,12 +434,46 @@ Ebadsize: goto failed; } +static int v7_sanity_check(struct super_block *sb, struct buffer_head *bh) +{ + struct v7_super_block *v7sb; + struct sysv_inode *v7i; + struct buffer_head *bh2; + struct sysv_sb_info *sbi; + + sbi = sb->s_fs_info; + + /* plausibility check on superblock */ + v7sb = (struct v7_super_block *) bh->b_data; + if (fs16_to_cpu(sbi, v7sb->s_nfree) > V7_NICFREE || + fs16_to_cpu(sbi, v7sb->s_ninode) > V7_NICINOD || + fs32_to_cpu(sbi, v7sb->s_fsize) > V7_MAXSIZE) + return 0; + + /* plausibility check on root inode: it is a directory, + with a nonzero size that is a multiple of 16 */ + bh2 = sb_bread(sb, 2); + if (bh2 == NULL) + return 0; + + v7i = (struct sysv_inode *)(bh2->b_data + 64); + if ((fs16_to_cpu(sbi, v7i->i_mode) & ~0777) != S_IFDIR || + (fs32_to_cpu(sbi, v7i->i_size) == 0) || + (fs32_to_cpu(sbi, v7i->i_size) & 017) || + (fs32_to_cpu(sbi, v7i->i_size) > V7_NFILES * + sizeof(struct sysv_dir_entry))) { + brelse(bh2); + return 0; + } + + brelse(bh2); + return 1; +} + static int v7_fill_super(struct super_block *sb, void *data, int silent) { struct sysv_sb_info *sbi; - struct buffer_head *bh, *bh2 = NULL; - struct v7_super_block *v7sb; - struct sysv_inode *v7i; + struct buffer_head *bh; if (440 != sizeof (struct v7_super_block)) panic("V7 FS: bad super-block size"); @@ -453,7 +487,6 @@ static int v7_fill_super(struct super_block *sb, void *data, int silent) sbi->s_sb = sb; sbi->s_block_base = 0; sbi->s_type = FSTYPE_V7; - sbi->s_bytesex = BYTESEX_PDP; sb->s_fs_info = sbi; sb_set_blocksize(sb, 512); @@ -465,34 +498,27 @@ static int v7_fill_super(struct super_block *sb, void *data, int silent) goto failed; } - /* plausibility check on superblock */ - v7sb = (struct v7_super_block *) bh->b_data; - if (fs16_to_cpu(sbi, v7sb->s_nfree) > V7_NICFREE || - fs16_to_cpu(sbi, v7sb->s_ninode) > V7_NICINOD || - fs32_to_cpu(sbi, v7sb->s_fsize) > V7_MAXSIZE) - goto failed; + /* Try PDP-11 UNIX */ + sbi->s_bytesex = BYTESEX_PDP; + if (v7_sanity_check(sb, bh)) + goto detected; - /* plausibility check on root inode: it is a directory, - with a nonzero size that is a multiple of 16 */ - if ((bh2 = sb_bread(sb, 2)) == NULL) - goto failed; - v7i = (struct sysv_inode *)(bh2->b_data + 64); - if ((fs16_to_cpu(sbi, v7i->i_mode) & ~0777) != S_IFDIR || - (fs32_to_cpu(sbi, v7i->i_size) == 0) || - (fs32_to_cpu(sbi, v7i->i_size) & 017) || - (fs32_to_cpu(sbi, v7i->i_size) > V7_NFILES * - sizeof (struct sysv_dir_entry))) - goto failed; - brelse(bh2); - bh2 = NULL; + /* Try PC/IX, v7/x86 */ + sbi->s_bytesex = BYTESEX_LE; + if (v7_sanity_check(sb, bh)) + goto detected; + goto failed; + +detected: sbi->s_bh1 = bh; sbi->s_bh2 = bh; if (complete_read_super(sb, silent, 1)) return 0; failed: - brelse(bh2); + printk(KERN_ERR "VFS: could not find a valid V7 on %s.\n", + sb->s_id); brelse(bh); kfree(sbi); return -EINVAL; From 542ce7a9bc6b3838832ae0f4f8de30c667af8ff3 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Tue, 10 Aug 2010 11:41:35 +0200 Subject: [PATCH 05/13] cachefiles: use path_get instead of lone dget Dentry references should not be acquired without a corresponding vfsmount ref. Signed-off-by: Miklos Szeredi Acked-by: David Howells Signed-off-by: Al Viro --- fs/cachefiles/daemon.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/fs/cachefiles/daemon.c b/fs/cachefiles/daemon.c index 24eb0d37241a..72d4d0042826 100644 --- a/fs/cachefiles/daemon.c +++ b/fs/cachefiles/daemon.c @@ -553,7 +553,7 @@ static int cachefiles_daemon_tag(struct cachefiles_cache *cache, char *args) static int cachefiles_daemon_cull(struct cachefiles_cache *cache, char *args) { struct fs_struct *fs; - struct dentry *dir; + struct path path; const struct cred *saved_cred; int ret; @@ -575,22 +575,23 @@ static int cachefiles_daemon_cull(struct cachefiles_cache *cache, char *args) /* extract the directory dentry from the cwd */ fs = current->fs; read_lock(&fs->lock); - dir = dget(fs->pwd.dentry); + path = fs->pwd; + path_get(&path); read_unlock(&fs->lock); - if (!S_ISDIR(dir->d_inode->i_mode)) + if (!S_ISDIR(path.dentry->d_inode->i_mode)) goto notdir; cachefiles_begin_secure(cache, &saved_cred); - ret = cachefiles_cull(cache, dir, args); + ret = cachefiles_cull(cache, path.dentry, args); cachefiles_end_secure(cache, saved_cred); - dput(dir); + path_put(&path); _leave(" = %d", ret); return ret; notdir: - dput(dir); + path_put(&path); kerror("cull command requires dirfd to be a directory"); return -ENOTDIR; @@ -629,7 +630,7 @@ inval: static int cachefiles_daemon_inuse(struct cachefiles_cache *cache, char *args) { struct fs_struct *fs; - struct dentry *dir; + struct path path; const struct cred *saved_cred; int ret; @@ -651,22 +652,23 @@ static int cachefiles_daemon_inuse(struct cachefiles_cache *cache, char *args) /* extract the directory dentry from the cwd */ fs = current->fs; read_lock(&fs->lock); - dir = dget(fs->pwd.dentry); + path = fs->pwd; + path_get(&path); read_unlock(&fs->lock); - if (!S_ISDIR(dir->d_inode->i_mode)) + if (!S_ISDIR(path.dentry->d_inode->i_mode)) goto notdir; cachefiles_begin_secure(cache, &saved_cred); - ret = cachefiles_check_in_use(cache, dir, args); + ret = cachefiles_check_in_use(cache, path.dentry, args); cachefiles_end_secure(cache, saved_cred); - dput(dir); + path_put(&path); //_leave(" = %d", ret); return ret; notdir: - dput(dir); + path_put(&path); kerror("inuse command requires dirfd to be a directory"); return -ENOTDIR; From f7ad3c6be90809b53b7f0ae9d4eaa45ce2564a79 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Tue, 10 Aug 2010 11:41:36 +0200 Subject: [PATCH 06/13] vfs: add helpers to get root and pwd Add three helpers that retrieve a refcounted copy of the root and cwd from the supplied fs_struct. get_fs_root() get_fs_pwd() get_fs_root_and_pwd() Signed-off-by: Miklos Szeredi Signed-off-by: Al Viro --- fs/cachefiles/daemon.c | 14 ++------------ fs/dcache.c | 12 ++---------- fs/fs_struct.c | 7 +------ fs/namei.c | 15 +++------------ fs/namespace.c | 5 +---- fs/proc/base.c | 22 +++++++++++----------- include/linux/fs_struct.h | 27 +++++++++++++++++++++++++++ kernel/auditsc.c | 9 ++------- 8 files changed, 49 insertions(+), 62 deletions(-) diff --git a/fs/cachefiles/daemon.c b/fs/cachefiles/daemon.c index 72d4d0042826..727caedcdd92 100644 --- a/fs/cachefiles/daemon.c +++ b/fs/cachefiles/daemon.c @@ -552,7 +552,6 @@ static int cachefiles_daemon_tag(struct cachefiles_cache *cache, char *args) */ static int cachefiles_daemon_cull(struct cachefiles_cache *cache, char *args) { - struct fs_struct *fs; struct path path; const struct cred *saved_cred; int ret; @@ -573,11 +572,7 @@ static int cachefiles_daemon_cull(struct cachefiles_cache *cache, char *args) } /* extract the directory dentry from the cwd */ - fs = current->fs; - read_lock(&fs->lock); - path = fs->pwd; - path_get(&path); - read_unlock(&fs->lock); + get_fs_pwd(current->fs, &path); if (!S_ISDIR(path.dentry->d_inode->i_mode)) goto notdir; @@ -629,7 +624,6 @@ inval: */ static int cachefiles_daemon_inuse(struct cachefiles_cache *cache, char *args) { - struct fs_struct *fs; struct path path; const struct cred *saved_cred; int ret; @@ -650,11 +644,7 @@ static int cachefiles_daemon_inuse(struct cachefiles_cache *cache, char *args) } /* extract the directory dentry from the cwd */ - fs = current->fs; - read_lock(&fs->lock); - path = fs->pwd; - path_get(&path); - read_unlock(&fs->lock); + get_fs_pwd(current->fs, &path); if (!S_ISDIR(path.dentry->d_inode->i_mode)) goto notdir; diff --git a/fs/dcache.c b/fs/dcache.c index 9f2c13417969..995d08069d26 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -2014,10 +2014,7 @@ char *d_path(const struct path *path, char *buf, int buflen) if (path->dentry->d_op && path->dentry->d_op->d_dname) return path->dentry->d_op->d_dname(path->dentry, buf, buflen); - read_lock(¤t->fs->lock); - root = current->fs->root; - path_get(&root); - read_unlock(¤t->fs->lock); + get_fs_root(current->fs, &root); spin_lock(&dcache_lock); tmp = root; res = __d_path(path, &tmp, buf, buflen); @@ -2129,12 +2126,7 @@ SYSCALL_DEFINE2(getcwd, char __user *, buf, unsigned long, size) if (!page) return -ENOMEM; - read_lock(¤t->fs->lock); - pwd = current->fs->pwd; - path_get(&pwd); - root = current->fs->root; - path_get(&root); - read_unlock(¤t->fs->lock); + get_fs_root_and_pwd(current->fs, &root, &pwd); error = -ENOENT; spin_lock(&dcache_lock); diff --git a/fs/fs_struct.c b/fs/fs_struct.c index eee059052db5..1ee40eb9a2c0 100644 --- a/fs/fs_struct.c +++ b/fs/fs_struct.c @@ -106,12 +106,7 @@ struct fs_struct *copy_fs_struct(struct fs_struct *old) fs->in_exec = 0; rwlock_init(&fs->lock); fs->umask = old->umask; - read_lock(&old->lock); - fs->root = old->root; - path_get(&old->root); - fs->pwd = old->pwd; - path_get(&old->pwd); - read_unlock(&old->lock); + get_fs_root_and_pwd(old, &fs->root, &fs->pwd); } return fs; } diff --git a/fs/namei.c b/fs/namei.c index 13ff4abdbdca..17ea76bf2fbe 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -483,13 +483,8 @@ ok: static __always_inline void set_root(struct nameidata *nd) { - if (!nd->root.mnt) { - struct fs_struct *fs = current->fs; - read_lock(&fs->lock); - nd->root = fs->root; - path_get(&nd->root); - read_unlock(&fs->lock); - } + if (!nd->root.mnt) + get_fs_root(current->fs, &nd->root); } static int link_path_walk(const char *, struct nameidata *); @@ -1015,11 +1010,7 @@ static int path_init(int dfd, const char *name, unsigned int flags, struct namei nd->path = nd->root; path_get(&nd->root); } else if (dfd == AT_FDCWD) { - struct fs_struct *fs = current->fs; - read_lock(&fs->lock); - nd->path = fs->pwd; - path_get(&fs->pwd); - read_unlock(&fs->lock); + get_fs_pwd(current->fs, &nd->path); } else { struct dentry *dentry; diff --git a/fs/namespace.c b/fs/namespace.c index 66c4f7e781cb..7972e51ccc06 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -2213,10 +2213,7 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root, goto out1; } - read_lock(¤t->fs->lock); - root = current->fs->root; - path_get(¤t->fs->root); - read_unlock(¤t->fs->lock); + get_fs_root(current->fs, &root); down_write(&namespace_sem); mutex_lock(&old.dentry->d_inode->i_mutex); error = -EINVAL; diff --git a/fs/proc/base.c b/fs/proc/base.c index c806dfb24e08..4cf5abf77ddf 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -149,18 +149,13 @@ static unsigned int pid_entry_count_dirs(const struct pid_entry *entries, return count; } -static int get_fs_path(struct task_struct *task, struct path *path, bool root) +static int get_task_root(struct task_struct *task, struct path *root) { - struct fs_struct *fs; int result = -ENOENT; task_lock(task); - fs = task->fs; - if (fs) { - read_lock(&fs->lock); - *path = root ? fs->root : fs->pwd; - path_get(path); - read_unlock(&fs->lock); + if (task->fs) { + get_fs_root(task->fs, root); result = 0; } task_unlock(task); @@ -173,7 +168,12 @@ static int proc_cwd_link(struct inode *inode, struct path *path) int result = -ENOENT; if (task) { - result = get_fs_path(task, path, 0); + task_lock(task); + if (task->fs) { + get_fs_pwd(task->fs, path); + result = 0; + } + task_unlock(task); put_task_struct(task); } return result; @@ -185,7 +185,7 @@ static int proc_root_link(struct inode *inode, struct path *path) int result = -ENOENT; if (task) { - result = get_fs_path(task, path, 1); + result = get_task_root(task, path); put_task_struct(task); } return result; @@ -597,7 +597,7 @@ static int mounts_open_common(struct inode *inode, struct file *file, get_mnt_ns(ns); } rcu_read_unlock(); - if (ns && get_fs_path(task, &root, 1) == 0) + if (ns && get_task_root(task, &root) == 0) ret = 0; put_task_struct(task); } diff --git a/include/linux/fs_struct.h b/include/linux/fs_struct.h index 78a05bfcd8eb..eca3d5202138 100644 --- a/include/linux/fs_struct.h +++ b/include/linux/fs_struct.h @@ -21,4 +21,31 @@ extern void free_fs_struct(struct fs_struct *); extern void daemonize_fs_struct(void); extern int unshare_fs_struct(void); +static inline void get_fs_root(struct fs_struct *fs, struct path *root) +{ + read_lock(&fs->lock); + *root = fs->root; + path_get(root); + read_unlock(&fs->lock); +} + +static inline void get_fs_pwd(struct fs_struct *fs, struct path *pwd) +{ + read_lock(&fs->lock); + *pwd = fs->pwd; + path_get(pwd); + read_unlock(&fs->lock); +} + +static inline void get_fs_root_and_pwd(struct fs_struct *fs, struct path *root, + struct path *pwd) +{ + read_lock(&fs->lock); + *root = fs->root; + path_get(root); + *pwd = fs->pwd; + path_get(pwd); + read_unlock(&fs->lock); +} + #endif /* _LINUX_FS_STRUCT_H */ diff --git a/kernel/auditsc.c b/kernel/auditsc.c index b87a63beb66c..1b31c130d034 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -1835,13 +1835,8 @@ void __audit_getname(const char *name) context->names[context->name_count].ino = (unsigned long)-1; context->names[context->name_count].osid = 0; ++context->name_count; - if (!context->pwd.dentry) { - read_lock(¤t->fs->lock); - context->pwd = current->fs->pwd; - path_get(¤t->fs->pwd); - read_unlock(¤t->fs->lock); - } - + if (!context->pwd.dentry) + get_fs_pwd(current->fs, &context->pwd); } /* audit_putname - intercept a putname request From 7ae6bdbd9b9d42da53ea809875c0a6cda3de694c Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Tue, 10 Aug 2010 11:41:37 +0200 Subject: [PATCH 07/13] ia64: perfmon: add d_dname method Switch ia64/perfmon to using the d_dname() instead of relying on __d_path() to prepend the name of the root dentry to the path. CC: Tony Luck CC: Fenghua Yu Signed-off-by: Miklos Szeredi Signed-off-by: Al Viro --- arch/ia64/kernel/perfmon.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/arch/ia64/kernel/perfmon.c b/arch/ia64/kernel/perfmon.c index 744329072f33..cce050e85c73 100644 --- a/arch/ia64/kernel/perfmon.c +++ b/arch/ia64/kernel/perfmon.c @@ -2191,8 +2191,15 @@ pfmfs_delete_dentry(struct dentry *dentry) return 1; } +static char *pfmfs_dname(struct dentry *dentry, char *buffer, int buflen) +{ + return dynamic_dname(dentry, buffer, buflen, "pfm:[%lu]", + dentry->d_inode->i_ino); +} + static const struct dentry_operations pfmfs_dentry_operations = { .d_delete = pfmfs_delete_dentry, + .d_dname = pfmfs_dname, }; @@ -2202,8 +2209,7 @@ pfm_alloc_file(pfm_context_t *ctx) struct file *file; struct inode *inode; struct path path; - char name[32]; - struct qstr this; + struct qstr this = { .name = "" }; /* * allocate a new inode @@ -2218,11 +2224,6 @@ pfm_alloc_file(pfm_context_t *ctx) inode->i_uid = current_fsuid(); inode->i_gid = current_fsgid(); - sprintf(name, "[%lu]", inode->i_ino); - this.name = name; - this.len = strlen(name); - this.hash = inode->i_ino; - /* * allocate a new dcache entry */ From 98dc568bc2ebefe4c0cb315a7fb7eff8bbb43176 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Tue, 10 Aug 2010 11:41:38 +0200 Subject: [PATCH 08/13] vfs: __d_path: dont prepend the name of the root dentry In the old times pseudo-filesystems set the name of theroot dentry to some prefix like "pipe:" and the name of the child dentry to "[123]" and relied on a hack in __d_path() to replace the preceding slash with the root's name to get "pipe:[123]". Then the d_dname() dentry operation was introduced which solved the same problem without having to pre-fill the name in each dentry. Currently the following pseudo filesystems exist in the kernel: perfmon mtd anon_inode bdev pipe socket Of these only perfmon, anon_inode, pipe and socket create sub-dentries, all of which have now been switched to using d_dname(). bdev and mtd only create inodes. This means that now the hack to overwrite the slash can be removed, so for unreachable paths (e.g. within a detached mount) the path string won't be polluted with garbage. For these cases a subsequent patch will add a prefix, indicating that the path is unreachable. Signed-off-by: Miklos Szeredi Signed-off-by: Al Viro --- fs/dcache.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/fs/dcache.c b/fs/dcache.c index 995d08069d26..f1809e6b9fda 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1970,9 +1970,15 @@ out: return retval; global_root: - retval += 1; /* hit the slash */ - if (prepend_name(&retval, &buflen, &dentry->d_name) != 0) - goto Elong; + /* + * Filesystems needing to implement special "root names" + * should do so with ->d_dname() + */ + if (IS_ROOT(dentry) && + (dentry->d_name.len != 1 || dentry->d_name.name[0] != '/')) { + WARN(1, "Root dentry has weird name <%.*s>\n", + (int) dentry->d_name.len, dentry->d_name.name); + } root->mnt = vfsmnt; root->dentry = dentry; goto out; From f2eb6575d5beba1e98d400463007d77555d1fc35 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Tue, 10 Aug 2010 11:41:39 +0200 Subject: [PATCH 09/13] vfs: add prepend_path() helper Split off prepend_path() from __d_path(). This new helper takes an end-of-buffer pointer and buffer-length pointer just like the other prepend_* functions. Move the " (deleted)" postfix out to __d_path(). This patch doesn't change any functionality but paves the way for the following patches. Signed-off-by: Miklos Szeredi Signed-off-by: Al Viro --- fs/dcache.c | 138 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 80 insertions(+), 58 deletions(-) diff --git a/fs/dcache.c b/fs/dcache.c index f1809e6b9fda..d09d93819b4d 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1904,6 +1904,74 @@ static int prepend_name(char **buffer, int *buflen, struct qstr *name) return prepend(buffer, buflen, name->name, name->len); } +/** + * Prepend path string to a buffer + * + * @path: the dentry/vfsmount to report + * @root: root vfsmnt/dentry (may be modified by this function) + * @buffer: pointer to the end of the buffer + * @buflen: pointer to buffer length + * + * Caller holds the dcache_lock. + * + * If path is not reachable from the supplied root, then the value of + * root is changed (without modifying refcounts). + */ +static int prepend_path(const struct path *path, struct path *root, + char **buffer, int *buflen) +{ + struct dentry *dentry = path->dentry; + struct vfsmount *vfsmnt = path->mnt; + bool slash = false; + int error = 0; + + spin_lock(&vfsmount_lock); + while (dentry != root->dentry || vfsmnt != root->mnt) { + struct dentry * parent; + + if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) { + /* Global root? */ + if (vfsmnt->mnt_parent == vfsmnt) { + goto global_root; + } + dentry = vfsmnt->mnt_mountpoint; + vfsmnt = vfsmnt->mnt_parent; + continue; + } + parent = dentry->d_parent; + prefetch(parent); + error = prepend_name(buffer, buflen, &dentry->d_name); + if (!error) + error = prepend(buffer, buflen, "/", 1); + if (error) + break; + + slash = true; + dentry = parent; + } + +out: + if (!error && !slash) + error = prepend(buffer, buflen, "/", 1); + + spin_unlock(&vfsmount_lock); + return error; + +global_root: + /* + * Filesystems needing to implement special "root names" + * should do so with ->d_dname() + */ + if (IS_ROOT(dentry) && + (dentry->d_name.len != 1 || dentry->d_name.name[0] != '/')) { + WARN(1, "Root dentry has weird name <%.*s>\n", + (int) dentry->d_name.len, dentry->d_name.name); + } + root->mnt = vfsmnt; + root->dentry = dentry; + goto out; +} + /** * __d_path - return the path of a dentry * @path: the dentry/vfsmount to report @@ -1923,69 +1991,23 @@ static int prepend_name(char **buffer, int *buflen, struct qstr *name) * root is changed (without modifying refcounts). */ char *__d_path(const struct path *path, struct path *root, - char *buffer, int buflen) + char *buf, int buflen) { - struct dentry *dentry = path->dentry; - struct vfsmount *vfsmnt = path->mnt; - char *end = buffer + buflen; - char *retval; + char *res = buf + buflen; + int error; - spin_lock(&vfsmount_lock); - prepend(&end, &buflen, "\0", 1); - if (d_unlinked(dentry) && - (prepend(&end, &buflen, " (deleted)", 10) != 0)) - goto Elong; - - if (buflen < 1) - goto Elong; - /* Get '/' right */ - retval = end-1; - *retval = '/'; - - for (;;) { - struct dentry * parent; - - if (dentry == root->dentry && vfsmnt == root->mnt) - break; - if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) { - /* Global root? */ - if (vfsmnt->mnt_parent == vfsmnt) { - goto global_root; - } - dentry = vfsmnt->mnt_mountpoint; - vfsmnt = vfsmnt->mnt_parent; - continue; - } - parent = dentry->d_parent; - prefetch(parent); - if ((prepend_name(&end, &buflen, &dentry->d_name) != 0) || - (prepend(&end, &buflen, "/", 1) != 0)) - goto Elong; - retval = end; - dentry = parent; + prepend(&res, &buflen, "\0", 1); + if (d_unlinked(path->dentry)) { + error = prepend(&res, &buflen, " (deleted)", 10); + if (error) + return ERR_PTR(error); } -out: - spin_unlock(&vfsmount_lock); - return retval; + error = prepend_path(path, root, &res, &buflen); + if (error) + return ERR_PTR(error); -global_root: - /* - * Filesystems needing to implement special "root names" - * should do so with ->d_dname() - */ - if (IS_ROOT(dentry) && - (dentry->d_name.len != 1 || dentry->d_name.name[0] != '/')) { - WARN(1, "Root dentry has weird name <%.*s>\n", - (int) dentry->d_name.len, dentry->d_name.name); - } - root->mnt = vfsmnt; - root->dentry = dentry; - goto out; - -Elong: - retval = ERR_PTR(-ENAMETOOLONG); - goto out; + return res; } /** From ffd1f4ed5bddccf2277e3d8525bcedf1983319f8 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Tue, 10 Aug 2010 11:41:40 +0200 Subject: [PATCH 10/13] vfs: only add " (deleted)" where necessary __d_path() has 4 callers: d_path() sys_getcwd() seq_path_root() tomoyo_realpath_from_path2() Of these the only one which needs the " (deleted)" ending is d_path(). sys_getcwd() checks for existence before calling __d_path(). seq_path_root() is used to show the mountpoint path in /proc/PID/mountinfo, which is always a positive. And tomoyo doesn't want the deleted ending. Create a helper "path_with_deleted()" as subsequent patches will need this in multiple places. Signed-off-by: Miklos Szeredi Signed-off-by: Al Viro --- fs/dcache.c | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/fs/dcache.c b/fs/dcache.c index d09d93819b4d..7fccb00f498d 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1979,8 +1979,7 @@ global_root: * @buffer: buffer to return value in * @buflen: buffer length * - * Convert a dentry into an ASCII path name. If the entry has been deleted - * the string " (deleted)" is appended. Note that this is ambiguous. + * Convert a dentry into an ASCII path name. * * Returns a pointer into the buffer or an error code if the * path was too long. @@ -1997,12 +1996,6 @@ char *__d_path(const struct path *path, struct path *root, int error; prepend(&res, &buflen, "\0", 1); - if (d_unlinked(path->dentry)) { - error = prepend(&res, &buflen, " (deleted)", 10); - if (error) - return ERR_PTR(error); - } - error = prepend_path(path, root, &res, &buflen); if (error) return ERR_PTR(error); @@ -2010,6 +2003,22 @@ char *__d_path(const struct path *path, struct path *root, return res; } +/* + * same as __d_path but appends "(deleted)" for unlinked files. + */ +static int path_with_deleted(const struct path *path, struct path *root, + char **buf, int *buflen) +{ + prepend(buf, buflen, "\0", 1); + if (d_unlinked(path->dentry)) { + int error = prepend(buf, buflen, " (deleted)", 10); + if (error) + return error; + } + + return prepend_path(path, root, buf, buflen); +} + /** * d_path - return the path of a dentry * @path: path to report @@ -2028,9 +2037,10 @@ char *__d_path(const struct path *path, struct path *root, */ char *d_path(const struct path *path, char *buf, int buflen) { - char *res; + char *res = buf + buflen; struct path root; struct path tmp; + int error; /* * We have various synthetic filesystems that never get mounted. On @@ -2045,7 +2055,9 @@ char *d_path(const struct path *path, char *buf, int buflen) get_fs_root(current->fs, &root); spin_lock(&dcache_lock); tmp = root; - res = __d_path(path, &tmp, buf, buflen); + error = path_with_deleted(path, &tmp, &res, &buflen); + if (error) + res = ERR_PTR(error); spin_unlock(&dcache_lock); path_put(&root); return res; From 8df9d1a4142311c084ffeeacb67cd34d190eff74 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Tue, 10 Aug 2010 11:41:41 +0200 Subject: [PATCH 11/13] vfs: show unreachable paths in getcwd and proc Prepend "(unreachable)" to path strings if the path is not reachable from the current root. Two places updated are - the return string from getcwd() - and symlinks under /proc/$PID. Other uses of d_path() are left unchanged (we know that some old software crashes if /proc/mounts is changed). Signed-off-by: Miklos Szeredi Signed-off-by: Al Viro --- fs/dcache.c | 54 ++++++++++++++++++++++++++++++++++++++---- fs/proc/base.c | 2 +- include/linux/dcache.h | 1 + include/linux/path.h | 5 ++++ 4 files changed, 57 insertions(+), 5 deletions(-) diff --git a/fs/dcache.c b/fs/dcache.c index 7fccb00f498d..166d35d56868 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -2019,6 +2019,11 @@ static int path_with_deleted(const struct path *path, struct path *root, return prepend_path(path, root, buf, buflen); } +static int prepend_unreachable(char **buffer, int *buflen) +{ + return prepend(buffer, buflen, "(unreachable)", 13); +} + /** * d_path - return the path of a dentry * @path: path to report @@ -2064,6 +2069,39 @@ char *d_path(const struct path *path, char *buf, int buflen) } EXPORT_SYMBOL(d_path); +/** + * d_path_with_unreachable - return the path of a dentry + * @path: path to report + * @buf: buffer to return value in + * @buflen: buffer length + * + * The difference from d_path() is that this prepends "(unreachable)" + * to paths which are unreachable from the current process' root. + */ +char *d_path_with_unreachable(const struct path *path, char *buf, int buflen) +{ + char *res = buf + buflen; + struct path root; + struct path tmp; + int error; + + if (path->dentry->d_op && path->dentry->d_op->d_dname) + return path->dentry->d_op->d_dname(path->dentry, buf, buflen); + + get_fs_root(current->fs, &root); + spin_lock(&dcache_lock); + tmp = root; + error = path_with_deleted(path, &tmp, &res, &buflen); + if (!error && !path_equal(&tmp, &root)) + error = prepend_unreachable(&res, &buflen); + spin_unlock(&dcache_lock); + path_put(&root); + if (error) + res = ERR_PTR(error); + + return res; +} + /* * Helper function for dentry_operations.d_dname() members */ @@ -2173,15 +2211,23 @@ SYSCALL_DEFINE2(getcwd, char __user *, buf, unsigned long, size) if (!d_unlinked(pwd.dentry)) { unsigned long len; struct path tmp = root; - char * cwd; + char *cwd = page + PAGE_SIZE; + int buflen = PAGE_SIZE; - cwd = __d_path(&pwd, &tmp, page, PAGE_SIZE); + prepend(&cwd, &buflen, "\0", 1); + error = prepend_path(&pwd, &tmp, &cwd, &buflen); spin_unlock(&dcache_lock); - error = PTR_ERR(cwd); - if (IS_ERR(cwd)) + if (error) goto out; + /* Unreachable from current root */ + if (!path_equal(&tmp, &root)) { + error = prepend_unreachable(&cwd, &buflen); + if (error) + goto out; + } + error = -ERANGE; len = PAGE_SIZE + page - cwd; if (len <= size) { diff --git a/fs/proc/base.c b/fs/proc/base.c index 4cf5abf77ddf..a1c43e7c8a7b 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -1526,7 +1526,7 @@ static int do_proc_readlink(struct path *path, char __user *buffer, int buflen) if (!tmp) return -ENOMEM; - pathname = d_path(path, tmp, PAGE_SIZE); + pathname = d_path_with_unreachable(path, tmp, PAGE_SIZE); len = PTR_ERR(pathname); if (IS_ERR(pathname)) goto out; diff --git a/include/linux/dcache.h b/include/linux/dcache.h index d23be0386e2d..6a4aea30aa09 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -315,6 +315,7 @@ extern char *dynamic_dname(struct dentry *, char *, int, const char *, ...); extern char *__d_path(const struct path *path, struct path *root, char *, int); extern char *d_path(const struct path *, char *, int); +extern char *d_path_with_unreachable(const struct path *, char *, int); extern char *__dentry_path(struct dentry *, char *, int); extern char *dentry_path(struct dentry *, char *, int); diff --git a/include/linux/path.h b/include/linux/path.h index 915e0c382a51..edc98dec6266 100644 --- a/include/linux/path.h +++ b/include/linux/path.h @@ -12,4 +12,9 @@ struct path { extern void path_get(struct path *); extern void path_put(struct path *); +static inline int path_equal(const struct path *path1, const struct path *path2) +{ + return path1->mnt == path2->mnt && path1->dentry == path2->dentry; +} + #endif /* _LINUX_PATH_H */ From 532490f0a5350fd92d838b7430a4c846bc8eac3f Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Mon, 2 Aug 2010 13:46:56 +0200 Subject: [PATCH 12/13] vfs: remove unused MNT_STRICTATIME Commit d0adde574b8487ef30f69e2d08bba769e4be513f added MNT_STRICTATIME but it isn't actually used (MS_STRICTATIME clears MNT_RELATIME and MNT_NOATIME rather than setting any mount flag). Signed-off-by: Miklos Szeredi Signed-off-by: Al Viro --- fs/namespace.c | 1 - include/linux/mount.h | 1 - 2 files changed, 2 deletions(-) diff --git a/fs/namespace.c b/fs/namespace.c index 7972e51ccc06..2e10cb19c5b0 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -788,7 +788,6 @@ static void show_mnt_opts(struct seq_file *m, struct vfsmount *mnt) { MNT_NOATIME, ",noatime" }, { MNT_NODIRATIME, ",nodiratime" }, { MNT_RELATIME, ",relatime" }, - { MNT_STRICTATIME, ",strictatime" }, { 0, NULL } }; const struct proc_fs_info *fs_infop; diff --git a/include/linux/mount.h b/include/linux/mount.h index 907210bd9f9c..5e7a59408dd4 100644 --- a/include/linux/mount.h +++ b/include/linux/mount.h @@ -27,7 +27,6 @@ struct mnt_namespace; #define MNT_NODIRATIME 0x10 #define MNT_RELATIME 0x20 #define MNT_READONLY 0x40 /* does the user want this to be r/o? */ -#define MNT_STRICTATIME 0x80 #define MNT_SHRINKABLE 0x100 #define MNT_WRITE_HOLD 0x200 From 66a362a2aa8ffa72670259fa15e2a77a01cc2217 Mon Sep 17 00:00:00 2001 From: Jan Andres Date: Wed, 4 Aug 2010 22:52:46 +0200 Subject: [PATCH 13/13] isofs: Fix lseek() to position beyond 4 GB isofs supports files larger than 4 GB by using multi-extent files. However an lseek() to a position beyond 4 GB in such a file will fail with EINVAL, because s_maxbytes in the isofs superblock is initialized to 2^32-1, and generic_file_llseek() checks against that value. I therefore suggest increasing the value of s_maxbytes to have full support for large files in isofs. With multi-extent files, file size is only limited by the maximum size of the file system (8 TB), so this seems a reasonable value for s_maxbytes. Signed-off-by: Jan Andres Signed-off-by: Al Viro --- fs/isofs/inode.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c index 6b4dcd4f2943..5a44811b5027 100644 --- a/fs/isofs/inode.c +++ b/fs/isofs/inode.c @@ -722,7 +722,12 @@ root_found: } s->s_magic = ISOFS_SUPER_MAGIC; - s->s_maxbytes = 0xffffffff; /* We can handle files up to 4 GB */ + + /* + * With multi-extent files, file size is only limited by the maximum + * size of a file system, which is 8 TB. + */ + s->s_maxbytes = 0x80000000000LL; /* * The CDROM is read-only, has no nodes (devices) on it, and since