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