From f38aa94224c5517a40ba56d453779f70d3229803 Mon Sep 17 00:00:00 2001 From: Amy Griffis Date: Thu, 3 Nov 2005 15:57:06 +0000 Subject: [PATCH] Pass dentry, not just name, in fsnotify creation hooks. The audit hooks (to be added shortly) will want to see dentry->d_inode too, not just the name. Signed-off-by: Amy Griffis Signed-off-by: David Woodhouse --- fs/namei.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'fs/namei.c') diff --git a/fs/namei.c b/fs/namei.c index 8dc2b038d5d..f6619af9e95 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1472,7 +1472,7 @@ int vfs_create(struct inode *dir, struct dentry *dentry, int mode, DQUOT_INIT(dir); error = dir->i_op->create(dir, dentry, mode, nd); if (!error) - fsnotify_create(dir, dentry->d_name.name); + fsnotify_create(dir, dentry); return error; } @@ -1793,7 +1793,7 @@ int vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) DQUOT_INIT(dir); error = dir->i_op->mknod(dir, dentry, mode, dev); if (!error) - fsnotify_create(dir, dentry->d_name.name); + fsnotify_create(dir, dentry); return error; } @@ -1870,7 +1870,7 @@ int vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) DQUOT_INIT(dir); error = dir->i_op->mkdir(dir, dentry, mode); if (!error) - fsnotify_mkdir(dir, dentry->d_name.name); + fsnotify_mkdir(dir, dentry); return error; } @@ -2133,7 +2133,7 @@ int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname, i DQUOT_INIT(dir); error = dir->i_op->symlink(dir, dentry, oldname); if (!error) - fsnotify_create(dir, dentry->d_name.name); + fsnotify_create(dir, dentry); return error; } @@ -2210,7 +2210,7 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de error = dir->i_op->link(old_dentry, dir, new_dentry); mutex_unlock(&old_dentry->d_inode->i_mutex); if (!error) - fsnotify_create(dir, new_dentry->d_name.name); + fsnotify_create(dir, new_dentry); return error; } -- cgit v1.2.3-70-g09d2 From 73241ccca0f7786933f1d31b3d86f2456549953a Mon Sep 17 00:00:00 2001 From: Amy Griffis Date: Thu, 3 Nov 2005 16:00:25 +0000 Subject: [PATCH] Collect more inode information during syscall processing. This patch augments the collection of inode info during syscall processing. It represents part of the functionality that was provided by the auditfs patch included in RHEL4. Specifically, it: - Collects information for target inodes created or removed during syscalls. Previous code only collects information for the target inode's parent. - Adds the audit_inode() hook to syscalls that operate on a file descriptor (e.g. fchown), enabling audit to do inode filtering for these calls. - Modifies filtering code to check audit context for either an inode # or a parent inode # matching a given rule. - Modifies logging to provide inode # for both parent and child. - Protect debug info from NULL audit_names.name. [AV: folded a later typo fix from the same author] Signed-off-by: Amy Griffis Signed-off-by: David Woodhouse Signed-off-by: Al Viro --- fs/namei.c | 1 + fs/open.c | 8 ++- fs/xattr.c | 11 +++- include/linux/audit.h | 18 +++++- include/linux/fsnotify.h | 5 ++ kernel/auditsc.c | 142 +++++++++++++++++++++++++++++++++++++++-------- 6 files changed, 157 insertions(+), 28 deletions(-) (limited to 'fs/namei.c') diff --git a/fs/namei.c b/fs/namei.c index f6619af9e95..51cfc9c3ed0 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1353,6 +1353,7 @@ static int may_delete(struct inode *dir,struct dentry *victim,int isdir) return -ENOENT; BUG_ON(victim->d_parent->d_inode != dir); + audit_inode_child(victim->d_name.name, victim->d_inode, dir->i_ino); error = permission(dir,MAY_WRITE | MAY_EXEC, NULL); if (error) diff --git a/fs/open.c b/fs/open.c index 70e0230d8e7..70510004d06 100644 --- a/fs/open.c +++ b/fs/open.c @@ -27,6 +27,7 @@ #include #include #include +#include #include @@ -626,6 +627,8 @@ asmlinkage long sys_fchmod(unsigned int fd, mode_t mode) dentry = file->f_dentry; inode = dentry->d_inode; + audit_inode(NULL, inode, 0); + err = -EROFS; if (IS_RDONLY(inode)) goto out_putf; @@ -775,7 +778,10 @@ asmlinkage long sys_fchown(unsigned int fd, uid_t user, gid_t group) file = fget(fd); if (file) { - error = chown_common(file->f_dentry, user, group); + struct dentry * dentry; + dentry = file->f_dentry; + audit_inode(NULL, dentry->d_inode, 0); + error = chown_common(dentry, user, group); fput(file); } return error; diff --git a/fs/xattr.c b/fs/xattr.c index 80eca7d3d69..e416190f5e9 100644 --- a/fs/xattr.c +++ b/fs/xattr.c @@ -17,6 +17,7 @@ #include #include #include +#include #include @@ -234,12 +235,15 @@ sys_fsetxattr(int fd, char __user *name, void __user *value, size_t size, int flags) { struct file *f; + struct dentry *dentry; int error = -EBADF; f = fget(fd); if (!f) return error; - error = setxattr(f->f_dentry, name, value, size, flags); + dentry = f->f_dentry; + audit_inode(NULL, dentry->d_inode, 0); + error = setxattr(dentry, name, value, size, flags); fput(f); return error; } @@ -458,12 +462,15 @@ asmlinkage long sys_fremovexattr(int fd, char __user *name) { struct file *f; + struct dentry *dentry; int error = -EBADF; f = fget(fd); if (!f) return error; - error = removexattr(f->f_dentry, name); + dentry = f->f_dentry; + audit_inode(NULL, dentry->d_inode, 0); + error = removexattr(dentry, name); fput(f); return error; } diff --git a/include/linux/audit.h b/include/linux/audit.h index fd65078e794..739b954cb24 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -260,7 +260,20 @@ extern void audit_syscall_entry(struct task_struct *task, int arch, extern void audit_syscall_exit(struct task_struct *task, int failed, long return_code); extern void audit_getname(const char *name); extern void audit_putname(const char *name); -extern void audit_inode(const char *name, const struct inode *inode, unsigned flags); +extern void __audit_inode(const char *name, const struct inode *inode, unsigned flags); +extern void __audit_inode_child(const char *dname, const struct inode *inode, + unsigned long pino); +static inline void audit_inode(const char *name, const struct inode *inode, + unsigned flags) { + if (unlikely(current->audit_context)) + __audit_inode(name, inode, flags); +} +static inline void audit_inode_child(const char *dname, + const struct inode *inode, + unsigned long pino) { + if (unlikely(current->audit_context)) + __audit_inode_child(dname, inode, pino); +} /* Private API (for audit.c only) */ extern int audit_receive_filter(int type, int pid, int uid, int seq, @@ -283,7 +296,10 @@ extern int audit_filter_user(struct netlink_skb_parms *cb, int type); #define audit_syscall_exit(t,f,r) do { ; } while (0) #define audit_getname(n) do { ; } while (0) #define audit_putname(n) do { ; } while (0) +#define __audit_inode(n,i,f) do { ; } while (0) +#define __audit_inode_child(d,i,p) do { ; } while (0) #define audit_inode(n,i,f) do { ; } while (0) +#define audit_inode_child(d,i,p) do { ; } while (0) #define audit_receive_filter(t,p,u,s,d,l) ({ -EOPNOTSUPP; }) #define auditsc_get_stamp(c,t,s) do { BUG(); } while (0) #define audit_get_loginuid(c) ({ -1; }) diff --git a/include/linux/fsnotify.h b/include/linux/fsnotify.h index b5ff64d2f09..94919c376a7 100644 --- a/include/linux/fsnotify.h +++ b/include/linux/fsnotify.h @@ -15,6 +15,7 @@ #include #include +#include /* * fsnotify_move - file old_name at old_dir was moved to new_name at new_dir @@ -45,6 +46,8 @@ static inline void fsnotify_move(struct inode *old_dir, struct inode *new_dir, if (source) { inotify_inode_queue_event(source, IN_MOVE_SELF, 0, NULL); } + audit_inode_child(old_name, source, old_dir->i_ino); + audit_inode_child(new_name, target, new_dir->i_ino); } /* @@ -74,6 +77,7 @@ static inline void fsnotify_create(struct inode *inode, struct dentry *dentry) { inode_dir_notify(inode, DN_CREATE); inotify_inode_queue_event(inode, IN_CREATE, 0, dentry->d_name.name); + audit_inode_child(dentry->d_name.name, dentry->d_inode, inode->i_ino); } /* @@ -84,6 +88,7 @@ static inline void fsnotify_mkdir(struct inode *inode, struct dentry *dentry) inode_dir_notify(inode, DN_CREATE); inotify_inode_queue_event(inode, IN_CREATE | IN_ISDIR, 0, dentry->d_name.name); + audit_inode_child(dentry->d_name.name, dentry->d_inode, inode->i_ino); } /* diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 55ba331757c..73f932b7deb 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -2,6 +2,7 @@ * Handles all system-call specific auditing features. * * Copyright 2003-2004 Red Hat Inc., Durham, North Carolina. + * Copyright 2005 Hewlett-Packard Development Company, L.P. * Copyright (C) 2005 IBM Corporation * All Rights Reserved. * @@ -31,11 +32,16 @@ * The support of additional filter rules compares (>, <, >=, <=) was * added by Dustin Kirkland , 2005. * + * Modified by Amy Griffis to collect additional + * filesystem information. */ #include #include #include +#include +#include +#include #include #include #include @@ -97,12 +103,12 @@ enum audit_state { struct audit_names { const char *name; unsigned long ino; + unsigned long pino; dev_t dev; umode_t mode; uid_t uid; gid_t gid; dev_t rdev; - unsigned flags; }; struct audit_aux_data { @@ -515,7 +521,8 @@ static int audit_filter_rules(struct task_struct *tsk, case AUDIT_INODE: if (ctx) { for (j = 0; j < ctx->name_count; j++) { - if ( audit_comparator(ctx->names[j].ino, op, value)) { + if (audit_comparator(ctx->names[j].ino, op, value) || + audit_comparator(ctx->names[j].pino, op, value)) { ++result; break; } @@ -696,17 +703,17 @@ static inline void audit_free_names(struct audit_context *context) #if AUDIT_DEBUG == 2 if (context->auditable ||context->put_count + context->ino_count != context->name_count) { - printk(KERN_ERR "audit.c:%d(:%d): major=%d in_syscall=%d" + printk(KERN_ERR "%s:%d(:%d): major=%d in_syscall=%d" " name_count=%d put_count=%d" " ino_count=%d [NOT freeing]\n", - __LINE__, + __FILE__, __LINE__, context->serial, context->major, context->in_syscall, context->name_count, context->put_count, context->ino_count); for (i = 0; i < context->name_count; i++) printk(KERN_ERR "names[%d] = %p = %s\n", i, context->names[i].name, - context->names[i].name); + context->names[i].name ?: "(null)"); dump_stack(); return; } @@ -932,27 +939,34 @@ static void audit_log_exit(struct audit_context *context, gfp_t gfp_mask) } } for (i = 0; i < context->name_count; i++) { + unsigned long ino = context->names[i].ino; + unsigned long pino = context->names[i].pino; + ab = audit_log_start(context, gfp_mask, AUDIT_PATH); if (!ab) continue; /* audit_panic has been called */ audit_log_format(ab, "item=%d", i); - if (context->names[i].name) { - audit_log_format(ab, " name="); + + audit_log_format(ab, " name="); + if (context->names[i].name) audit_log_untrustedstring(ab, context->names[i].name); - } - audit_log_format(ab, " flags=%x\n", context->names[i].flags); - - if (context->names[i].ino != (unsigned long)-1) - audit_log_format(ab, " inode=%lu dev=%02x:%02x mode=%#o" - " ouid=%u ogid=%u rdev=%02x:%02x", - context->names[i].ino, - MAJOR(context->names[i].dev), - MINOR(context->names[i].dev), - context->names[i].mode, - context->names[i].uid, - context->names[i].gid, - MAJOR(context->names[i].rdev), + else + audit_log_format(ab, "(null)"); + + if (pino != (unsigned long)-1) + audit_log_format(ab, " parent=%lu", pino); + if (ino != (unsigned long)-1) + audit_log_format(ab, " inode=%lu", ino); + if ((pino != (unsigned long)-1) || (ino != (unsigned long)-1)) + audit_log_format(ab, " dev=%02x:%02x mode=%#o" + " ouid=%u ogid=%u rdev=%02x:%02x", + MAJOR(context->names[i].dev), + MINOR(context->names[i].dev), + context->names[i].mode, + context->names[i].uid, + context->names[i].gid, + MAJOR(context->names[i].rdev), MINOR(context->names[i].rdev)); audit_log_end(ab); } @@ -1174,7 +1188,7 @@ void audit_putname(const char *name) for (i = 0; i < context->name_count; i++) printk(KERN_ERR "name[%d] = %p = %s\n", i, context->names[i].name, - context->names[i].name); + context->names[i].name ?: "(null)"); } #endif __putname(name); @@ -1204,7 +1218,7 @@ void audit_putname(const char *name) * * Called from fs/namei.c:path_lookup(). */ -void audit_inode(const char *name, const struct inode *inode, unsigned flags) +void __audit_inode(const char *name, const struct inode *inode, unsigned flags) { int idx; struct audit_context *context = current->audit_context; @@ -1230,13 +1244,93 @@ void audit_inode(const char *name, const struct inode *inode, unsigned flags) ++context->ino_count; #endif } - context->names[idx].flags = flags; - context->names[idx].ino = inode->i_ino; context->names[idx].dev = inode->i_sb->s_dev; context->names[idx].mode = inode->i_mode; context->names[idx].uid = inode->i_uid; context->names[idx].gid = inode->i_gid; context->names[idx].rdev = inode->i_rdev; + if ((flags & LOOKUP_PARENT) && (strcmp(name, "/") != 0) && + (strcmp(name, ".") != 0)) { + context->names[idx].ino = (unsigned long)-1; + context->names[idx].pino = inode->i_ino; + } else { + context->names[idx].ino = inode->i_ino; + context->names[idx].pino = (unsigned long)-1; + } +} + +/** + * audit_inode_child - collect inode info for created/removed objects + * @dname: inode's dentry name + * @inode: inode being audited + * @pino: inode number of dentry parent + * + * For syscalls that create or remove filesystem objects, audit_inode + * can only collect information for the filesystem object's parent. + * This call updates the audit context with the child's information. + * Syscalls that create a new filesystem object must be hooked after + * the object is created. Syscalls that remove a filesystem object + * must be hooked prior, in order to capture the target inode during + * unsuccessful attempts. + */ +void __audit_inode_child(const char *dname, const struct inode *inode, + unsigned long pino) +{ + int idx; + struct audit_context *context = current->audit_context; + + if (!context->in_syscall) + return; + + /* determine matching parent */ + if (dname) + for (idx = 0; idx < context->name_count; idx++) + if (context->names[idx].pino == pino) { + const char *n; + const char *name = context->names[idx].name; + int dlen = strlen(dname); + int nlen = name ? strlen(name) : 0; + + if (nlen < dlen) + continue; + + /* disregard trailing slashes */ + n = name + nlen - 1; + while ((*n == '/') && (n > name)) + n--; + + /* find last path component */ + n = n - dlen + 1; + if (n < name) + continue; + else if (n > name) { + if (*--n != '/') + continue; + else + n++; + } + + if (strncmp(n, dname, dlen) == 0) + goto update_context; + } + + /* catch-all in case match not found */ + idx = context->name_count++; + context->names[idx].name = NULL; + context->names[idx].pino = pino; +#if AUDIT_DEBUG + context->ino_count++; +#endif + +update_context: + if (inode) { + context->names[idx].ino = inode->i_ino; + context->names[idx].dev = inode->i_sb->s_dev; + context->names[idx].mode = inode->i_mode; + context->names[idx].uid = inode->i_uid; + context->names[idx].gid = inode->i_gid; + context->names[idx].rdev = inode->i_rdev; + } } /** -- cgit v1.2.3-70-g09d2 From a11f3a0574a5734db3e5de38922430d005d35118 Mon Sep 17 00:00:00 2001 From: Arjan van de Ven Date: Thu, 23 Mar 2006 03:00:33 -0800 Subject: [PATCH] sem2mutex: vfs_rename_mutex Semaphore to mutex conversion. The conversion was generated via scripts, and the result was validated automatically via a script as well. Signed-off-by: Arjan van de Ven Signed-off-by: Ingo Molnar Cc: Al Viro Cc: Christoph Hellwig Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/cifs/dir.c | 8 ++++---- fs/cifs/fcntl.c | 4 ++-- fs/cifs/file.c | 4 ++-- fs/cifs/inode.c | 16 ++++++++-------- fs/cifs/link.c | 16 ++++++++-------- fs/cifs/readdir.c | 4 ++-- fs/cifs/xattr.c | 16 ++++++++-------- fs/namei.c | 12 ++++++------ fs/super.c | 2 +- include/linux/fs.h | 2 +- 10 files changed, 42 insertions(+), 42 deletions(-) (limited to 'fs/namei.c') diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index fed55e3c53d..632561dd9c5 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -138,9 +138,9 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode, cifs_sb = CIFS_SB(inode->i_sb); pTcon = cifs_sb->tcon; - down(&direntry->d_sb->s_vfs_rename_sem); + mutex_lock(&direntry->d_sb->s_vfs_rename_mutex); full_path = build_path_from_dentry(direntry); - up(&direntry->d_sb->s_vfs_rename_sem); + mutex_unlock(&direntry->d_sb->s_vfs_rename_mutex); if(full_path == NULL) { FreeXid(xid); return -ENOMEM; @@ -317,9 +317,9 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode, cifs_sb = CIFS_SB(inode->i_sb); pTcon = cifs_sb->tcon; - down(&direntry->d_sb->s_vfs_rename_sem); + mutex_lock(&direntry->d_sb->s_vfs_rename_mutex); full_path = build_path_from_dentry(direntry); - up(&direntry->d_sb->s_vfs_rename_sem); + mutex_unlock(&direntry->d_sb->s_vfs_rename_mutex); if(full_path == NULL) rc = -ENOMEM; else if (pTcon->ses->capabilities & CAP_UNIX) { diff --git a/fs/cifs/fcntl.c b/fs/cifs/fcntl.c index a7a47bb36bf..ec4dfe9bf5e 100644 --- a/fs/cifs/fcntl.c +++ b/fs/cifs/fcntl.c @@ -86,9 +86,9 @@ int cifs_dir_notify(struct file * file, unsigned long arg) cifs_sb = CIFS_SB(file->f_dentry->d_sb); pTcon = cifs_sb->tcon; - down(&file->f_dentry->d_sb->s_vfs_rename_sem); + mutex_lock(&file->f_dentry->d_sb->s_vfs_rename_mutex); full_path = build_path_from_dentry(file->f_dentry); - up(&file->f_dentry->d_sb->s_vfs_rename_sem); + mutex_unlock(&file->f_dentry->d_sb->s_vfs_rename_mutex); if(full_path == NULL) { rc = -ENOMEM; diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 675bd256829..165d6742638 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -203,9 +203,9 @@ int cifs_open(struct inode *inode, struct file *file) } } - down(&inode->i_sb->s_vfs_rename_sem); + mutex_lock(&inode->i_sb->s_vfs_rename_mutex); full_path = build_path_from_dentry(file->f_dentry); - up(&inode->i_sb->s_vfs_rename_sem); + mutex_unlock(&inode->i_sb->s_vfs_rename_mutex); if (full_path == NULL) { FreeXid(xid); return -ENOMEM; diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 59359911f48..ff93a9f81d1 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -574,9 +574,9 @@ int cifs_unlink(struct inode *inode, struct dentry *direntry) /* Unlink can be called from rename so we can not grab the sem here since we deadlock otherwise */ -/* down(&direntry->d_sb->s_vfs_rename_sem);*/ +/* mutex_lock(&direntry->d_sb->s_vfs_rename_mutex);*/ full_path = build_path_from_dentry(direntry); -/* up(&direntry->d_sb->s_vfs_rename_sem);*/ +/* mutex_unlock(&direntry->d_sb->s_vfs_rename_mutex);*/ if (full_path == NULL) { FreeXid(xid); return -ENOMEM; @@ -718,9 +718,9 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode) cifs_sb = CIFS_SB(inode->i_sb); pTcon = cifs_sb->tcon; - down(&inode->i_sb->s_vfs_rename_sem); + mutex_lock(&inode->i_sb->s_vfs_rename_mutex); full_path = build_path_from_dentry(direntry); - up(&inode->i_sb->s_vfs_rename_sem); + mutex_unlock(&inode->i_sb->s_vfs_rename_mutex); if (full_path == NULL) { FreeXid(xid); return -ENOMEM; @@ -803,9 +803,9 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry) cifs_sb = CIFS_SB(inode->i_sb); pTcon = cifs_sb->tcon; - down(&inode->i_sb->s_vfs_rename_sem); + mutex_lock(&inode->i_sb->s_vfs_rename_mutex); full_path = build_path_from_dentry(direntry); - up(&inode->i_sb->s_vfs_rename_sem); + mutex_unlock(&inode->i_sb->s_vfs_rename_mutex); if (full_path == NULL) { FreeXid(xid); return -ENOMEM; @@ -1137,9 +1137,9 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs) rc = 0; } - down(&direntry->d_sb->s_vfs_rename_sem); + mutex_lock(&direntry->d_sb->s_vfs_rename_mutex); full_path = build_path_from_dentry(direntry); - up(&direntry->d_sb->s_vfs_rename_sem); + mutex_unlock(&direntry->d_sb->s_vfs_rename_mutex); if (full_path == NULL) { FreeXid(xid); return -ENOMEM; diff --git a/fs/cifs/link.c b/fs/cifs/link.c index 0f99aae3316..8d0da7c87c7 100644 --- a/fs/cifs/link.c +++ b/fs/cifs/link.c @@ -48,10 +48,10 @@ cifs_hardlink(struct dentry *old_file, struct inode *inode, /* No need to check for cross device links since server will do that BB note DFS case in future though (when we may have to check) */ - down(&inode->i_sb->s_vfs_rename_sem); + mutex_lock(&inode->i_sb->s_vfs_rename_mutex); fromName = build_path_from_dentry(old_file); toName = build_path_from_dentry(direntry); - up(&inode->i_sb->s_vfs_rename_sem); + mutex_unlock(&inode->i_sb->s_vfs_rename_mutex); if((fromName == NULL) || (toName == NULL)) { rc = -ENOMEM; goto cifs_hl_exit; @@ -103,9 +103,9 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd) xid = GetXid(); - down(&direntry->d_sb->s_vfs_rename_sem); + mutex_lock(&direntry->d_sb->s_vfs_rename_mutex); full_path = build_path_from_dentry(direntry); - up(&direntry->d_sb->s_vfs_rename_sem); + mutex_unlock(&direntry->d_sb->s_vfs_rename_mutex); if (!full_path) goto out_no_free; @@ -164,9 +164,9 @@ cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname) cifs_sb = CIFS_SB(inode->i_sb); pTcon = cifs_sb->tcon; - down(&inode->i_sb->s_vfs_rename_sem); + mutex_lock(&inode->i_sb->s_vfs_rename_mutex); full_path = build_path_from_dentry(direntry); - up(&inode->i_sb->s_vfs_rename_sem); + mutex_unlock(&inode->i_sb->s_vfs_rename_mutex); if(full_path == NULL) { FreeXid(xid); @@ -232,9 +232,9 @@ cifs_readlink(struct dentry *direntry, char __user *pBuffer, int buflen) /* BB would it be safe against deadlock to grab this sem even though rename itself grabs the sem and calls lookup? */ -/* down(&inode->i_sb->s_vfs_rename_sem);*/ +/* mutex_lock(&inode->i_sb->s_vfs_rename_mutex);*/ full_path = build_path_from_dentry(direntry); -/* up(&inode->i_sb->s_vfs_rename_sem);*/ +/* mutex_unlock(&inode->i_sb->s_vfs_rename_mutex);*/ if(full_path == NULL) { FreeXid(xid); diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c index 288cc048d37..edb3b6eb34b 100644 --- a/fs/cifs/readdir.c +++ b/fs/cifs/readdir.c @@ -404,9 +404,9 @@ static int initiate_cifs_search(const int xid, struct file *file) if(pTcon == NULL) return -EINVAL; - down(&file->f_dentry->d_sb->s_vfs_rename_sem); + mutex_lock(&file->f_dentry->d_sb->s_vfs_rename_mutex); full_path = build_path_from_dentry(file->f_dentry); - up(&file->f_dentry->d_sb->s_vfs_rename_sem); + mutex_unlock(&file->f_dentry->d_sb->s_vfs_rename_mutex); if(full_path == NULL) { return -ENOMEM; diff --git a/fs/cifs/xattr.c b/fs/cifs/xattr.c index 777e3363c2a..3938444d87b 100644 --- a/fs/cifs/xattr.c +++ b/fs/cifs/xattr.c @@ -62,9 +62,9 @@ int cifs_removexattr(struct dentry * direntry, const char * ea_name) cifs_sb = CIFS_SB(sb); pTcon = cifs_sb->tcon; - down(&sb->s_vfs_rename_sem); + mutex_lock(&sb->s_vfs_rename_mutex); full_path = build_path_from_dentry(direntry); - up(&sb->s_vfs_rename_sem); + mutex_unlock(&sb->s_vfs_rename_mutex); if(full_path == NULL) { FreeXid(xid); return -ENOMEM; @@ -116,9 +116,9 @@ int cifs_setxattr(struct dentry * direntry, const char * ea_name, cifs_sb = CIFS_SB(sb); pTcon = cifs_sb->tcon; - down(&sb->s_vfs_rename_sem); + mutex_lock(&sb->s_vfs_rename_mutex); full_path = build_path_from_dentry(direntry); - up(&sb->s_vfs_rename_sem); + mutex_unlock(&sb->s_vfs_rename_mutex); if(full_path == NULL) { FreeXid(xid); return -ENOMEM; @@ -223,9 +223,9 @@ ssize_t cifs_getxattr(struct dentry * direntry, const char * ea_name, cifs_sb = CIFS_SB(sb); pTcon = cifs_sb->tcon; - down(&sb->s_vfs_rename_sem); + mutex_lock(&sb->s_vfs_rename_mutex); full_path = build_path_from_dentry(direntry); - up(&sb->s_vfs_rename_sem); + mutex_unlock(&sb->s_vfs_rename_mutex); if(full_path == NULL) { FreeXid(xid); return -ENOMEM; @@ -341,9 +341,9 @@ ssize_t cifs_listxattr(struct dentry * direntry, char * data, size_t buf_size) cifs_sb = CIFS_SB(sb); pTcon = cifs_sb->tcon; - down(&sb->s_vfs_rename_sem); + mutex_lock(&sb->s_vfs_rename_mutex); full_path = build_path_from_dentry(direntry); - up(&sb->s_vfs_rename_sem); + mutex_unlock(&sb->s_vfs_rename_mutex); if(full_path == NULL) { FreeXid(xid); return -ENOMEM; diff --git a/fs/namei.c b/fs/namei.c index 8dc2b038d5d..c72b940797f 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -104,7 +104,7 @@ */ /* * [Sep 2001 AV] Single-semaphore locking scheme (kudos to David Holland) - * implemented. Let's see if raised priority of ->s_vfs_rename_sem gives + * implemented. Let's see if raised priority of ->s_vfs_rename_mutex gives * any extra contention... */ @@ -1422,7 +1422,7 @@ struct dentry *lock_rename(struct dentry *p1, struct dentry *p2) return NULL; } - down(&p1->d_inode->i_sb->s_vfs_rename_sem); + mutex_lock(&p1->d_inode->i_sb->s_vfs_rename_mutex); for (p = p1; p->d_parent != p; p = p->d_parent) { if (p->d_parent == p2) { @@ -1450,7 +1450,7 @@ void unlock_rename(struct dentry *p1, struct dentry *p2) mutex_unlock(&p1->d_inode->i_mutex); if (p1 != p2) { mutex_unlock(&p2->d_inode->i_mutex); - up(&p1->d_inode->i_sb->s_vfs_rename_sem); + mutex_unlock(&p1->d_inode->i_sb->s_vfs_rename_mutex); } } @@ -2277,17 +2277,17 @@ asmlinkage long sys_link(const char __user *oldname, const char __user *newname) * a) we can get into loop creation. Check is done in is_subdir(). * b) race potential - two innocent renames can create a loop together. * That's where 4.4 screws up. Current fix: serialization on - * sb->s_vfs_rename_sem. We might be more accurate, but that's another + * sb->s_vfs_rename_mutex. We might be more accurate, but that's another * story. * c) we have to lock _three_ objects - parents and victim (if it exists). * And that - after we got ->i_mutex on parents (until then we don't know * whether the target exists). Solution: try to be smart with locking * order for inodes. We rely on the fact that tree topology may change - * only under ->s_vfs_rename_sem _and_ that parent of the object we + * only under ->s_vfs_rename_mutex _and_ that parent of the object we * move will be locked. Thus we can rank directories by the tree * (ancestors first) and rank all non-directories after them. * That works since everybody except rename does "lock parent, lookup, - * lock child" and rename is under ->s_vfs_rename_sem. + * lock child" and rename is under ->s_vfs_rename_mutex. * HOWEVER, it relies on the assumption that any object with ->lookup() * has no more than 1 dentry. If "hybrid" objects will ever appear, * we'd better make sure that there's no link(2) for them. diff --git a/fs/super.c b/fs/super.c index 9cc6545dfa4..425861cb1ca 100644 --- a/fs/super.c +++ b/fs/super.c @@ -76,7 +76,7 @@ static struct super_block *alloc_super(void) down_write(&s->s_umount); s->s_count = S_BIAS; atomic_set(&s->s_active, 1); - sema_init(&s->s_vfs_rename_sem,1); + mutex_init(&s->s_vfs_rename_mutex); mutex_init(&s->s_dquot.dqio_mutex); mutex_init(&s->s_dquot.dqonoff_mutex); init_rwsem(&s->s_dquot.dqptr_sem); diff --git a/include/linux/fs.h b/include/linux/fs.h index 9ed1f36b6d5..0f71ee73012 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -847,7 +847,7 @@ struct super_block { * The next field is for VFS *only*. No filesystems have any business * even looking at it. You had been warned. */ - struct semaphore s_vfs_rename_sem; /* Kludge */ + struct mutex s_vfs_rename_mutex; /* Kludge */ /* Granuality of c/m/atime in ns. Cannot be worse than a second */ -- cgit v1.2.3-70-g09d2 From 4af4c52f34606bdaab6930a845550c6fb02078a4 Mon Sep 17 00:00:00 2001 From: Oleg Drokin Date: Sat, 25 Mar 2006 03:06:54 -0800 Subject: [PATCH] Missed error checking for intent's filp in open_namei(). It seems there is error check missing in open_namei for errors returned through intent.open.file (from lookup_instantiate_filp). If there is plain open performed, then such a check done inside __path_lookup_intent_open called from path_lookup_open(), but when the open is performed with O_CREAT flag set, then __path_lookup_intent_open is only called with LOOKUP_PARENT set where no file opening can occur yet. Later on lookup_hash is called where exact opening might take place and intent.open.file may be filled. If it is filled with error value of some sort, then we get kernel attempting to dereference this error value as address (and corresponding oops) in nameidata_to_filp() called from filp_open(). While this is relatively simple to workaround in ->lookup() method by just checking lookup_instantiate_filp() return value and returning error as needed, this is not so easy in ->d_revalidate(), where we can only return "yes, dentry is valid" or "no, dentry is invalid, perform full lookup again", and just returning 0 on error would cause extra lookup (with potential extra costly RPCs). So in short, I believe that there should be no difference in error handling for opening a file and creating a file in open_namei() and propose this simple patch as a solution. Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/namei.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'fs/namei.c') diff --git a/fs/namei.c b/fs/namei.c index c72b940797f..1baf1b06fe4 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1628,6 +1628,12 @@ do_last: goto exit; } + if (IS_ERR(nd->intent.open.file)) { + mutex_unlock(&dir->d_inode->i_mutex); + error = PTR_ERR(nd->intent.open.file); + goto exit_dput; + } + /* Negative dentry, just create the file */ if (!path.dentry->d_inode) { if (!IS_POSIXACL(dir->d_inode)) -- cgit v1.2.3-70-g09d2 From 7e53cac41da9ebb9be774220c1b2615182667c9d Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Sat, 25 Mar 2006 03:07:57 -0800 Subject: [PATCH] Honour AOP_TRUNCATE_PAGE returns in page_symlink As prepare_write, commit_write and readpage are allowed to return AOP_TRUNCATE_PAGE, page_symlink should respond to them. Signed-off-by: Neil Brown Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/namei.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'fs/namei.c') diff --git a/fs/namei.c b/fs/namei.c index 1baf1b06fe4..712dfc77793 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2627,16 +2627,27 @@ int __page_symlink(struct inode *inode, const char *symname, int len, int err = -ENOMEM; char *kaddr; +retry: page = find_or_create_page(mapping, 0, gfp_mask); if (!page) goto fail; err = mapping->a_ops->prepare_write(NULL, page, 0, len-1); + if (err == AOP_TRUNCATED_PAGE) { + page_cache_release(page); + goto retry; + } if (err) goto fail_map; kaddr = kmap_atomic(page, KM_USER0); memcpy(kaddr, symname, len-1); kunmap_atomic(kaddr, KM_USER0); - mapping->a_ops->commit_write(NULL, page, 0, len-1); + err = mapping->a_ops->commit_write(NULL, page, 0, len-1); + if (err == AOP_TRUNCATED_PAGE) { + page_cache_release(page); + goto retry; + } + if (err) + goto fail_map; /* * Notice that we are _not_ going to block here - end of page is * unmapped, so this will only try to map the rest of page, see @@ -2646,7 +2657,8 @@ int __page_symlink(struct inode *inode, const char *symname, int len, */ if (!PageUptodate(page)) { err = mapping->a_ops->readpage(NULL, page); - wait_on_page_locked(page); + if (err != AOP_TRUNCATED_PAGE) + wait_on_page_locked(page); } else { unlock_page(page); } -- cgit v1.2.3-70-g09d2 From 051d381259eb57d6074d02a6ba6e90e744f1a29f Mon Sep 17 00:00:00 2001 From: Ian Kent Date: Mon, 27 Mar 2006 01:14:53 -0800 Subject: [PATCH] autofs4: nameidata needs to be up to date for follow_link In order to be able to trigger a mount using the follow_link inode method the nameidata struct that is passed in needs to have the vfsmount of the autofs trigger not its parent. During a path walk if an autofs trigger is mounted on a dentry, when the follow_link method is called, the nameidata struct contains the vfsmount and mountpoint dentry of the parent mount while the dentry that is passed in is the root of the autofs trigger mount. I believe it is impossible to get the vfsmount of the trigger mount, within the follow_link method, when only the parent vfsmount and the root dentry of the trigger mount are known. This patch updates the nameidata struct on entry to __do_follow_link if it detects that it is out of date. It moves the path_to_nameidata to above __do_follow_link to facilitate calling it from there. The dput_path is moved as well as that seemed sensible. No changes are made to these two functions. Signed-off-by: Ian Kent Cc: Al Viro Cc: Christoph Hellwig Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/namei.c | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) (limited to 'fs/namei.c') diff --git a/fs/namei.c b/fs/namei.c index 98dc2e13436..22f6e8d16aa 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -546,6 +546,22 @@ struct path { struct dentry *dentry; }; +static inline void dput_path(struct path *path, struct nameidata *nd) +{ + dput(path->dentry); + if (path->mnt != nd->mnt) + mntput(path->mnt); +} + +static inline void path_to_nameidata(struct path *path, struct nameidata *nd) +{ + dput(nd->dentry); + if (nd->mnt != path->mnt) + mntput(nd->mnt); + nd->mnt = path->mnt; + nd->dentry = path->dentry; +} + static __always_inline int __do_follow_link(struct path *path, struct nameidata *nd) { int error; @@ -555,8 +571,11 @@ static __always_inline int __do_follow_link(struct path *path, struct nameidata touch_atime(path->mnt, dentry); nd_set_link(nd, NULL); - if (path->mnt == nd->mnt) - mntget(path->mnt); + if (path->mnt != nd->mnt) { + path_to_nameidata(path, nd); + dget(dentry); + } + mntget(path->mnt); cookie = dentry->d_inode->i_op->follow_link(dentry, nd); error = PTR_ERR(cookie); if (!IS_ERR(cookie)) { @@ -573,22 +592,6 @@ static __always_inline int __do_follow_link(struct path *path, struct nameidata return error; } -static inline void dput_path(struct path *path, struct nameidata *nd) -{ - dput(path->dentry); - if (path->mnt != nd->mnt) - mntput(path->mnt); -} - -static inline void path_to_nameidata(struct path *path, struct nameidata *nd) -{ - dput(nd->dentry); - if (nd->mnt != path->mnt) - mntput(nd->mnt); - nd->mnt = path->mnt; - nd->dentry = path->dentry; -} - /* * This limits recursive symlink follows to 8, while * limiting consecutive symlinks to 40. -- cgit v1.2.3-70-g09d2 From a244e1698ae3609cdfe24088e1293593cb7a5278 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Fri, 31 Mar 2006 02:32:11 -0800 Subject: [PATCH] fs/namei.c: make lookup_hash() static As announced, lookup_hash() can now become static. Signed-off-by: Adrian Bunk Cc: Christoph Hellwig Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/feature-removal-schedule.txt | 7 ------- fs/namei.c | 3 +-- include/linux/namei.h | 1 - 3 files changed, 1 insertion(+), 10 deletions(-) (limited to 'fs/namei.c') diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index a92b10bb0b0..59d0c74c79c 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -127,13 +127,6 @@ Who: Christoph Hellwig --------------------------- -What: EXPORT_SYMBOL(lookup_hash) -When: January 2006 -Why: Too low-level interface. Use lookup_one_len or lookup_create instead. -Who: Christoph Hellwig - ---------------------------- - What: CONFIG_FORCED_INLINING When: June 2006 Why: Config option is there to see if gcc is good enough. (in january diff --git a/fs/namei.c b/fs/namei.c index 22f6e8d16aa..96723ae83c8 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1254,7 +1254,7 @@ out: return dentry; } -struct dentry * lookup_hash(struct nameidata *nd) +static struct dentry *lookup_hash(struct nameidata *nd) { return __lookup_hash(&nd->last, nd->dentry, nd); } @@ -2697,7 +2697,6 @@ EXPORT_SYMBOL(follow_up); EXPORT_SYMBOL(get_write_access); /* binfmt_aout */ EXPORT_SYMBOL(getname); EXPORT_SYMBOL(lock_rename); -EXPORT_SYMBOL(lookup_hash); EXPORT_SYMBOL(lookup_one_len); EXPORT_SYMBOL(page_follow_link_light); EXPORT_SYMBOL(page_put_link); diff --git a/include/linux/namei.h b/include/linux/namei.h index e6698013e4d..58cb3d3d44b 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -75,7 +75,6 @@ extern struct file *nameidata_to_filp(struct nameidata *nd, int flags); extern void release_open_intent(struct nameidata *); extern struct dentry * lookup_one_len(const char *, struct dentry *, int); -extern __deprecated_for_modules struct dentry * lookup_hash(struct nameidata *); extern int follow_down(struct vfsmount **, struct dentry **); extern int follow_up(struct vfsmount **, struct dentry **); -- cgit v1.2.3-70-g09d2