summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2008-04-26 06:05:55 +0400
committerLinus Torvalds <torvalds@linux-foundation.org>2008-04-26 06:05:55 +0400
commitbc84e0a160e383deb56568f4e03bc51b1ce16775 (patch)
tree3c8cf1ae23c2f9c165bd7ce63a40b1d5c3aee275
parenta92910723a5af54f81373875fd95133c88df94bd (diff)
parentf8f95702f0c4529b0f59488f4509608f0c160e77 (diff)
downloadlinux-bc84e0a160e383deb56568f4e03bc51b1ce16775.tar.xz
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs-2.6
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs-2.6: [PATCH] sanitize locate_fd() [PATCH] sanitize unshare_files/reset_files_struct [PATCH] sanitize handling of shared descriptor tables in failing execve() [PATCH] close race in unshare_files() [PATCH] restore sane ->umount_begin() API cifs: timeout dfs automounts +little fix.
-rw-r--r--fs/9p/vfs_super.c7
-rw-r--r--fs/binfmt_elf.c23
-rw-r--r--fs/binfmt_misc.c18
-rw-r--r--fs/binfmt_som.c10
-rw-r--r--fs/cifs/cifs_dfs_ref.c29
-rw-r--r--fs/cifs/cifsfs.c10
-rw-r--r--fs/cifs/cifsproto.h8
-rw-r--r--fs/exec.c28
-rw-r--r--fs/fcntl.c40
-rw-r--r--fs/fuse/inode.c5
-rw-r--r--fs/namespace.c9
-rw-r--r--fs/nfs/super.c8
-rw-r--r--include/linux/file.h3
-rw-r--r--include/linux/fs.h5
-rw-r--r--kernel/exit.c6
-rw-r--r--kernel/fork.c58
16 files changed, 95 insertions, 172 deletions
diff --git a/fs/9p/vfs_super.c b/fs/9p/vfs_super.c
index 678c02f1ae23..a452ac67fc94 100644
--- a/fs/9p/vfs_super.c
+++ b/fs/9p/vfs_super.c
@@ -224,12 +224,11 @@ static int v9fs_show_options(struct seq_file *m, struct vfsmount *mnt)
}
static void
-v9fs_umount_begin(struct vfsmount *vfsmnt, int flags)
+v9fs_umount_begin(struct super_block *sb)
{
- struct v9fs_session_info *v9ses = vfsmnt->mnt_sb->s_fs_info;
+ struct v9fs_session_info *v9ses = sb->s_fs_info;
- if (flags & MNT_FORCE)
- v9fs_session_cancel(v9ses);
+ v9fs_session_cancel(v9ses);
}
static const struct super_operations v9fs_super_ops = {
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index 5e1a4fb5cacb..9924581df6f6 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -543,7 +543,6 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs)
unsigned long interp_load_addr = 0;
unsigned long start_code, end_code, start_data, end_data;
unsigned long reloc_func_desc = 0;
- struct files_struct *files;
int executable_stack = EXSTACK_DEFAULT;
unsigned long def_flags = 0;
struct {
@@ -593,20 +592,9 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs)
goto out_free_ph;
}
- files = current->files; /* Refcounted so ok */
- retval = unshare_files();
- if (retval < 0)
- goto out_free_ph;
- if (files == current->files) {
- put_files_struct(files);
- files = NULL;
- }
-
- /* exec will make our files private anyway, but for the a.out
- loader stuff we need to do it earlier */
retval = get_unused_fd();
if (retval < 0)
- goto out_free_fh;
+ goto out_free_ph;
get_file(bprm->file);
fd_install(elf_exec_fileno = retval, bprm->file);
@@ -728,12 +716,6 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs)
if (retval)
goto out_free_dentry;
- /* Discard our unneeded old files struct */
- if (files) {
- put_files_struct(files);
- files = NULL;
- }
-
/* OK, This is the point of no return */
current->flags &= ~PF_FORKNOEXEC;
current->mm->def_flags = def_flags;
@@ -1016,9 +998,6 @@ out_free_interp:
kfree(elf_interpreter);
out_free_file:
sys_close(elf_exec_fileno);
-out_free_fh:
- if (files)
- reset_files_struct(current, files);
out_free_ph:
kfree(elf_phdata);
goto out;
diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c
index b53c7e5f41bb..dbf0ac0523de 100644
--- a/fs/binfmt_misc.c
+++ b/fs/binfmt_misc.c
@@ -110,7 +110,6 @@ static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs)
char *iname_addr = iname;
int retval;
int fd_binary = -1;
- struct files_struct *files = NULL;
retval = -ENOEXEC;
if (!enabled)
@@ -133,21 +132,13 @@ static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs)
if (fmt->flags & MISC_FMT_OPEN_BINARY) {
- files = current->files;
- retval = unshare_files();
- if (retval < 0)
- goto _ret;
- if (files == current->files) {
- put_files_struct(files);
- files = NULL;
- }
/* if the binary should be opened on behalf of the
* interpreter than keep it open and assign descriptor
* to it */
fd_binary = get_unused_fd();
if (fd_binary < 0) {
retval = fd_binary;
- goto _unshare;
+ goto _ret;
}
fd_install(fd_binary, bprm->file);
@@ -205,10 +196,6 @@ static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs)
if (retval < 0)
goto _error;
- if (files) {
- put_files_struct(files);
- files = NULL;
- }
_ret:
return retval;
_error:
@@ -216,9 +203,6 @@ _error:
sys_close(fd_binary);
bprm->interp_flags = 0;
bprm->interp_data = 0;
-_unshare:
- if (files)
- reset_files_struct(current, files);
goto _ret;
}
diff --git a/fs/binfmt_som.c b/fs/binfmt_som.c
index 14c63527c762..fdc36bfd6a7b 100644
--- a/fs/binfmt_som.c
+++ b/fs/binfmt_som.c
@@ -194,7 +194,6 @@ load_som_binary(struct linux_binprm * bprm, struct pt_regs * regs)
unsigned long som_entry;
struct som_hdr *som_ex;
struct som_exec_auxhdr *hpuxhdr;
- struct files_struct *files;
/* Get the exec-header */
som_ex = (struct som_hdr *) bprm->buf;
@@ -221,15 +220,6 @@ load_som_binary(struct linux_binprm * bprm, struct pt_regs * regs)
goto out_free;
}
- files = current->files; /* Refcounted so ok */
- retval = unshare_files();
- if (retval < 0)
- goto out_free;
- if (files == current->files) {
- put_files_struct(files);
- files = NULL;
- }
-
retval = get_unused_fd();
if (retval < 0)
goto out_free;
diff --git a/fs/cifs/cifs_dfs_ref.c b/fs/cifs/cifs_dfs_ref.c
index f53f41ff1665..95024c066d89 100644
--- a/fs/cifs/cifs_dfs_ref.c
+++ b/fs/cifs/cifs_dfs_ref.c
@@ -25,14 +25,26 @@
static LIST_HEAD(cifs_dfs_automount_list);
-/*
- * DFS functions
-*/
+static void cifs_dfs_expire_automounts(struct work_struct *work);
+static DECLARE_DELAYED_WORK(cifs_dfs_automount_task,
+ cifs_dfs_expire_automounts);
+static int cifs_dfs_mountpoint_expiry_timeout = 500 * HZ;
+
+static void cifs_dfs_expire_automounts(struct work_struct *work)
+{
+ struct list_head *list = &cifs_dfs_automount_list;
+
+ mark_mounts_for_expiry(list);
+ if (!list_empty(list))
+ schedule_delayed_work(&cifs_dfs_automount_task,
+ cifs_dfs_mountpoint_expiry_timeout);
+}
-void dfs_shrink_umount_helper(struct vfsmount *vfsmnt)
+void cifs_dfs_release_automount_timer(void)
{
- mark_mounts_for_expiry(&cifs_dfs_automount_list);
- mark_mounts_for_expiry(&cifs_dfs_automount_list);
+ BUG_ON(!list_empty(&cifs_dfs_automount_list));
+ cancel_delayed_work(&cifs_dfs_automount_task);
+ flush_scheduled_work();
}
/**
@@ -261,10 +273,11 @@ static int add_mount_helper(struct vfsmount *newmnt, struct nameidata *nd,
err = do_add_mount(newmnt, nd, nd->path.mnt->mnt_flags, mntlist);
switch (err) {
case 0:
- dput(nd->path.dentry);
- mntput(nd->path.mnt);
+ path_put(&nd->path);
nd->path.mnt = newmnt;
nd->path.dentry = dget(newmnt->mnt_root);
+ schedule_delayed_work(&cifs_dfs_automount_task,
+ cifs_dfs_mountpoint_expiry_timeout);
break;
case -EBUSY:
/* someone else made a mount here whilst we were busy */
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index a04b17e5a9d0..39c2cbdface7 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -466,16 +466,11 @@ static struct quotactl_ops cifs_quotactl_ops = {
};
#endif
-static void cifs_umount_begin(struct vfsmount *vfsmnt, int flags)
+static void cifs_umount_begin(struct super_block *sb)
{
- struct cifs_sb_info *cifs_sb;
+ struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
struct cifsTconInfo *tcon;
- dfs_shrink_umount_helper(vfsmnt);
-
- if (!(flags & MNT_FORCE))
- return;
- cifs_sb = CIFS_SB(vfsmnt->mnt_sb);
if (cifs_sb == NULL)
return;
@@ -1100,6 +1095,7 @@ exit_cifs(void)
cFYI(DBG2, ("exit_cifs"));
cifs_proc_clean();
#ifdef CONFIG_CIFS_DFS_UPCALL
+ cifs_dfs_release_automount_timer();
unregister_key_type(&key_type_dns_resolver);
#endif
#ifdef CONFIG_CIFS_UPCALL
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index 0c83da4a7dab..50f9fdae19b3 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -104,13 +104,7 @@ extern int mode_to_acl(struct inode *inode, const char *path, __u64);
extern int cifs_mount(struct super_block *, struct cifs_sb_info *, char *,
const char *);
extern int cifs_umount(struct super_block *, struct cifs_sb_info *);
-#ifdef CONFIG_CIFS_DFS_UPCALL
-extern void dfs_shrink_umount_helper(struct vfsmount *vfsmnt);
-#else
-static inline void dfs_shrink_umount_helper(struct vfsmount *vfsmnt)
-{
-}
-#endif /* DFS_UPCALL */
+extern void cifs_dfs_release_automount_timer(void);
void cifs_proc_init(void);
void cifs_proc_clean(void);
diff --git a/fs/exec.c b/fs/exec.c
index 54a0a557b678..b152029f18f6 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -953,7 +953,6 @@ int flush_old_exec(struct linux_binprm * bprm)
{
char * name;
int i, ch, retval;
- struct files_struct *files;
char tcomm[sizeof(current->comm)];
/*
@@ -965,26 +964,15 @@ int flush_old_exec(struct linux_binprm * bprm)
goto out;
/*
- * Make sure we have private file handles. Ask the
- * fork helper to do the work for us and the exit
- * helper to do the cleanup of the old one.
- */
- files = current->files; /* refcounted so safe to hold */
- retval = unshare_files();
- if (retval)
- goto out;
- /*
* Release all of the old mmap stuff
*/
retval = exec_mmap(bprm->mm);
if (retval)
- goto mmap_failed;
+ goto out;
bprm->mm = NULL; /* We're using it now */
/* This is the point of no return */
- put_files_struct(files);
-
current->sas_ss_sp = current->sas_ss_size = 0;
if (current->euid == current->uid && current->egid == current->gid)
@@ -1034,8 +1022,6 @@ int flush_old_exec(struct linux_binprm * bprm)
return 0;
-mmap_failed:
- reset_files_struct(current, files);
out:
return retval;
}
@@ -1283,12 +1269,17 @@ int do_execve(char * filename,
struct linux_binprm *bprm;
struct file *file;
unsigned long env_p;
+ struct files_struct *displaced;
int retval;
+ retval = unshare_files(&displaced);
+ if (retval)
+ goto out_ret;
+
retval = -ENOMEM;
bprm = kzalloc(sizeof(*bprm), GFP_KERNEL);
if (!bprm)
- goto out_ret;
+ goto out_files;
file = open_exec(filename);
retval = PTR_ERR(file);
@@ -1343,6 +1334,8 @@ int do_execve(char * filename,
security_bprm_free(bprm);
acct_update_integrals(current);
kfree(bprm);
+ if (displaced)
+ put_files_struct(displaced);
return retval;
}
@@ -1363,6 +1356,9 @@ out_file:
out_kfree:
kfree(bprm);
+out_files:
+ if (displaced)
+ reset_files_struct(displaced);
out_ret:
return retval;
}
diff --git a/fs/fcntl.c b/fs/fcntl.c
index e632da761fc1..3f3ac630ccde 100644
--- a/fs/fcntl.c
+++ b/fs/fcntl.c
@@ -55,14 +55,16 @@ static int get_close_on_exec(unsigned int fd)
* file_lock held for write.
*/
-static int locate_fd(struct files_struct *files,
- struct file *file, unsigned int orig_start)
+static int locate_fd(unsigned int orig_start, int cloexec)
{
+ struct files_struct *files = current->files;
unsigned int newfd;
unsigned int start;
int error;
struct fdtable *fdt;
+ spin_lock(&files->file_lock);
+
error = -EINVAL;
if (orig_start >= current->signal->rlim[RLIMIT_NOFILE].rlim_cur)
goto out;
@@ -97,42 +99,28 @@ repeat:
if (error)
goto repeat;
- /*
- * We reacquired files_lock, so we are safe as long as
- * we reacquire the fdtable pointer and use it while holding
- * the lock, no one can free it during that time.
- */
if (start <= files->next_fd)
files->next_fd = newfd + 1;
+ FD_SET(newfd, fdt->open_fds);
+ if (cloexec)
+ FD_SET(newfd, fdt->close_on_exec);
+ else
+ FD_CLR(newfd, fdt->close_on_exec);
error = newfd;
-
+
out:
+ spin_unlock(&files->file_lock);
return error;
}
static int dupfd(struct file *file, unsigned int start, int cloexec)
{
- struct files_struct * files = current->files;
- struct fdtable *fdt;
- int fd;
-
- spin_lock(&files->file_lock);
- fd = locate_fd(files, file, start);
- if (fd >= 0) {
- /* locate_fd() may have expanded fdtable, load the ptr */
- fdt = files_fdtable(files);
- FD_SET(fd, fdt->open_fds);
- if (cloexec)
- FD_SET(fd, fdt->close_on_exec);
- else
- FD_CLR(fd, fdt->close_on_exec);
- spin_unlock(&files->file_lock);
+ int fd = locate_fd(start, cloexec);
+ if (fd >= 0)
fd_install(fd, file);
- } else {
- spin_unlock(&files->file_lock);
+ else
fput(file);
- }
return fd;
}
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index 033f7bdd47e8..4df34da2284a 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -242,10 +242,9 @@ struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid,
return inode;
}
-static void fuse_umount_begin(struct vfsmount *vfsmnt, int flags)
+static void fuse_umount_begin(struct super_block *sb)
{
- if (flags & MNT_FORCE)
- fuse_abort_conn(get_fuse_conn_super(vfsmnt->mnt_sb));
+ fuse_abort_conn(get_fuse_conn_super(sb));
}
static void fuse_send_destroy(struct fuse_conn *fc)
diff --git a/fs/namespace.c b/fs/namespace.c
index 0505fb61aa74..f48f98110c30 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -1061,10 +1061,11 @@ static int do_umount(struct vfsmount *mnt, int flags)
* about for the moment.
*/
- lock_kernel();
- if (sb->s_op->umount_begin)
- sb->s_op->umount_begin(mnt, flags);
- unlock_kernel();
+ if (flags & MNT_FORCE && sb->s_op->umount_begin) {
+ lock_kernel();
+ sb->s_op->umount_begin(sb);
+ unlock_kernel();
+ }
/*
* No sense to grab the lock for this test, but test itself looks
diff --git a/fs/nfs/super.c b/fs/nfs/super.c
index 20a1cb1810fe..fa220dc74609 100644
--- a/fs/nfs/super.c
+++ b/fs/nfs/super.c
@@ -198,7 +198,7 @@ static match_table_t nfs_secflavor_tokens = {
};
-static void nfs_umount_begin(struct vfsmount *, int);
+static void nfs_umount_begin(struct super_block *);
static int nfs_statfs(struct dentry *, struct kstatfs *);
static int nfs_show_options(struct seq_file *, struct vfsmount *);
static int nfs_show_stats(struct seq_file *, struct vfsmount *);
@@ -647,13 +647,11 @@ static int nfs_show_stats(struct seq_file *m, struct vfsmount *mnt)
* Begin unmount by attempting to remove all automounted mountpoints we added
* in response to xdev traversals and referrals
*/
-static void nfs_umount_begin(struct vfsmount *vfsmnt, int flags)
+static void nfs_umount_begin(struct super_block *sb)
{
- struct nfs_server *server = NFS_SB(vfsmnt->mnt_sb);
+ struct nfs_server *server = NFS_SB(sb);
struct rpc_clnt *rpc;
- if (!(flags & MNT_FORCE))
- return;
/* -EIO all pending I/O */
rpc = server->client_acl;
if (!IS_ERR(rpc))
diff --git a/include/linux/file.h b/include/linux/file.h
index 653477021e4c..69baf5a4f0a5 100644
--- a/include/linux/file.h
+++ b/include/linux/file.h
@@ -117,7 +117,8 @@ struct task_struct;
struct files_struct *get_files_struct(struct task_struct *);
void put_files_struct(struct files_struct *fs);
-void reset_files_struct(struct task_struct *, struct files_struct *);
+void reset_files_struct(struct files_struct *);
+int unshare_files(struct files_struct **);
extern struct kmem_cache *files_cachep;
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 6556f2f967e5..d6d7c52055c6 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1309,7 +1309,7 @@ struct super_operations {
int (*statfs) (struct dentry *, struct kstatfs *);
int (*remount_fs) (struct super_block *, int *, char *);
void (*clear_inode) (struct inode *);
- void (*umount_begin) (struct vfsmount *, int);
+ void (*umount_begin) (struct super_block *);
int (*show_options)(struct seq_file *, struct vfsmount *);
int (*show_stats)(struct seq_file *, struct vfsmount *);
@@ -2034,9 +2034,6 @@ static inline ino_t parent_ino(struct dentry *dentry)
return res;
}
-/* kernel/fork.c */
-extern int unshare_files(void);
-
/* Transaction based IO helpers */
/*
diff --git a/kernel/exit.c b/kernel/exit.c
index cece89f80ab4..97f609f574b1 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -507,10 +507,9 @@ void put_files_struct(struct files_struct *files)
}
}
-EXPORT_SYMBOL(put_files_struct);
-
-void reset_files_struct(struct task_struct *tsk, struct files_struct *files)
+void reset_files_struct(struct files_struct *files)
{
+ struct task_struct *tsk = current;
struct files_struct *old;
old = tsk->files;
@@ -519,7 +518,6 @@ void reset_files_struct(struct task_struct *tsk, struct files_struct *files)
task_unlock(tsk);
put_files_struct(old);
}
-EXPORT_SYMBOL(reset_files_struct);
void exit_files(struct task_struct *tsk)
{
diff --git a/kernel/fork.c b/kernel/fork.c
index 89fe414645e9..efb618fc8ffe 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -805,12 +805,6 @@ static int copy_files(unsigned long clone_flags, struct task_struct * tsk)
goto out;
}
- /*
- * Note: we may be using current for both targets (See exec.c)
- * This works because we cache current->files (old) as oldf. Don't
- * break this.
- */
- tsk->files = NULL;
newf = dup_fd(oldf, &error);
if (!newf)
goto out;
@@ -846,34 +840,6 @@ static int copy_io(unsigned long clone_flags, struct task_struct *tsk)
return 0;
}
-/*
- * Helper to unshare the files of the current task.
- * We don't want to expose copy_files internals to
- * the exec layer of the kernel.
- */
-
-int unshare_files(void)
-{
- struct files_struct *files = current->files;
- int rc;
-
- BUG_ON(!files);
-
- /* This can race but the race causes us to copy when we don't
- need to and drop the copy */
- if(atomic_read(&files->count) == 1)
- {
- atomic_inc(&files->count);
- return 0;
- }
- rc = copy_files(0, current);
- if(rc)
- current->files = files;
- return rc;
-}
-
-EXPORT_SYMBOL(unshare_files);
-
static int copy_sighand(unsigned long clone_flags, struct task_struct *tsk)
{
struct sighand_struct *sig;
@@ -1811,3 +1777,27 @@ bad_unshare_cleanup_thread:
bad_unshare_out:
return err;
}
+
+/*
+ * Helper to unshare the files of the current task.
+ * We don't want to expose copy_files internals to
+ * the exec layer of the kernel.
+ */
+
+int unshare_files(struct files_struct **displaced)
+{
+ struct task_struct *task = current;
+ struct files_struct *copy;
+ int error;
+
+ error = unshare_fd(CLONE_FILES, &copy);
+ if (error || !copy) {
+ *displaced = NULL;
+ return error;
+ }
+ *displaced = task->files;
+ task_lock(task);
+ task->files = copy;
+ task_unlock(task);
+ return 0;
+}