There are some problems around permission bits of a dir.
For example, unionfs cannot remove the un-writable dir while unix/posix
can. And if a dir doen't have exec bit (rare case), unionfs cannot
handle it correctly.
This patch will solve those problems.

Junjiro Okajima


unionfs /dev/shm/u unionfs 
rw,dirs=/dev/shm/rw=rw:/dev/shm/ro=ro,debug=0,delete=all,copyup=preserve 0 0
$ cd /dev/shm/u
$ mkdir a
$ sudo mkdir a/b
$ ls -al a
total 3
drwxr-xr-x   3 jro  jro  1024 Feb 10 17:02 .
drwxrwxrwx  22 jro  jro  1024 Feb 10 17:02 ..
drwxr-xr-x   2 root root 1024 Feb 10 17:02 b
$ id
uid=1000(jro) gid=1000(jro) groups=1000(jro)
$ rmdir a/b
rmdir: `a/b': Directory not empty

$ mkdir a/b
$ chmod -x a/b
$ ls -al a
total 3
drwxr-xr-x   3 jro jro 1024 Feb 10 17:21 .
drwxrwxrwx  22 jro jro 1024 Feb 10 17:21 ..
drw-r--r--   2 jro jro 1024 Feb 10 17:21 b
$ find /usr -ls # forget dentry 'b'
$ ls -al a
ls: a/b: Permission denied
total 2

drwxr-xr-x   3 jro jro 1024 Feb 10 17:11 .
drwxrwxrwx  22 jro jro 1024 Feb 10 17:11 ..
$ rmdir a/b
rmdir: `a/b': Permission denied


--- unionfs-20060208-2346/dirhelper.c   9 Feb 2006 13:04:42 -0000       1.1
+++ unionfs-20060208-2346/dirhelper.c   10 Feb 2006 06:55:42 -0000      1.3
@@ -29,7 +29,10 @@ int delete_whiteouts(struct dentry *dent
        struct dentry *hidden_dir_dentry = NULL;
        struct dentry *hidden_dentry;
        struct super_block *sb;
-       char *name = NULL;
+       char *name = NULL, *p;
+       struct inode *hidden_dir;
+       struct superio sio;
+       int do_superio;
 
        int i;
        struct list_head *pos;
@@ -38,22 +41,34 @@ int delete_whiteouts(struct dentry *dent
        print_entry_location();
 
        sb = dentry->d_sb;
+       unionfs_read_lock(sb);
 
        BUG_ON(!S_ISDIR(dentry->d_inode->i_mode));
        BUG_ON(bindex < dbstart(dentry));
        BUG_ON(bindex > dbend(dentry));
+       err = is_robranch_super(sb, bindex);
+       if (err)
+               goto out;
 
        /* Find out hidden parent dentry */
        hidden_dir_dentry = dtohd_index(dentry, bindex);
        BUG_ON(!S_ISDIR(hidden_dir_dentry->d_inode->i_mode));
+       hidden_dir = hidden_dir_dentry->d_inode;
+       BUG_ON(!S_ISDIR(hidden_dir->i_mode));
 
-       name = (char *)__get_free_page(GFP_KERNEL);
-       if (!name) {
-               err = -ENOMEM;
+       err = -ENOMEM;
+       name = __getname();
+       if (!name)
                goto out;
-       }
+       strcpy(name, WHPFX);
+       p = name+WHLEN;
 
-       for (i = 0; i < namelist->uds_size; i++) {
+       err = 0;
+       down(&hidden_dir->i_sem);
+       do_superio = permission(hidden_dir, MAY_WRITE | MAY_EXEC, NULL);
+       if (do_superio)
+               superio_store(&sio);
+       for (i = 0; !err && i < namelist->uds_size; i++) {
                list_for_each(pos, &namelist->uds_list[i]) {
                        cursor =
                            list_entry(pos, struct filldir_node, file_list);
@@ -63,41 +78,34 @@ int delete_whiteouts(struct dentry *dent
                        if (!cursor->whiteout)
                                continue;
 
-                       strcpy(name, WHPFX);
-                       strncpy(name + WHLEN, cursor->name, PAGE_SIZE - WHLEN);
-
+                       strcpy(p, cursor->name);
                        hidden_dentry =
                            LOOKUP_ONE_LEN(name, hidden_dir_dentry,
                                           cursor->namelen + WHLEN);
                        if (IS_ERR(hidden_dentry)) {
                                err = PTR_ERR(hidden_dentry);
-                               goto out;
-                       }
-                       if (!hidden_dentry->d_inode) {
-                               DPUT(hidden_dentry);
-                               continue;
+                               break;
                        }
-
-                       down(&hidden_dir_dentry->d_inode->i_sem);
-                       err =
-                           vfs_unlink(hidden_dir_dentry->d_inode,
-                                      hidden_dentry);
-                       up(&hidden_dir_dentry->d_inode->i_sem);
+                       if (hidden_dentry->d_inode)
+                               err = vfs_unlink(hidden_dir, hidden_dentry);
                        DPUT(hidden_dentry);
-
-                       if (err && !IS_COPYUP_ERR(err))
-                               goto out;
+                       if (err)
+                               break;
                }
        }
+       up(&hidden_dir->i_sem);
+       if (do_superio)
+               superio_revert(&sio);
+
+       __putname(name);
 
-      out:
        /* After all of the removals, we should copy the attributes once. */
        fist_copy_attr_times(dentry->d_inode, hidden_dir_dentry->d_inode);
-       dentry->d_inode->i_nlink = get_nlinks(dentry->d_inode);
+       //dentry->d_inode->i_nlink = get_nlinks(dentry->d_inode);
 
-       if (name)
-               free_page((unsigned long)name);
+      out:
+       unionfs_read_unlock(sb);
        print_exit_status(err);
        return err;
 }

--- unionfs-20060208-2346/lookup.c      9 Feb 2006 13:04:42 -0000       1.1
+++ unionfs-20060208-2346/lookup.c      10 Feb 2006 07:00:55 -0000
@@ -331,8 +336,8 @@ static int is_opaque_dir(struct dentry *
        struct dentry *hidden_dentry;
        struct dentry *wh_hidden_dentry;
        struct inode *hidden_inode;
-       uid_t saved_uid = current->fsuid;
-       gid_t saved_gid = current->fsgid;
+       struct superio sio;
+       int do_superio;
 
        print_entry_location();
 
@@ -341,13 +346,16 @@ static int is_opaque_dir(struct dentry *
 
        BUG_ON(!S_ISDIR(hidden_inode->i_mode));
 
-       current->fsuid = hidden_inode->i_uid;
-       current->fsgid = hidden_inode->i_gid;
+       down(&hidden_inode->i_sem);
+       do_superio = permission(hidden_inode, MAY_EXEC, NULL);
+       if (do_superio)
+               superio_store(&sio);
        wh_hidden_dentry = LOOKUP_ONE_LEN(UNIONFS_DIR_OPAQUE, hidden_dentry,
                                          sizeof(UNIONFS_DIR_OPAQUE) - 1);
-       current->fsuid = saved_uid;
-       current->fsgid = saved_gid;
+       if (do_superio)
+               superio_revert(&sio);
+       up(&hidden_inode->i_sem);
        if (IS_ERR(wh_hidden_dentry)) {
                err = PTR_ERR(wh_hidden_dentry);
                fist_dprint(1, "LOOKUP_ONE_LEN returned: %d\n", err);

--- unionfs-20060208-2346/subr.c        9 Feb 2006 13:04:42 -0000       1.1
+++ unionfs-20060208-2346/subr.c        10 Feb 2006 06:55:42 -0000      1.3
@@ -20,6 +20,7 @@
  */
 
 #include "unionfs.h"
+#include <linux/security.h>
 
 /* Pass an unionfs dentry and an index.  It will try to create a whiteout
  * for the filename in dentry, and will try in branch 'index'.  On error,
@@ -272,7 +273,56 @@ int unionfs_refresh_hidden_dentry(struct
        return err;
 }
 
+/* you should call superio_restore() asap */
+void superio_store(struct superio *sio)
+{
+       int err;
+       kernel_cap_t super, drop;
+       struct rlimit *rl;
+
+       drop = cap_t(CAP_TO_MASK(CAP_SYS_RESOURCE)
+                    | CAP_TO_MASK(CAP_SETPCAP)
+                    | CAP_TO_MASK(CAP_SETUID));
+
+       write_lock(&tasklist_lock); /* task_capability_lock is not exported */
+       err = security_capget(current, &sio->cap.effective,
+                             &sio->cap.inheritable, &sio->cap.permitted);
+       if (err) {
+               write_unlock(&tasklist_lock);
+               Warn("%.*s[%d]: security_capget failed\n",
+                    sizeof(current->comm), current->comm, current->pid);
+               return;
+       }
+       super = cap_combine(sio->cap.effective, CAP_FS_MASK);
+       super = cap_drop(super, drop);
+       security_capset_set(current, &super, &sio->cap.inheritable,
+                           &sio->cap.permitted);
+       write_unlock(&tasklist_lock);
+
+       rl = current->signal->rlim+RLIMIT_CORE;
+       task_lock(current->group_leader); //??
+       sio->rlim_core = rl->rlim_cur;
+       rl->rlim_cur = 0;
+       task_unlock(current->group_leader);
+
+       sio->fsuid = current->fsuid;
+       current->fsuid = 0;
+}
+
+void superio_revert(struct superio *sio)
+{
+       current->fsuid = sio->fsuid;
+       task_lock(current->group_leader); //??
+       current->signal->rlim[RLIMIT_CORE].rlim_cur = sio->rlim_core;
+       task_unlock(current->group_leader);
+
+       write_lock(&tasklist_lock); /* task_capability_lock is not exported */
+       security_capset_set(current, &sio->cap.effective,
+                           &sio->cap.inheritable, &sio->cap.permitted);
+       write_unlock(&tasklist_lock);
+}
+
 /*
  *
  * vim:shiftwidth=8

--- unionfs-20060208-2346/unionfs.h     9 Feb 2006 13:04:42 -0000       1.1
+++ unionfs-20060208-2346/unionfs.h     10 Feb 2006 06:55:42 -0000      1.3
@@ -380,6 +380,7 @@ extern int new_dentry_private_data(struc
 void free_dentry_private_data(struct unionfs_dentry_info *udi);
 void update_bstart(struct dentry *dentry);
 #define sbt(sb) ((sb)->s_type->name)
+#define UNIONFS_NAME "unionfs"
 
 /*
  * EXTERNALS:
@@ -816,7 +817,19 @@ static inline void unlock_dir(struct den
        DPUT(dir);
 }
 
+struct superio {
+       uid_t fsuid;
+       struct __user_cap_data_struct cap;
+       unsigned long rlim_core;
+};
+extern void superio_store(struct superio *sio);
+extern void superio_revert(struct superio *sio);
+
+#define Warn(format, args...) \
+       do {printk(KERN_WARNING UNIONFS_NAME " %s:%d: " format, \
+       __func__, __LINE__, ##args);} while(0)
+
 #endif                         /* __KERNEL__ */
 
 /*

--- unionfs-20060208-2346/unlink.c      9 Feb 2006 13:04:42 -0000       1.1
+++ unionfs-20060208-2346/unlink.c      9 Feb 2006 13:37:55 -0000       1.2
@@ -281,8 +281,8 @@ static int unionfs_rmdir_all(struct inod
 
                hidden_dir_dentry = lock_parent(hidden_dentry);
                if (S_ISDIR(hidden_dentry->d_inode->i_mode)) {
-                       delete_whiteouts(dentry, bindex, namelist);
-                       if (!(err = is_robranch_super(dentry->d_sb, bindex))) {
+                       err = delete_whiteouts(dentry, bindex, namelist);
+                       if (!err && !(err = is_robranch_super(dentry->d_sb, 
bindex))) {
                                err =
                                    vfs_rmdir(hidden_dir_dentry->d_inode,
                                              hidden_dentry);
_______________________________________________
unionfs mailing list
[email protected]
http://www.fsl.cs.sunysb.edu/mailman/listinfo/unionfs

Reply via email to