void unionfs_reinterpose(struct dentry *dentry) { struct dentry *hidden_dentry; struct inode *inode; int bindex, bstart, bend; print_entry_location(); verify_locked(dentry); fist_print_dentry("IN: unionfs_reinterpose: ", dentry); /* This is pre-allocated inode */ inode = dentry->d_inode; bstart = dbstart(dentry); bend = dbend(dentry); for (bindex = bstart; bindex <= bend; bindex++) { hidden_dentry = dtohd_index(dentry, bindex); if (!hidden_dentry) continue; if (!hidden_dentry->d_inode) continue; if (itohi_index(inode, bindex)) continue; set_itohi_index(inode, bindex, IGRAB(hidden_dentry->d_inode)); } ibstart(inode) = dbstart(dentry); ibend(inode) = dbend(dentry); fist_print_dentry("OUT: unionfs_reinterpose: ", dentry); fist_print_inode("OUT: unionfs_reinterpose: ", inode); print_exit_location(); }
static int unionfs_permission(struct inode *inode, int mask, struct nameidata *nd) { struct inode *hidden_inode = NULL; int err = 0; int bindex, bstart, bend; const int is_file = !S_ISDIR(inode->i_mode); const int write_mask = (mask & MAY_WRITE) && !(mask & MAY_READ); print_entry_location(); bstart = ibstart(inode); bend = ibend(inode); fist_print_inode("IN unionfs_permission", inode); for (bindex = bstart; bindex <= bend; bindex++) { hidden_inode = itohi_index(inode, bindex); if (!hidden_inode) continue; /* check the condition for D-F-D underlying files/directories, * we dont have to check for files, if we are checking for * directories. */ if (!is_file && !S_ISDIR(hidden_inode->i_mode)) continue; /* We use our own special version of permission, such that * only the first branch returns -EROFS. */ err = inode_permission(hidden_inode, mask, nd, bindex); /* The permissions are an intersection of the overall directory * permissions, so we fail if one fails. */ if (err) goto out; /* only the leftmost file matters. */ if (is_file || write_mask) { if (is_file && write_mask) { err = get_write_access(hidden_inode); if (!err) put_write_access(hidden_inode); } break; } } out: print_exit_status(err); return err; }
int copyup_named_dentry(struct inode *dir, struct dentry *dentry, int bstart, int new_bindex, char *name, int namelen, struct file **copyup_file, int len) { struct dentry *new_hidden_dentry; struct dentry *old_hidden_dentry = NULL; struct super_block *sb; struct file *input_file = NULL; struct file *output_file = NULL; ssize_t read_bytes, write_bytes; mm_segment_t old_fs; int err = 0; char *buf; int old_bindex; int got_branch_input = -1; int got_branch_output = -1; int old_bstart; int old_bend; int size = len; struct dentry *new_hidden_parent_dentry; mm_segment_t oldfs; char *symbuf = NULL; uid_t saved_uid = current->fsuid; gid_t saved_gid = current->fsgid; print_entry_location(); verify_locked(dentry); fist_print_dentry("IN: copyup_named_dentry", dentry); old_bindex = bstart; old_bstart = dbstart(dentry); old_bend = dbend(dentry); ASSERT(new_bindex >= 0); ASSERT(new_bindex < old_bindex); PASSERT(dir); PASSERT(dentry); sb = dir->i_sb; if ((err = is_robranch_super(sb, new_bindex))) goto out; /* Create the directory structure above this dentry. */ new_hidden_dentry = create_parents_named(dir, dentry, name, new_bindex); PASSERT(new_hidden_dentry); if (IS_ERR(new_hidden_dentry)) { err = PTR_ERR(new_hidden_dentry); goto out; } fist_print_generic_dentry("Copyup Object", new_hidden_dentry); /* Now we actually create the object. */ old_hidden_dentry = dtohd_index(dentry, old_bindex); PASSERT(old_hidden_dentry); PASSERT(old_hidden_dentry->d_inode); DGET(old_hidden_dentry); /* For symlinks, we must read the link before we lock the directory. */ if (S_ISLNK(old_hidden_dentry->d_inode->i_mode)) { PASSERT(old_hidden_dentry->d_inode->i_op); PASSERT(old_hidden_dentry->d_inode->i_op->readlink); symbuf = KMALLOC(PATH_MAX, GFP_UNIONFS); if (!symbuf) { err = -ENOMEM; goto copyup_readlink_err; } oldfs = get_fs(); set_fs(KERNEL_DS); err = old_hidden_dentry->d_inode->i_op-> readlink(old_hidden_dentry, symbuf, PATH_MAX); set_fs(oldfs); if (err < 0) goto copyup_readlink_err; symbuf[err] = '\0'; } /* Now we lock the parent, and create the object in the new branch. */ new_hidden_parent_dentry = lock_parent(new_hidden_dentry); current->fsuid = new_hidden_parent_dentry->d_inode->i_uid; current->fsgid = new_hidden_parent_dentry->d_inode->i_gid; if (S_ISDIR(old_hidden_dentry->d_inode->i_mode)) { err = vfs_mkdir(new_hidden_parent_dentry->d_inode, new_hidden_dentry, S_IRWXU); } else if (S_ISLNK(old_hidden_dentry->d_inode->i_mode)) { #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) err = vfs_symlink(new_hidden_parent_dentry->d_inode, new_hidden_dentry, symbuf); #else err = vfs_symlink(new_hidden_parent_dentry->d_inode, new_hidden_dentry, symbuf, S_IRWXU); #endif } else if (S_ISBLK(old_hidden_dentry->d_inode->i_mode) || S_ISCHR(old_hidden_dentry->d_inode->i_mode) || S_ISFIFO(old_hidden_dentry->d_inode->i_mode) || S_ISSOCK(old_hidden_dentry->d_inode->i_mode)) { #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) err = vfs_mknod(new_hidden_parent_dentry->d_inode, new_hidden_dentry, old_hidden_dentry->d_inode->i_mode, kdev_t_to_nr(old_hidden_dentry->d_inode-> i_rdev)); #else err = vfs_mknod(new_hidden_parent_dentry->d_inode, new_hidden_dentry, old_hidden_dentry->d_inode->i_mode, old_hidden_dentry->d_inode->i_rdev); #endif } else if (S_ISREG(old_hidden_dentry->d_inode->i_mode)) { #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) err = vfs_create(new_hidden_parent_dentry->d_inode, new_hidden_dentry, S_IRWXU); #else err = vfs_create(new_hidden_parent_dentry->d_inode, new_hidden_dentry, S_IRWXU, NULL); #endif } else { char diemsg[100]; snprintf(diemsg, sizeof(diemsg), "Unknown inode type %d\n", old_hidden_dentry->d_inode->i_mode); FISTBUG(diemsg); } current->fsuid = saved_uid; current->fsgid = saved_gid; unlock_dir(new_hidden_parent_dentry); copyup_readlink_err: KFREE(symbuf); if (err) { /* get rid of the hidden dentry and all its traces */ DPUT(new_hidden_dentry); set_dtohd_index(dentry, new_bindex, NULL); set_dbstart(dentry, old_bstart); set_dbend(dentry, old_bend); goto out; } /* We actually copyup the file here. */ if (S_ISREG(old_hidden_dentry->d_inode->i_mode)) { mntget(stohiddenmnt_index(sb, old_bindex)); branchget(sb, old_bindex); got_branch_input = old_bindex; input_file = DENTRY_OPEN(old_hidden_dentry, stohiddenmnt_index(sb, old_bindex), O_RDONLY); if (IS_ERR(input_file)) { err = PTR_ERR(input_file); goto out; } if (!input_file->f_op || !input_file->f_op->read) { err = -EINVAL; goto out; } /* copy the new file */ DGET(new_hidden_dentry); mntget(stohiddenmnt_index(sb, new_bindex)); branchget(sb, new_bindex); got_branch_output = new_bindex; output_file = DENTRY_OPEN(new_hidden_dentry, stohiddenmnt_index(sb, new_bindex), O_WRONLY); if (IS_ERR(output_file)) { err = PTR_ERR(output_file); goto out; } if (!output_file->f_op || !output_file->f_op->write) { err = -EINVAL; goto out; } /* allocating a buffer */ buf = (char *)KMALLOC(PAGE_SIZE, GFP_UNIONFS); if (!buf) { err = -ENOMEM; goto out; } /* now read PAGE_SIZE bytes from offset 0 in a loop */ old_fs = get_fs(); input_file->f_pos = 0; output_file->f_pos = 0; set_fs(KERNEL_DS); do { if (len >= PAGE_SIZE) size = PAGE_SIZE; else if ((len < PAGE_SIZE) && (len > 0)) size = len; len -= PAGE_SIZE; read_bytes = input_file->f_op->read(input_file, buf, size, &input_file->f_pos); if (read_bytes <= 0) { err = read_bytes; break; } write_bytes = output_file->f_op->write(output_file, buf, read_bytes, &output_file->f_pos); if (write_bytes < 0 || (write_bytes < read_bytes)) { err = -EIO; break; } } while ((read_bytes > 0) && (len > 0)); set_fs(old_fs); KFREE(buf); } /* Set permissions. */ if ((err = copyup_permissions(sb, old_hidden_dentry, new_hidden_dentry))) goto out; /* Selinux uses extended attributes for permissions. */ #if defined(UNIONFS_XATTR) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20)) if ((err = copyup_xattrs(old_hidden_dentry, new_hidden_dentry))) goto out; #endif /* do not allow files getting deleted to be reinterposed */ if (!d_deleted(dentry)) unionfs_reinterpose(dentry); out: if (input_file && !IS_ERR(input_file)) { fput(input_file); } else { /* since input file was not opened, we need to explicitly * dput the old_hidden_dentry */ DPUT(old_hidden_dentry); } /* in any case, we have to branchput */ if (got_branch_input >= 0) branchput(sb, got_branch_input); if (output_file) { if (copyup_file && !err) { *copyup_file = output_file; } else { fput(output_file); branchput(sb, got_branch_output); } } fist_print_dentry("OUT: copyup_dentry", dentry); fist_print_inode("OUT: copyup_dentry", dentry->d_inode); print_exit_status(err); return err; }
STATIC struct dentry * base0fs_lookup(inode_t *dir, struct dentry *dentry, struct nameidata* nd_unused_in_this_fxn /* XXX: fix code if ever used */ ) { int err = 0; struct dentry *lower_dir_dentry; struct dentry *lower_dentry = NULL; struct vfsmount *lower_mount; const char *name; vnode_t *this_vnode; struct dentry *this_dir; unsigned int namelen; print_entry_location(); lower_dir_dentry = base0fs_lower_dentry(dentry->d_parent); /* CPW: Moved below print_entry_location */ name = dentry->d_name.name; namelen = dentry->d_name.len; fist_checkinode(dir, "base0fs_lookup"); this_vnode = dir; this_dir = lower_dir_dentry; fist_print_dentry("base0fs_lookup IN", dentry); fist_print_dentry("base0fs_lookup: dentry->d_parent IN", dentry->d_parent); fist_print_dentry("base0fs_lookup: lower_dir_dentry IN", lower_dir_dentry); fist_print_inode("base0fs_lookup: dir IN", dir); if (lower_dir_dentry->d_inode) fist_print_inode("base0fs_lookup: lower_dir_dentry->d_inode", lower_dir_dentry->d_inode); /* must initialize dentry operations */ dentry->d_op = &base0fs_dops; ; /* increase refcount of base dentry (lookup_one[_len] will decrement) */ // THIS IS RIGHT! (don't "fix" it) // NO THIS IS WRONG IN 2.3.99-pre6. lookup_one[_len] will NOT decrement // dget(lower_dir_dentry); lock_inode(lower_dir_dentry->d_inode); /* will allocate a new lower dentry if needed */ lower_dentry = lookup_one_len(name, lower_dir_dentry, namelen); unlock_inode(lower_dir_dentry->d_inode); if (IS_ERR(lower_dentry)) { /* * this produces an unusual dentry: one that has neither an * inode, nor a private structure attached to it. All cleanup * methods (d_delete, d_release, etc) must be prepared to deal * with such dentries. Ion 09/29/2001 */ err = PTR_ERR(lower_dentry); goto out; } lower_mount = mntget(DENTRY_TO_LVFSMNT(dentry->d_parent)); ; if (lower_dentry->d_inode && lower_dentry->d_inode->i_sb) { struct super_block *lower_sb = lower_dentry->d_inode->i_sb; if (lower_sb->s_dev == sProcDev || lower_sb->s_dev == sDevDev) { d_drop(dentry); /* Don't leak our dentry. */ return lower_dentry; } } /* update parent directory's atime */ fist_copy_attr_atime(dir, lower_dir_dentry->d_inode); /* link the upper and lower dentries */ DENTRY_TO_PRIVATE_SM(dentry) = (struct base0fs_dentry_info *) KMALLOC(sizeof(struct base0fs_dentry_info), GFP_KERNEL); if (!DENTRY_TO_PRIVATE(dentry)) { err = -ENOMEM; goto out_dput; } DENTRY_TO_PRIVATE(dentry)->wdi_dentry = lower_dentry; DENTRY_TO_PRIVATE(dentry)->wdi_mnt = lower_mount; /* lookup is special: it needs to handle negative dentries */ if (!lower_dentry->d_inode) { d_add(dentry, NULL); fist_print_dentry("base0fs_lookup OUT lower_dentry", lower_dentry); goto out; } fist_dprint(6, "lookup \"%s\" -> inode %lu\n", name, lower_dentry->d_inode->i_ino); err = base0fs_interpose(lower_dentry, dentry, dir->i_sb, 1); if (err) goto out_free; fist_checkinode(dentry->d_inode, "base0fs_lookup OUT: dentry->d_inode:"); fist_checkinode(dir, "base0fs_lookup OUT: dir"); fist_print_dentry("base0fs_lookup OUT lower_dentry", lower_dentry); fist_print_inode("base0fs_lookup OUT lower_inode", lower_dentry->d_inode); /* All is well */ goto out; out_free: d_drop(dentry); /* so that our bad dentry will get destroyed */ KFREE(DENTRY_TO_PRIVATE(dentry)); DENTRY_TO_PRIVATE_SM(dentry) = NULL; /* be safe */ out_dput: if (lower_dentry) dput(lower_dentry); out: fist_print_dentry("base0fs_lookup OUT", dentry); print_exit_status(err); return ERR_PTR(err); }
/* sb we pass is unionfs's super_block */ int unionfs_interpose(struct dentry *dentry, struct super_block *sb, int flag) { struct inode *hidden_inode; struct dentry *hidden_dentry; int err = 0; struct inode *inode; int is_negative_dentry = 1; int bindex, bstart, bend; print_entry("flag = %d", flag); verify_locked(dentry); fist_print_dentry("In unionfs_interpose", dentry); bstart = dbstart(dentry); bend = dbend(dentry); /* Make sure that we didn't get a negative dentry. */ for (bindex = bstart; bindex <= bend; bindex++) { if (dtohd_index(dentry, bindex) && dtohd_index(dentry, bindex)->d_inode) { is_negative_dentry = 0; break; } } BUG_ON(is_negative_dentry); /* We allocate our new inode below, by calling iget. * iget will call our read_inode which will initialize some * of the new inode's fields */ /* On revalidate we've already got our own inode and just need * to fix it up. */ if (flag == INTERPOSE_REVAL) { inode = dentry->d_inode; itopd(inode)->b_start = -1; itopd(inode)->b_end = -1; atomic_set(&itopd(inode)->uii_generation, atomic_read(&stopd(sb)->usi_generation)); itohi_ptr(inode) = KZALLOC(sbmax(sb) * sizeof(struct inode *), GFP_KERNEL); if (!itohi_ptr(inode)) { err = -ENOMEM; goto out; } } else { ino_t ino; /* get unique inode number for unionfs */ #ifdef UNIONFS_IMAP if (stopd(sb)->usi_persistent) { err = read_uin(sb, bindex, dtohd_index(dentry, bindex)->d_inode->i_ino, O_CREAT, &ino); if (err) goto out; } else #endif ino = iunique(sb, UNIONFS_ROOT_INO); inode = IGET(sb, ino); if (!inode) { err = -EACCES; /* should be impossible??? */ goto out; } } down(&inode->i_sem); if (atomic_read(&inode->i_count) > 1) goto skip; for (bindex = bstart; bindex <= bend; bindex++) { hidden_dentry = dtohd_index(dentry, bindex); if (!hidden_dentry) { set_itohi_index(inode, bindex, NULL); continue; } /* Initialize the hidden inode to the new hidden inode. */ if (!hidden_dentry->d_inode) continue; set_itohi_index(inode, bindex, IGRAB(hidden_dentry->d_inode)); } ibstart(inode) = dbstart(dentry); ibend(inode) = dbend(dentry); /* Use attributes from the first branch. */ hidden_inode = itohi(inode); /* Use different set of inode ops for symlinks & directories */ if (S_ISLNK(hidden_inode->i_mode)) inode->i_op = &unionfs_symlink_iops; else if (S_ISDIR(hidden_inode->i_mode)) inode->i_op = &unionfs_dir_iops; /* Use different set of file ops for directories */ if (S_ISDIR(hidden_inode->i_mode)) inode->i_fop = &unionfs_dir_fops; /* properly initialize special inodes */ if (S_ISBLK(hidden_inode->i_mode) || S_ISCHR(hidden_inode->i_mode) || S_ISFIFO(hidden_inode->i_mode) || S_ISSOCK(hidden_inode->i_mode)) init_special_inode(inode, hidden_inode->i_mode, hidden_inode->i_rdev); /* Fix our inode's address operations to that of the lower inode (Unionfs is FiST-Lite) */ if (inode->i_mapping->a_ops != hidden_inode->i_mapping->a_ops) { fist_dprint(7, "fixing inode 0x%p a_ops (0x%p -> 0x%p)\n", inode, inode->i_mapping->a_ops, hidden_inode->i_mapping->a_ops); inode->i_mapping->a_ops = hidden_inode->i_mapping->a_ops; } /* all well, copy inode attributes */ fist_copy_attr_all(inode, hidden_inode); skip: /* only (our) lookup wants to do a d_add */ switch (flag) { case INTERPOSE_DEFAULT: case INTERPOSE_REVAL_NEG: d_instantiate(dentry, inode); break; case INTERPOSE_LOOKUP: err = PTR_ERR(d_splice_alias(inode, dentry)); break; case INTERPOSE_REVAL: /* Do nothing. */ break; default: printk(KERN_ERR "Invalid interpose flag passed!"); BUG(); } fist_print_dentry("Leaving unionfs_interpose", dentry); fist_print_inode("Leaving unionfs_interpose", inode); up(&inode->i_sem); out: print_exit_status(err); return err; }