diff --git a/arch/ia64/kernel/perfmon.c b/arch/ia64/kernel/perfmon.c index 744329072f3..cce050e85c7 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 */ diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index d97c34a24f7..c7c23eab944 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; } /** diff --git a/fs/cachefiles/daemon.c b/fs/cachefiles/daemon.c index 24eb0d37241..727caedcdd9 100644 --- a/fs/cachefiles/daemon.c +++ b/fs/cachefiles/daemon.c @@ -552,8 +552,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; @@ -573,24 +572,21 @@ 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); - read_unlock(&fs->lock); + get_fs_pwd(current->fs, &path); - 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; @@ -628,8 +624,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; @@ -649,24 +644,21 @@ 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); - read_unlock(&fs->lock); + get_fs_pwd(current->fs, &path); - 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; diff --git a/fs/dcache.c b/fs/dcache.c index 9f2c1341796..166d35d5686 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1905,48 +1905,30 @@ static int prepend_name(char **buffer, int *buflen, struct qstr *name) } /** - * __d_path - return the path of a dentry + * Prepend path string to a buffer + * * @path: the dentry/vfsmount to report * @root: root vfsmnt/dentry (may be modified by this function) - * @buffer: buffer to return value in - * @buflen: buffer length + * @buffer: pointer to the end of the buffer + * @buflen: pointer to 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. - * - * Returns a pointer into the buffer or an error code if the - * path was too long. - * - * "buflen" should be positive. Caller holds the dcache_lock. + * Caller holds the dcache_lock. * * If path is not reachable from the supplied root, then the value of * root is changed (without modifying refcounts). */ -char *__d_path(const struct path *path, struct path *root, - char *buffer, int buflen) +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; - char *end = buffer + buflen; - char *retval; + bool slash = false; + int error = 0; 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 (;;) { + while (dentry != root->dentry || vfsmnt != root->mnt) { 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) { @@ -1958,28 +1940,88 @@ char *__d_path(const struct path *path, struct path *root, } parent = dentry->d_parent; prefetch(parent); - if ((prepend_name(&end, &buflen, &dentry->d_name) != 0) || - (prepend(&end, &buflen, "/", 1) != 0)) - goto Elong; - retval = end; + 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 retval; + return error; 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; +} -Elong: - retval = ERR_PTR(-ENAMETOOLONG); - goto out; +/** + * __d_path - return the path of a dentry + * @path: the dentry/vfsmount to report + * @root: root vfsmnt/dentry (may be modified by this function) + * @buffer: buffer to return value in + * @buflen: buffer length + * + * Convert a dentry into an ASCII path name. + * + * Returns a pointer into the buffer or an error code if the + * path was too long. + * + * "buflen" should be positive. Caller holds the dcache_lock. + * + * If path is not reachable from the supplied root, then the value of + * root is changed (without modifying refcounts). + */ +char *__d_path(const struct path *path, struct path *root, + char *buf, int buflen) +{ + char *res = buf + buflen; + int error; + + prepend(&res, &buflen, "\0", 1); + error = prepend_path(path, root, &res, &buflen); + if (error) + return ERR_PTR(error); + + 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); +} + +static int prepend_unreachable(char **buffer, int *buflen) +{ + return prepend(buffer, buflen, "(unreachable)", 13); } /** @@ -2000,9 +2042,10 @@ Elong: */ 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 @@ -2014,19 +2057,51 @@ 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); + error = path_with_deleted(path, &tmp, &res, &buflen); + if (error) + res = ERR_PTR(error); spin_unlock(&dcache_lock); path_put(&root); return res; } 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 */ @@ -2129,27 +2204,30 @@ 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); 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/fs_struct.c b/fs/fs_struct.c index eee059052db..1ee40eb9a2c 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/isofs/inode.c b/fs/isofs/inode.c index 6b4dcd4f294..5a44811b502 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 diff --git a/fs/namei.c b/fs/namei.c index 13ff4abdbdc..17ea76bf2fb 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 66c4f7e781c..2e10cb19c5b 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; @@ -2213,10 +2212,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 c806dfb24e0..a1c43e7c8a7 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); } @@ -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/fs/sysv/super.c b/fs/sysv/super.c index 85359a8df60..a0b0cda6927 100644 --- a/fs/sysv/super.c +++ b/fs/sysv/super.c @@ -24,7 +24,6 @@ #include #include #include -#include #include "sysv.h" /* diff --git a/include/linux/dcache.h b/include/linux/dcache.h index d23be0386e2..6a4aea30aa0 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/fs_struct.h b/include/linux/fs_struct.h index 78a05bfcd8e..eca3d520213 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/include/linux/mount.h b/include/linux/mount.h index 907210bd9f9..5e7a59408dd 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 diff --git a/include/linux/path.h b/include/linux/path.h index 915e0c382a5..edc98dec626 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 */ diff --git a/kernel/auditsc.c b/kernel/auditsc.c index b87a63beb66..1b31c130d03 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