|
Line 0
Link Here
|
|
|
1 |
/* |
| 2 |
* subfs.c |
| 3 |
* |
| 4 |
* Copyright (C) 2003-2004 Eugene S. Weiss <eweiss@sbclobal.net> |
| 5 |
* |
| 6 |
* Distributed under the terms of the GNU General Public License version 2 |
| 7 |
* or above. |
| 8 |
*/ |
| 9 |
|
| 10 |
#include <linux/init.h> |
| 11 |
#include <linux/module.h> |
| 12 |
#include <linux/kernel.h> |
| 13 |
#include <linux/moduleparam.h> |
| 14 |
#include <linux/pagemap.h> |
| 15 |
#include <linux/fs.h> |
| 16 |
#include <asm/atomic.h> |
| 17 |
#include <asm/uaccess.h> |
| 18 |
#include <linux/list.h> |
| 19 |
#include <linux/mount.h> |
| 20 |
#include <linux/mnt_namespace.h> |
| 21 |
#include <linux/namei.h> |
| 22 |
#include <linux/dcache.h> |
| 23 |
#include <linux/sysfs.h> |
| 24 |
#include <asm/semaphore.h> |
| 25 |
#include <asm/signal.h> |
| 26 |
#include <linux/signal.h> |
| 27 |
#include <linux/sched.h> |
| 28 |
#include <linux/version.h> |
| 29 |
#include <linux/rcupdate.h> |
| 30 |
|
| 31 |
#include "subfs.h" |
| 32 |
|
| 33 |
MODULE_LICENSE("GPL"); |
| 34 |
MODULE_AUTHOR("Eugene S. Weiss"); |
| 35 |
|
| 36 |
|
| 37 |
/* get_subfs_vfsmount tries to find the vfsmount structure assigned to |
| 38 |
* the pending mount. It looks for a vfsmount structure matching the |
| 39 |
* superblock pointer sent. This is not ideal, but I don't know of |
| 40 |
* another way to find the structure without altering the code in the |
| 41 |
* mount routines. |
| 42 |
*/ |
| 43 |
static struct vfsmount *get_subfs_vfsmount(struct super_block *sb) |
| 44 |
{ |
| 45 |
struct fs_struct *init_fs; |
| 46 |
struct list_head *entry, *head, *lh; |
| 47 |
struct vfsmount *mnt; |
| 48 |
|
| 49 |
/* Get the head of the global mount list from the init process. */ |
| 50 |
/* Is there a better way? */ |
| 51 |
init_fs = init_task.fs; |
| 52 |
head = &init_fs->root.mnt->mnt_list; |
| 53 |
|
| 54 |
/* Go through the list and look for a superblock pointer match. */ |
| 55 |
list_for_each_safe(entry, lh, head) { |
| 56 |
mnt = list_entry(entry, struct vfsmount, mnt_list); |
| 57 |
if (mnt->mnt_sb == sb) |
| 58 |
return mnt; |
| 59 |
} |
| 60 |
ERR("subfs: Unable to find mount structure for superblock.\n"); |
| 61 |
return NULL; /* If there was no match */ |
| 62 |
} |
| 63 |
|
| 64 |
|
| 65 |
/* Same as set_fs_pwd from namespace.c. There's a problem with the |
| 66 |
* symbol. When it is fixed, discard this. |
| 67 |
* Replace the fs->{pwdmnt,pwd} with {mnt,dentry}. Put the old values. |
| 68 |
* It can block. Requires the big lock held. |
| 69 |
*/ |
| 70 |
static void subfs_set_fs_pwd(struct fs_struct *fs, struct vfsmount *mnt, |
| 71 |
struct dentry *dentry) |
| 72 |
{ |
| 73 |
struct dentry *old_pwd; |
| 74 |
struct vfsmount *old_pwdmnt; |
| 75 |
|
| 76 |
write_lock(&fs->lock); |
| 77 |
old_pwd = fs->pwd.dentry; |
| 78 |
old_pwdmnt = fs->pwd.mnt; |
| 79 |
fs->pwd.mnt = mntget(mnt); |
| 80 |
fs->pwd.dentry = dget(dentry); |
| 81 |
write_unlock(&fs->lock); |
| 82 |
|
| 83 |
if (old_pwd) { |
| 84 |
dput(old_pwd); |
| 85 |
mntput(old_pwdmnt); |
| 86 |
} |
| 87 |
} |
| 88 |
|
| 89 |
|
| 90 |
/* Quickly sends an ignored signal to the signal handling system. This |
| 91 |
* causes the system to restart the system call when it receives the |
| 92 |
* -ERESTARTSYS error. |
| 93 |
*/ |
| 94 |
static void subfs_send_signal(void) |
| 95 |
{ |
| 96 |
struct task_struct *task = current; |
| 97 |
int signal = SIGCONT; |
| 98 |
|
| 99 |
rcu_read_lock(); |
| 100 |
spin_lock_irq(&task->sighand->siglock); |
| 101 |
sigaddset(&task->pending.signal, signal); |
| 102 |
spin_unlock_irq(&task->sighand->siglock); |
| 103 |
rcu_read_unlock(); |
| 104 |
set_tsk_thread_flag(task, TIF_SIGPENDING); |
| 105 |
return; |
| 106 |
} |
| 107 |
|
| 108 |
|
| 109 |
/* If the option "procuid" is chosen when subfs is mounted, the uid |
| 110 |
* and gid numbers for the current process will bea dded to the mount |
| 111 |
* option line. Hence, non-unix filesystems will be mounted with |
| 112 |
* that ownership. |
| 113 |
*/ |
| 114 |
static void add_procuid(struct subfs_mount *sfs_mnt) |
| 115 |
{ |
| 116 |
struct task_struct *task = current; |
| 117 |
char *o = kmalloc(strlen(sfs_mnt->options) + 1 + 32 + 1, GFP_KERNEL); |
| 118 |
|
| 119 |
if (sfs_mnt->options[0] == '\0') |
| 120 |
sprintf(o, "uid=%d,gid=%d", task->uid, task->gid); |
| 121 |
else |
| 122 |
sprintf(o, "%s,uid=%d,gid=%d", sfs_mnt->options, task->uid, task->gid); |
| 123 |
|
| 124 |
kfree(sfs_mnt->options); |
| 125 |
sfs_mnt->options = o; |
| 126 |
} |
| 127 |
|
| 128 |
|
| 129 |
/* This routine calls the /sbin/submountd program to mount the |
| 130 |
* appropriate filesystem on top of the subfs mount. Returns |
| 131 |
* 0 if the userspace program exited normally, or an error if |
| 132 |
* it did not. |
| 133 |
*/ |
| 134 |
static int mount_real_fs(struct subfs_mount *sfs_mnt) |
| 135 |
{ |
| 136 |
char *argv[7] = |
| 137 |
{ sfs_mnt->helper_prog, NULL, NULL, NULL, NULL, NULL, NULL }; |
| 138 |
char *envp[2] = { "HOME=/", NULL }; |
| 139 |
char *path_buf; |
| 140 |
int result, len = 0; |
| 141 |
struct path p; |
| 142 |
|
| 143 |
argv[1] = sfs_mnt->device; |
| 144 |
path_buf = (char *) __get_free_page(GFP_KERNEL); |
| 145 |
if (!path_buf) |
| 146 |
return -ENOMEM; |
| 147 |
p.mnt = sfs_mnt->mount; |
| 148 |
p.dentry = p.mnt->mnt_root; |
| 149 |
argv[2] = d_path(&p, path_buf, PAGE_SIZE); |
| 150 |
argv[3] = sfs_mnt->req_fs; |
| 151 |
if (!(argv[4] = kmalloc(17, GFP_KERNEL))) { |
| 152 |
free_page((unsigned long) path_buf); |
| 153 |
return -ENOMEM; /* 64 bits on some platforms */ |
| 154 |
} |
| 155 |
sprintf(argv[4], "%lx", sfs_mnt->flags); |
| 156 |
len = strlen(sfs_mnt->options); |
| 157 |
if (sfs_mnt->procuid) |
| 158 |
add_procuid(sfs_mnt); |
| 159 |
argv[5] = sfs_mnt->options; |
| 160 |
result = call_usermodehelper(sfs_mnt->helper_prog, argv, envp, 1); |
| 161 |
free_page((unsigned long) path_buf); |
| 162 |
kfree(argv[4]); |
| 163 |
if (sfs_mnt->procuid) |
| 164 |
sfs_mnt->options[len] = '\0'; |
| 165 |
return result; |
| 166 |
} |
| 167 |
|
| 168 |
|
| 169 |
/* This routine returns a pointer to the filesystem mounted on top |
| 170 |
* of the subfs mountpoint, or an error pointer if it was unable to. |
| 171 |
*/ |
| 172 |
static struct vfsmount *get_child_mount(struct subfs_mount *sfs_mnt) |
| 173 |
{ |
| 174 |
struct vfsmount *child; |
| 175 |
int result; |
| 176 |
/* First time: find the vfsmount structure that matches sfs_mnt. */ |
| 177 |
if (!sfs_mnt->mount) { |
| 178 |
if(!(sfs_mnt->mount = get_subfs_vfsmount(sfs_mnt->sb))) |
| 179 |
return ERR_PTR(-ENOMEDIUM); |
| 180 |
sfs_mnt->flags = sfs_mnt->sb->s_flags; |
| 181 |
if (sfs_mnt->mount->mnt_flags & MNT_NOSUID) |
| 182 |
sfs_mnt->flags |= MS_NOSUID; |
| 183 |
if (sfs_mnt->mount->mnt_flags & MNT_NODEV) |
| 184 |
sfs_mnt->flags |= MS_NODEV; |
| 185 |
if (sfs_mnt->mount->mnt_flags & MNT_NOEXEC) |
| 186 |
sfs_mnt->flags |= MS_NOEXEC; |
| 187 |
} |
| 188 |
/* Check to see if a child mount does not already exist. */ |
| 189 |
if (&sfs_mnt->mount->mnt_mounts == sfs_mnt->mount->mnt_mounts.next) { |
| 190 |
result = mount_real_fs(sfs_mnt); |
| 191 |
if (result) { |
| 192 |
ERR("subfs: "SUBMOUNTD_PATH" unsuccessful attempt to mount media (%d)\n", |
| 193 |
result); |
| 194 |
/* Workaround for call_usermodehelper return value bug. */ |
| 195 |
if(result < 0) |
| 196 |
return ERR_PTR(result); |
| 197 |
return ERR_PTR(-ENOMEDIUM); |
| 198 |
} |
| 199 |
if (&sfs_mnt->mount->mnt_mounts |
| 200 |
== sfs_mnt->mount->mnt_mounts.next) { |
| 201 |
ERR("subfs: submountd mount failure.\n"); |
| 202 |
return ERR_PTR(-ENOMEDIUM); |
| 203 |
} |
| 204 |
} |
| 205 |
child = list_entry(sfs_mnt->mount->mnt_mounts.next, |
| 206 |
struct vfsmount, mnt_child); |
| 207 |
return child; |
| 208 |
} |
| 209 |
|
| 210 |
|
| 211 |
/* Implements the lookup method for subfs. Tries to get the child |
| 212 |
* mount. If it succeeds, it emits a signal and returns |
| 213 |
* -ERESTARSYS. If it receives an error, it passes it on to the |
| 214 |
* system. It raises the semaphore in the directory inode before mounting |
| 215 |
* because the mount routine also calls lookup, and hence a function is |
| 216 |
* calling itself from within semaphore protected code. Only the semaphore |
| 217 |
* on the subfs pseudo-directory is effected, so this isn't deadly. |
| 218 |
*/ |
| 219 |
static struct dentry *subfs_lookup(struct inode *dir, |
| 220 |
struct dentry *dentry, struct nameidata *nd) |
| 221 |
{ |
| 222 |
struct subfs_mount *sfs_mnt = dir->i_sb->s_fs_info; |
| 223 |
struct vfsmount *child; |
| 224 |
|
| 225 |
/* This is ugly, but prevents a lockup during mount. */ |
| 226 |
mutex_unlock(&dir->i_mutex); |
| 227 |
if (down_interruptible(&sfs_mnt->sem)) { |
| 228 |
mutex_lock(&dir->i_mutex); /* put the dir sem back down if interrupted */ |
| 229 |
return ERR_PTR(-ERESTARTSYS); |
| 230 |
} |
| 231 |
child = get_child_mount(sfs_mnt); |
| 232 |
up(&sfs_mnt->sem); |
| 233 |
mutex_lock(&dir->i_mutex); |
| 234 |
if (IS_ERR(child)) |
| 235 |
return (void *) child; |
| 236 |
subfs_send_signal(); |
| 237 |
if (sfs_mnt->mount == current->fs->pwd.mnt) |
| 238 |
subfs_set_fs_pwd(current->fs, child, child->mnt_root); |
| 239 |
return ERR_PTR(-ERESTARTSYS); |
| 240 |
} |
| 241 |
|
| 242 |
|
| 243 |
/* Implements the open method for subfs. Tries to get the child mount |
| 244 |
* for the subfs mountpoint which is being opened. Returns -ERESTARTSYS |
| 245 |
* and emits an ignored signal to the calling process if it succeeds, |
| 246 |
* or passes the error message received if it fails. |
| 247 |
*/ |
| 248 |
static int subfs_open(struct inode *inode, struct file *filp) |
| 249 |
{ |
| 250 |
struct subfs_mount *sfs_mnt = filp->f_dentry->d_sb->s_fs_info; |
| 251 |
struct vfsmount *child; |
| 252 |
if (down_interruptible(&sfs_mnt->sem)) |
| 253 |
return -ERESTARTSYS; |
| 254 |
child = get_child_mount(sfs_mnt); |
| 255 |
up(&sfs_mnt->sem); |
| 256 |
if (IS_ERR(child)) |
| 257 |
return PTR_ERR(child); |
| 258 |
subfs_send_signal(); |
| 259 |
if (sfs_mnt->mount == current->fs->pwd.mnt) |
| 260 |
subfs_set_fs_pwd(current->fs, child, child->mnt_root); |
| 261 |
return -ERESTARTSYS; |
| 262 |
} |
| 263 |
|
| 264 |
|
| 265 |
/* Implements the statfs method so df and such will work on the mountpoint. |
| 266 |
*/ |
| 267 |
static int subfs_statfs(struct dentry *dentry, struct kstatfs *buf) |
| 268 |
{ |
| 269 |
struct subfs_mount *sfs_mnt = dentry->d_sb->s_fs_info; |
| 270 |
struct vfsmount *child; |
| 271 |
if (down_interruptible(&sfs_mnt->sem)) |
| 272 |
return -ERESTARTSYS; |
| 273 |
child = get_child_mount(sfs_mnt); |
| 274 |
up(&sfs_mnt->sem); |
| 275 |
if (IS_ERR(child)) |
| 276 |
return PTR_ERR(child); |
| 277 |
subfs_send_signal(); |
| 278 |
return -ERESTARTSYS; |
| 279 |
} |
| 280 |
|
| 281 |
|
| 282 |
/* Creates the inodes for subfs superblocks. |
| 283 |
*/ |
| 284 |
static struct inode *subfs_make_inode(struct super_block *sb, int mode) |
| 285 |
{ |
| 286 |
struct inode *ret = new_inode(sb); |
| 287 |
|
| 288 |
if (ret) { |
| 289 |
ret->i_mode = mode; |
| 290 |
ret->i_uid = ret->i_gid = 0; |
| 291 |
ret->i_blocks = 0; |
| 292 |
ret->i_atime = ret->i_mtime = ret->i_ctime = CURRENT_TIME; |
| 293 |
ret->i_fop = &subfs_file_ops; |
| 294 |
} |
| 295 |
return ret; |
| 296 |
} |
| 297 |
|
| 298 |
|
| 299 |
/* Fills the fields for the superblock created when subfs is mounted. |
| 300 |
*/ |
| 301 |
static int subfs_fill_super(struct super_block *sb, void *data, int silent) |
| 302 |
{ |
| 303 |
struct inode *root; |
| 304 |
struct dentry *root_dentry; |
| 305 |
|
| 306 |
sb->s_blocksize = PAGE_CACHE_SIZE; |
| 307 |
sb->s_blocksize_bits = PAGE_CACHE_SHIFT; |
| 308 |
sb->s_magic = SUBFS_MAGIC; |
| 309 |
sb->s_op = &subfs_s_ops; |
| 310 |
root = subfs_make_inode(sb, S_IFDIR|ROOT_MODE); |
| 311 |
if (!root) |
| 312 |
goto out; |
| 313 |
root->i_op = &subfs_dir_inode_operations; |
| 314 |
root_dentry = d_alloc_root(root); |
| 315 |
if (!root_dentry) |
| 316 |
goto out_iput; |
| 317 |
sb->s_root = root_dentry; |
| 318 |
return 0; |
| 319 |
out_iput: |
| 320 |
iput(root); |
| 321 |
out: |
| 322 |
return -ENOMEM; |
| 323 |
} |
| 324 |
|
| 325 |
|
| 326 |
/* Parse the options string and remove submount specific options |
| 327 |
* and store the appropriate data. |
| 328 |
*/ |
| 329 |
static int proc_opts(struct subfs_mount *sfs_mnt, void *data) |
| 330 |
{ |
| 331 |
char *opts = data, *opt, *ptr, *fs = NULL, *prog; |
| 332 |
int len; |
| 333 |
|
| 334 |
if (!opts) { |
| 335 |
if (!(data = opts = kmalloc(PAGE_SIZE, GFP_KERNEL))) |
| 336 |
return -EINVAL; |
| 337 |
strcat(opts, "fs=auto"); |
| 338 |
} |
| 339 |
len = strnlen(opts, PAGE_SIZE - 1) + 1; |
| 340 |
if (strstr(opts, "procuid")) |
| 341 |
sfs_mnt->procuid = 1; |
| 342 |
if (!(sfs_mnt->options = kmalloc(len, GFP_KERNEL))) |
| 343 |
return -ENOMEM; |
| 344 |
sfs_mnt->options[0] = '\0'; |
| 345 |
while ((opt = strsep(&opts, ","))) { |
| 346 |
if ((ptr = strstr(opt, "program="))) { |
| 347 |
if (!(prog = kmalloc((strlen(ptr) - 7), GFP_KERNEL))) |
| 348 |
return -ENOMEM; |
| 349 |
strcpy(prog, (ptr + 8)); |
| 350 |
kfree(sfs_mnt->helper_prog); |
| 351 |
sfs_mnt->helper_prog = prog; |
| 352 |
} |
| 353 |
else if ((ptr = strstr(opt, "fs="))) { |
| 354 |
if (!(fs = kmalloc((strlen(ptr) - 2), GFP_KERNEL))) |
| 355 |
return -ENOMEM; |
| 356 |
strcpy(fs, (ptr + 3)); |
| 357 |
sfs_mnt->req_fs = fs; |
| 358 |
} |
| 359 |
else if ((ptr = strstr(opt, "procuid"))) { |
| 360 |
} else { |
| 361 |
strncat(sfs_mnt->options, opt, 32); |
| 362 |
strcat(sfs_mnt->options, ","); |
| 363 |
} |
| 364 |
} |
| 365 |
if((len = strlen(sfs_mnt->options)) && (sfs_mnt->options[len-1] == ',')) |
| 366 |
sfs_mnt->options[len-1] = '\0'; |
| 367 |
if ( !sfs_mnt->req_fs ){ |
| 368 |
if (!(sfs_mnt->req_fs = kmalloc(5, GFP_KERNEL))) |
| 369 |
return -ENOMEM; /* 64 bits on some platforms */ |
| 370 |
strcpy(sfs_mnt->req_fs, "auto" ); |
| 371 |
} |
| 372 |
return 0; |
| 373 |
} |
| 374 |
|
| 375 |
|
| 376 |
|
| 377 |
/* subfs_get_super is the subfs implementation of the get_sb method on |
| 378 |
* the file_system_type structure. It should only be called in the |
| 379 |
* case of a mount. It creates a new subfs_mount structure, fills |
| 380 |
* the fields of the structure, except for the mount structure, and then |
| 381 |
* calls a generic get_sb function. The superblock pointer is stored on |
| 382 |
* the subfs_mount structure, and returned to the calling function. The |
| 383 |
* subfs_mount structure is pointed to by the s_fs_info field of the |
| 384 |
* superblock structure. |
| 385 |
*/ |
| 386 |
static int subfs_get_super(struct file_system_type *fst, |
| 387 |
int flags, const char *devname, void *data, |
| 388 |
struct vfsmount *mnt) |
| 389 |
{ |
| 390 |
char *device; |
| 391 |
struct subfs_mount *newmount; |
| 392 |
int ret; |
| 393 |
|
| 394 |
if (!(newmount = kmalloc(sizeof(struct subfs_mount), GFP_KERNEL))) { |
| 395 |
ret = -ENOMEM; |
| 396 |
goto err; |
| 397 |
} |
| 398 |
newmount->req_fs = NULL; |
| 399 |
newmount->sb = NULL; |
| 400 |
newmount->mount = NULL; |
| 401 |
newmount->procuid = 0; |
| 402 |
sema_init(&newmount->sem, 1); |
| 403 |
if (!(device = kmalloc((strlen(devname) + 1), GFP_KERNEL))) { |
| 404 |
ret = -ENOMEM; |
| 405 |
goto err; |
| 406 |
} |
| 407 |
strcpy(device, devname); |
| 408 |
newmount->device = device; |
| 409 |
if (!(newmount->helper_prog = |
| 410 |
kmalloc(sizeof(SUBMOUNTD_PATH), GFP_KERNEL))) { |
| 411 |
ret = -ENOMEM; |
| 412 |
goto err; |
| 413 |
} |
| 414 |
strcpy(newmount->helper_prog, SUBMOUNTD_PATH); |
| 415 |
if ((ret = proc_opts(newmount, data))) |
| 416 |
goto err; |
| 417 |
ret = get_sb_nodev(fst, flags, data, subfs_fill_super, mnt); |
| 418 |
if (ret) |
| 419 |
goto err; |
| 420 |
newmount->sb = mnt->mnt_sb; |
| 421 |
newmount->sb->s_fs_info = newmount; |
| 422 |
return ret; |
| 423 |
|
| 424 |
err: |
| 425 |
return ret; |
| 426 |
} |
| 427 |
|
| 428 |
|
| 429 |
/* subfs_kill_super is the subfs implementation of the kill_sb method. |
| 430 |
* It should be called only on umount. It cleans up the appropriate |
| 431 |
* subfs_mount structure and then calls a generic function to actually |
| 432 |
* clean up the superblock structure. |
| 433 |
*/ |
| 434 |
static void subfs_kill_super(struct super_block *sb) |
| 435 |
{ |
| 436 |
struct subfs_mount *sfs_mnt = sb->s_fs_info; |
| 437 |
|
| 438 |
if(sfs_mnt) { |
| 439 |
if (sfs_mnt->device) |
| 440 |
kfree(sfs_mnt->device); |
| 441 |
if (sfs_mnt->options) |
| 442 |
kfree(sfs_mnt->options); |
| 443 |
if (sfs_mnt->req_fs) |
| 444 |
kfree(sfs_mnt->req_fs); |
| 445 |
if (sfs_mnt->helper_prog) |
| 446 |
kfree(sfs_mnt->helper_prog); |
| 447 |
kfree(sfs_mnt); |
| 448 |
sb->s_fs_info = NULL; |
| 449 |
} |
| 450 |
kill_litter_super(sb); |
| 451 |
return; |
| 452 |
} |
| 453 |
|
| 454 |
|
| 455 |
static int __init subfs_init(void) |
| 456 |
{ |
| 457 |
printk(KERN_INFO "subfs %s\n", SUBFS_VER); |
| 458 |
return register_filesystem(&subfs_type); |
| 459 |
} |
| 460 |
|
| 461 |
static void __exit subfs_exit(void) |
| 462 |
{ |
| 463 |
printk(KERN_INFO "subfs exiting.\n"); |
| 464 |
unregister_filesystem(&subfs_type); |
| 465 |
} |
| 466 |
|
| 467 |
module_init(subfs_init); |
| 468 |
module_exit(subfs_exit); |