static int unionfs_fsync(struct file *file, struct dentry *dentry, int datasync) { int err; struct file *hidden_file = NULL; print_entry_location(); if ((err = unionfs_file_revalidate(file, 1))) goto out; PASSERT(ftopd(file)); hidden_file = ftohf(file); err = -EINVAL; if (!hidden_file->f_op || !hidden_file->f_op->fsync) goto out; down(&hidden_file->f_dentry->d_inode->i_sem); err = hidden_file->f_op->fsync(hidden_file, hidden_file->f_dentry, datasync); up(&hidden_file->f_dentry->d_inode->i_sem); out: print_exit_status(err); return err; }
int unionfs_ioctl_branchcount(struct file *file, unsigned int cmd, unsigned long arg) { int err = 0; int bstart, bend; int i; struct super_block *sb = file->f_dentry->d_sb; print_entry_location(); bstart = sbstart(sb); bend = sbend(sb); err = bend + 1; if (!arg) goto out; for (i = bstart; i <= bend; i++) { if (put_user(branch_count(sb, i), ((int *)arg) + i)) { err = -EFAULT; goto out; } } out: print_exit_status(err); return err; }
STATIC int base0fs_readlink(struct dentry *dentry, char *buf, int bufsiz) { int err; struct dentry *lower_dentry; print_entry_location(); lower_dentry = base0fs_lower_dentry(dentry);/* CPW: Moved below print_entry_location */ fist_print_dentry("base0fs_readlink IN", dentry); if (!lower_dentry->d_inode->i_op || !lower_dentry->d_inode->i_op->readlink) { err = -EINVAL; goto out; } err = lower_dentry->d_inode->i_op->readlink(lower_dentry, buf, bufsiz); if (err > 0) fist_copy_attr_atime(dentry->d_inode, lower_dentry->d_inode); out: print_exit_status(err); return err; }
ssize_t unionfs_read(struct file * file, char *buf, size_t count, loff_t * ppos) { int err = -EINVAL; struct file *hidden_file = NULL; loff_t pos = *ppos; print_entry_location(); if ((err = unionfs_file_revalidate(file, 0))) goto out; fist_print_file("entering read()", file); PASSERT(ftopd(file)); hidden_file = ftohf(file); PASSERT(hidden_file); if (!hidden_file->f_op || !hidden_file->f_op->read) goto out; err = hidden_file->f_op->read(hidden_file, buf, count, &pos); *ppos = pos; if (err >= 0) { /* atime should also be updated for reads of size zero or more */ fist_copy_attr_atime(file->f_dentry->d_inode, hidden_file->f_dentry->d_inode); } memcpy(&(file->f_ra), &(hidden_file->f_ra), sizeof(struct file_ra_state)); out: fist_print_file("leaving read()", file); print_exit_status(err); return err; }
int unionfs_readlink(struct dentry *dentry, char __user * buf, int bufsiz) { int err; struct dentry *hidden_dentry; print_entry_location(); lock_dentry(dentry); hidden_dentry = dtohd(dentry); fist_print_dentry("unionfs_readlink IN", dentry); if (!hidden_dentry->d_inode->i_op || !hidden_dentry->d_inode->i_op->readlink) { err = -EINVAL; goto out; } err = hidden_dentry->d_inode->i_op->readlink(hidden_dentry, buf, bufsiz); if (err > 0) fist_copy_attr_atime(dentry->d_inode, hidden_dentry->d_inode); out: unlock_dentry(dentry); print_exit_status(err); return err; }
STATIC int base0fs_mknod(inode_t *dir, struct dentry *dentry, int mode, dev_t dev) { int err; struct dentry *lower_dentry; struct dentry *lower_dir_dentry; print_entry_location(); lower_dentry = base0fs_lower_dentry(dentry); /* CPW: Moved below print_entry_location */ fist_checkinode(dir, "base0fs_mknod-dir"); lower_dir_dentry = base0fs_lock_parent(lower_dentry); err = VFS_MKNOD(lower_dir_dentry->d_inode, lower_dentry, mode, dev); if (err || !lower_dentry->d_inode) goto out; err = base0fs_interpose(lower_dentry, dentry, dir->i_sb, 0); if (err) goto out; fist_copy_attr_timesizes(dir, lower_dir_dentry->d_inode); out: unlock_dir(lower_dir_dentry); if (!dentry->d_inode) d_drop(dentry); fist_checkinode(dir, "post base0fs_mknod-dir"); print_exit_status(err); return err; }
/* BKL held by caller. * dentry->d_inode->i_sem down */ int unionfs_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags) { struct dentry *hidden_dentry = NULL; int err = -EOPNOTSUPP; print_entry_location(); lock_dentry(dentry); hidden_dentry = dtohd(dentry); fist_dprint(8, "setxattr: name=\"%s\", value %lu bytes, flags=%x\n", name, (unsigned long)size, flags); if (hidden_dentry->d_inode->i_op->setxattr) { down(&hidden_dentry->d_inode->i_sem); /* lock_kernel() already done by caller. */ err = hidden_dentry->d_inode->i_op-> setxattr(hidden_dentry, name, value, size, flags); /* unlock_kernel() will be done by caller. */ up(&hidden_dentry->d_inode->i_sem); } unlock_dentry(dentry); print_exit_status(err); return err; }
/* BKL held by caller. * dentry->d_inode->i_sem down */ int unionfs_removexattr(struct dentry *dentry, const char *name) { struct dentry *hidden_dentry = NULL; int err = -EOPNOTSUPP; char *encoded_name; print_entry_location(); lock_dentry(dentry); hidden_dentry = dtohd(dentry); fist_dprint(8, "removexattr: name=\"%s\"\n", name); if (hidden_dentry->d_inode->i_op->removexattr) { encoded_name = (char *)name; down(&hidden_dentry->d_inode->i_sem); /* lock_kernel() already done by caller. */ err = hidden_dentry->d_inode->i_op->removexattr(hidden_dentry, encoded_name); /* unlock_kernel() will be done by caller. */ up(&hidden_dentry->d_inode->i_sem); } unlock_dentry(dentry); print_exit_status(err); return err; }
/* The rest of these are utility functions for lookup. */ static int is_opaque_dir(struct dentry *dentry, int bindex) { int err = 0; 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; print_entry_location(); hidden_dentry = dtohd_index(dentry, bindex); hidden_inode = hidden_dentry->d_inode; BUG_ON(!S_ISDIR(hidden_inode->i_mode)); current->fsuid = hidden_inode->i_uid; current->fsgid = hidden_inode->i_gid; 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 (IS_ERR(wh_hidden_dentry)) { err = PTR_ERR(wh_hidden_dentry); fist_dprint(1, "LOOKUP_ONE_LEN returned: %d\n", err); goto out; } if (wh_hidden_dentry->d_inode) err = 1; DPUT(wh_hidden_dentry); out: print_exit_status(err); return err; }
int kdb3fs_d_delete(dentry_t *dentry) { dentry_t *hidden_dentry; int err = 0; print_entry_location(); #if 0 /* this could be a negative dentry, so check first */ if (!dtopd(dentry)) { fist_dprint(6, "dentry without private data: %*s", dentry->d_name.len, dentry->d_name.name); goto out; } if (!(hidden_dentry = dtohd(dentry))) { fist_dprint(6, "dentry without hidden_dentry: %*s", dentry->d_name.len, dentry->d_name.name); goto out; } // fist_print_dentry("D_DELETE IN", dentry); /* added b/c of changes to dput(): it calls d_drop on us */ if (hidden_dentry->d_op && hidden_dentry->d_op->d_delete) { err = hidden_dentry->d_op->d_delete(hidden_dentry); } #endif out: print_exit_status(err); return err; }
/* FIST-LITE special version of mmap */ static int unionfs_mmap(struct file *file, struct vm_area_struct *vma) { int err = 0; struct file *hidden_file = NULL; int willwrite; print_entry_location(); /* This might could be deferred to mmap's writepage. */ willwrite = ((vma->vm_flags | VM_SHARED | VM_WRITE) == vma->vm_flags); if ((err = unionfs_file_revalidate(file, willwrite))) goto out; PASSERT(ftopd(file)); hidden_file = ftohf(file); err = -ENODEV; if (!hidden_file->f_op || !hidden_file->f_op->mmap) goto out; PASSERT(hidden_file); PASSERT(hidden_file->f_op); PASSERT(hidden_file->f_op->mmap); vma->vm_file = hidden_file; err = hidden_file->f_op->mmap(hidden_file, vma); get_file(hidden_file); /* make sure it doesn't get freed on us */ fput(file); /* no need to keep extra ref on ours */ out: print_exit_status(err); return err; }
STATIC int base0fs_setattr(struct dentry *dentry, struct iattr *ia) { int err = 0; struct dentry *lower_dentry; inode_t *inode; inode_t *lower_inode; print_entry_location(); lower_dentry = base0fs_lower_dentry(dentry); inode = dentry->d_inode; lower_inode = INODE_TO_LOWER(inode); fist_checkinode(inode, "base0fs_setattr"); err = notify_change(lower_dentry, #ifdef HAVE_3_ARG_NOTIFY_CHANGE DENTRY_TO_LVFSMNT(dentry), #endif ia); #if defined(FIST_FILTER_DATA) || defined(FIST_FILTER_SCA) out: #endif /* FIST_FILTER_DATA || FIST_FILTER_SCA */ /* * The lower file system might has changed the attributes, even if * notify_change above resulted in an error(!) so we copy the * lower_inode's attributes (and a few more) to our inode. */ fist_copy_attr_all(inode, lower_inode); fist_checkinode(inode, "post base0fs_setattr"); print_exit_status(err); return err; }
/* BKL held by caller. * dentry->d_inode->i_sem down */ ssize_t unionfs_listxattr(struct dentry * dentry, char *list, size_t size) { struct dentry *hidden_dentry = NULL; int err = -EOPNOTSUPP; char *encoded_list = NULL; print_entry_location(); lock_dentry(dentry); hidden_dentry = dtohd(dentry); if (hidden_dentry->d_inode->i_op->listxattr) { encoded_list = list; down(&hidden_dentry->d_inode->i_sem); /* lock_kernel() already done by caller. */ err = hidden_dentry->d_inode->i_op->listxattr(hidden_dentry, encoded_list, size); /* unlock_kernel() will be done by caller. */ up(&hidden_dentry->d_inode->i_sem); } unlock_dentry(dentry); print_exit_status(err); return err; }
/* * BKL held by caller. * dentry->d_inode->i_{sem,mutex} down */ STATIC ssize_t base0fs_listxattr(struct dentry *dentry, char *list, size_t size) { struct dentry *lower_dentry = NULL; int err = -ENOTSUPP; char *encoded_list = NULL; print_entry_location(); lower_dentry = DENTRY_TO_LOWER(dentry); BUG_ON(!lower_dentry); BUG_ON(!lower_dentry->d_inode); BUG_ON(!lower_dentry->d_inode->i_op); if (lower_dentry->d_inode->i_op->listxattr) { encoded_list = list; lock_inode(lower_dentry->d_inode); /* lock_kernel() already done by caller. */ err = lower_dentry->d_inode->i_op->listxattr(lower_dentry, encoded_list, size); /* unlock_kernel() will be done by caller. */ unlock_inode(lower_dentry->d_inode); } out: print_exit_status(err); return err; }
static unsigned int unionfs_poll(struct file *file, poll_table * wait) { unsigned int mask = DEFAULT_POLLMASK; struct file *hidden_file = NULL; print_entry_location(); if (unionfs_file_revalidate(file, 0)) { /* We should pretend an error happend. */ mask = POLLERR | POLLIN | POLLOUT; goto out; } if (ftopd(file) != NULL) hidden_file = ftohf(file); if (!hidden_file->f_op || !hidden_file->f_op->poll) goto out; mask = hidden_file->f_op->poll(hidden_file, wait); out: print_exit_status(mask); return mask; }
/* * BKL held by caller. * dentry->d_inode->i_{sem,mutex} down */ STATIC int base0fs_removexattr(struct dentry *dentry, const char *name) { struct dentry *lower_dentry = NULL; int err = -ENOTSUPP; char *encoded_name; print_entry_location(); lower_dentry = DENTRY_TO_LOWER(dentry); BUG_ON(!lower_dentry); BUG_ON(!lower_dentry->d_inode); BUG_ON(!lower_dentry->d_inode->i_op); fist_dprint(18, "removexattr: name=\"%s\"\n", name); if (lower_dentry->d_inode->i_op->removexattr) { encoded_name = (char *)name; lock_inode(lower_dentry->d_inode); /* lock_kernel() already done by caller. */ err = lower_dentry->d_inode->i_op->removexattr(lower_dentry, encoded_name); /* unlock_kernel() will be done by caller. */ unlock_inode(lower_dentry->d_inode); } out: print_exit_status(err); return err; }
STATIC int base0fs_permission(inode_t *inode, int mask, struct nameidata* nd) { inode_t *lower_inode; int err; struct dentry *lower_dentry; struct vfsmount *lower_mount; FIST_ND_DECLARATIONS; print_entry_location(); lower_inode = INODE_TO_LOWER(inode); if(nd) { BUG_ON(!NAMEIDATA_TO_DENTRY(nd)); /* needed to find lower_dentry */ lower_dentry = base0fs_lower_dentry(NAMEIDATA_TO_DENTRY(nd)); lower_mount = DENTRY_TO_LVFSMNT(NAMEIDATA_TO_DENTRY(nd)); BUG_ON(!SUPERBLOCK_TO_PRIVATE(NAMEIDATA_TO_DENTRY(nd)->d_sb)); /* needed in FIST_ND_SAVE_ARGS macro */ FIST_ND_SAVE_ARGS(NAMEIDATA_TO_DENTRY(nd), lower_dentry, lower_mount); } err = permission(lower_inode, mask, nd); if(nd) { FIST_ND_RESTORE_ARGS; } out: print_exit_status(err); return err; }
static int unionfs_file_readdir(struct file *file, void *dirent, filldir_t filldir) { int err = -ENOTDIR; print_entry_location(); print_exit_status(err); return err; }
int main(int argc, char *argv[]) { int retc = 0; int opt; int errflg = 0; if ((command = strrchr(argv[0], '/')) != NULL) command++; else command = argv[0]; while ((opt = getopt(argc, argv, "F")) != EOF) { switch (opt) { case 'F': /* force grabbing (no O_EXCL) */ Fflag = PGRAB_FORCE; break; default: errflg = 1; break; } } argc -= optind; argv += optind; if (errflg || argc <= 0) { (void) fprintf(stderr, "usage: %s pid ...\n", command); (void) fprintf(stderr, " (Reap a defunct process by forcing " "its parent to wait(2) for it)\n"); exit(2); } /* catch signals from terminal */ if (sigset(SIGHUP, SIG_IGN) == SIG_DFL) (void) sigset(SIGHUP, intr); if (sigset(SIGINT, SIG_IGN) == SIG_DFL) (void) sigset(SIGINT, intr); if (sigset(SIGPIPE, SIG_IGN) == SIG_DFL) (void) sigset(SIGPIPE, intr); if (sigset(SIGQUIT, SIG_IGN) == SIG_DFL) (void) sigset(SIGQUIT, intr); (void) sigset(SIGTERM, intr); while (--argc >= 0 && !interrupt) { pid_t pid; int wstat, r; retc += r = reap(*argv++, &pid, &wstat); if (r == 0) print_exit_status(pid, wstat); } if (interrupt && retc == 0) retc++; return (retc == 0 ? 0 : 1); }
/* * Rename all occurences of source except for the leftmost destination */ static int __rename_all(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry, fd_set * success_mask, struct rename_info *info) { int bindex; int err = 0; print_entry_location(); /* Loop through all the branches from right to left and rename all * instances of source to destination, except the leftmost destination */ for (bindex = info->old_bend; bindex >= info->old_bstart; bindex--) { /* We don't rename if there is no source. */ if (dtohd_index(old_dentry, bindex) == NULL) continue; /* we rename the bstart of destination only at the last of * all operations, so that we don't lose it on error */ if (info->clobber && (bindex == info->new_bstart)) continue; DPUT(info->wh_old); info->bwh_old = bindex; /* We shouldn't have a handle on this if there is no inode. */ err = do_rename(old_dir, old_dentry, new_dir, new_dentry, bindex, &info->wh_old); if (!err) { /* For reverting. */ FD_SET(bindex, success_mask); /* So we know not to copyup on failures the right */ info->rename_ok = bindex; } else if (IS_COPYUP_ERR(err)) { if (info->isdir) { err = -EXDEV; break; } /* we need a whiteout... */ info->do_whiteout = bindex - 1; if (bindex == info->old_bstart) /* ...and a copyup */ info->do_copyup = bindex - 1; err = 0; /* reset error */ } else break; /* error is set by do_rename */ } print_exit_status(err); return err; }
static int unionfs_unlink_whiteout(struct inode *dir, struct dentry *dentry) { struct dentry *hidden_dentry; struct dentry *hidden_dir_dentry; int bindex; int err = 0; print_entry_location(); if ((err = unionfs_partial_lookup(dentry))) goto out; bindex = dbstart(dentry); hidden_dentry = dtohd_index(dentry, bindex); if (!hidden_dentry) goto out; hidden_dir_dentry = lock_parent(hidden_dentry); /* avoid destroying the hidden inode if the file is in use */ DGET(hidden_dentry); if (!(err = is_robranch_super(dentry->d_sb, bindex))) err = vfs_unlink(hidden_dir_dentry->d_inode, hidden_dentry); DPUT(hidden_dentry); fist_copy_attr_times(dir, hidden_dir_dentry->d_inode); unlock_dir(hidden_dir_dentry); if (err) { if (!IS_COPYUP_ERR(err)) goto out; } if (err) { if (dbstart(dentry) == 0) { goto out; } err = create_whiteout(dentry, dbstart(dentry) - 1); } else if (dbopaque(dentry) != -1) { /* There is a hidden lower-priority file with the same name. */ err = create_whiteout(dentry, dbopaque(dentry)); } else { err = create_whiteout(dentry, dbstart(dentry)); } out: if (!err) dentry->d_inode->i_nlink--; /* We don't want to leave negative leftover dentries for revalidate. */ if (!err && (dbopaque(dentry) != -1)) update_bstart(dentry); print_exit_status(err); return err; }
/* this unionfs_write() does not modify data pages! */ ssize_t unionfs_write(struct file * file, const char *buf, size_t count, loff_t * ppos) { int err = -EINVAL; struct file *hidden_file = NULL; struct inode *inode; struct inode *hidden_inode; loff_t pos = *ppos; int bstart, bend; print_entry_location(); if ((err = unionfs_file_revalidate(file, 1))) goto out; inode = file->f_dentry->d_inode; bstart = fbstart(file); bend = fbend(file); ASSERT(bstart != -1); PASSERT(ftopd(file)); PASSERT(ftohf(file)); hidden_file = ftohf(file); hidden_inode = hidden_file->f_dentry->d_inode; if (!hidden_file->f_op || !hidden_file->f_op->write) goto out; /* adjust for append -- seek to the end of the file */ if (file->f_flags & O_APPEND) pos = inode->i_size; err = hidden_file->f_op->write(hidden_file, buf, count, &pos); /* * copy ctime and mtime from lower layer attributes * atime is unchanged for both layers */ if (err >= 0) fist_copy_attr_times(inode, hidden_inode); *ppos = pos; /* update this inode's size */ if (pos > inode->i_size) inode->i_size = pos; out: print_exit_status(err); return err; }
int unionfs_ioctl_rdwrbranch(struct inode *inode, unsigned int cmd, unsigned long arg) { int err; struct unionfs_rdwrbranch_args *rdwrargs = NULL; int gen; print_entry_location(); unionfs_write_lock(inode->i_sb); lock_dentry(inode->i_sb->s_root); if ((err = newputmap(inode->i_sb))) goto out; err = -ENOMEM; rdwrargs = KMALLOC(sizeof(struct unionfs_rdwrbranch_args), GFP_KERNEL); if (!rdwrargs) goto out; err = -EFAULT; if (copy_from_user (rdwrargs, (const void __user *)arg, sizeof(struct unionfs_rdwrbranch_args))) goto out; err = -EINVAL; if (rdwrargs->rwb_branch < 0 || (rdwrargs->rwb_branch > (sbend(inode->i_sb) + 1))) goto out; if (rdwrargs->rwb_perms & ~(MAY_READ | MAY_WRITE | MAY_NFSRO)) goto out; if (!(rdwrargs->rwb_perms & MAY_READ)) goto out; set_branchperms(inode->i_sb, rdwrargs->rwb_branch, rdwrargs->rwb_perms); atomic_inc(&stopd(inode->i_sb)->usi_generation); gen = atomic_read(&stopd(inode->i_sb)->usi_generation); atomic_set(&dtopd(inode->i_sb->s_root)->udi_generation, gen); atomic_set(&itopd(inode->i_sb->s_root->d_inode)->uii_generation, gen); err = 0; out: unlock_dentry(inode->i_sb->s_root); unionfs_write_unlock(inode->i_sb); KFREE(rdwrargs); print_exit_status(err); return err; }
STATIC int base0fs_permission(inode_t *inode, int mask) { inode_t *lower_inode; int err; print_entry_location(); lower_inode = INODE_TO_LOWER(inode); err = inode_permission(lower_inode, mask); print_exit_status(err); return err; }
/* * Unlink all destinations (if they exist) to the left of the left-most * source */ static int __rename_all_unlink(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry, struct rename_info *info) { int bindex; struct dentry *unlink_dentry; struct dentry *unlink_dir_dentry; int err = 0; print_entry_location(); for (bindex = info->old_bstart - 1; bindex > info->new_bstart; bindex--) { unlink_dentry = dtohd_index(new_dentry, bindex); if (!unlink_dentry) continue; /* lock, unlink if possible, copyup times, unlock */ unlink_dir_dentry = lock_parent(unlink_dentry); if (!(err = is_robranch_super(old_dir->i_sb, bindex))) err = vfs_unlink(unlink_dir_dentry->d_inode, unlink_dentry); fist_copy_attr_times(new_dentry->d_parent->d_inode, unlink_dir_dentry->d_inode); new_dentry->d_parent->d_inode->i_nlink = get_nlinks(new_dentry->d_parent->d_inode); unlock_dir(unlink_dir_dentry); if (!err) { if (bindex != info->new_bstart) { DPUT(unlink_dentry); set_dtohd_index(new_dentry, bindex, NULL); } } else if (IS_COPYUP_ERR(err)) { if (info->isdir) { err = -EXDEV; break; } info->do_copyup = bindex - 1; err = 0; /* reset error */ } else break; /* err is set by is_ro_branch_super or vfs_unlink */ } print_exit_status(err); return err; }
int unionfs_rmdir(struct inode *dir, struct dentry *dentry) { int err = 0; struct unionfs_dir_state *namelist = NULL; print_entry_location(); lock_dentry(dentry); fist_print_dentry("IN unionfs_rmdir: ", dentry); /* check if this unionfs directory is empty or not */ err = check_empty(dentry, &namelist); if (err) { goto out; } if (IS_SET(dir->i_sb, DELETE_WHITEOUT)) { /* Delete the first directory. */ err = unionfs_rmdir_first(dir, dentry, namelist); /* create whiteout */ if (!err) { err = create_whiteout(dentry, dbstart(dentry)); } else { int new_err; if (dbstart(dentry) == 0) goto out; /* exit if the error returned was NOT -EROFS */ if (!IS_COPYUP_ERR(err)) goto out; new_err = create_whiteout(dentry, dbstart(dentry) - 1); if (new_err != -EEXIST) err = new_err; } } else { /* delete all. */ err = unionfs_rmdir_all(dir, dentry, namelist); } out: /* call d_drop so the system "forgets" about us */ if (!err) d_drop(dentry); if (namelist) free_rdstate(namelist); unlock_dentry(dentry); print_exit_status(err); return err; }
int unionfs_d_revalidate_wrap(struct dentry *dentry, struct nameidata *nd) { int err; print_entry_location(); lock_dentry(dentry); err = unionfs_d_revalidate(dentry, nd); unlock_dentry(dentry); print_exit_status(err); return err; }
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; }
STATIC int base0fs_unlink(inode_t *dir, struct dentry *dentry) { int err = 0; inode_t *lower_dir; struct dentry *lower_dentry; struct dentry *lower_dir_dentry; print_entry_location(); lower_dir = INODE_TO_LOWER(dir); /* CPW: Moved below print_entry_location */ lower_dentry = base0fs_lower_dentry(dentry); BUG_ON(!lower_dentry); fist_checkinode(dir, "base0fs_unlink-dir"); dget(dentry); lower_dir_dentry = base0fs_lock_parent(lower_dentry); /* avoid destroying the lower inode if the file is in use */ dget(lower_dentry); err = VFS_UNLINK(lower_dir, lower_dentry); dput(lower_dentry); if (!err) /* vfs_unlink does that */ d_delete(lower_dentry); out_lock: fist_copy_attr_times(dir, lower_dir); /* propagate number of hard-links */ dentry->d_inode->i_nlink = INODE_TO_LOWER(dentry->d_inode)->i_nlink; fist_copy_attr_ctime(dentry->d_inode, dir); unlock_dir(lower_dir_dentry); /* * call d_drop so the system "forgets" about us */ if (!err) { d_drop(dentry); } dput(dentry); fist_checkinode(dir, "post base0fs_unlink-dir"); print_exit_status(err); return err; }
/* * call d_drop so the system "forgets" about us */ if (!err) d_drop(dentry); dput(dentry); print_exit_status(err); return err; } STATIC int base0fs_mknod(inode_t *dir, struct dentry *dentry, int mode, dev_t dev) { int err; struct dentry *lower_dentry; struct dentry *lower_dir_dentry; print_entry_location(); lower_dentry = base0fs_lower_dentry(dentry); /* CPW: Moved below print_entry_location */ fist_checkinode(dir, "base0fs_mknod-dir"); lower_dir_dentry = base0fs_lock_parent(lower_dentry); err = VFS_MKNOD(lower_dir_dentry->d_inode, lower_dentry, mode, dev); if (err || !lower_dentry->d_inode) goto out; err = base0fs_interpose(lower_dentry, dentry, dir->i_sb, 0); if (err) goto out; fist_copy_attr_timesizes(dir, lower_dir_dentry->d_inode); out: unlock_dir(lower_dir_dentry); if (!dentry->d_inode) d_drop(dentry); fist_checkinode(dir, "post base0fs_mknod-dir"); print_exit_status(err); return err; } STATIC int base0fs_rename(inode_t *old_dir, struct dentry *old_dentry, inode_t *new_dir, struct dentry *new_dentry) { int err; struct dentry *lower_old_dentry; struct dentry *lower_new_dentry; struct dentry *lower_old_dir_dentry; struct dentry *lower_new_dir_dentry; print_entry_location(); lower_old_dentry = base0fs_lower_dentry(old_dentry);/* CPW: Moved below print_entry_location */ lower_new_dentry = base0fs_lower_dentry(new_dentry); fist_checkinode(old_dir, "base0fs_rename-old_dir"); fist_checkinode(new_dir, "base0fs_rename-new_dir"); dget(lower_old_dentry); dget(lower_new_dentry); lower_old_dir_dentry = dget_parent(lower_old_dentry); lower_new_dir_dentry = dget_parent(lower_new_dentry); lock_rename(lower_old_dir_dentry, lower_new_dir_dentry); err = VFS_RENAME(lower_old_dir_dentry->d_inode, lower_old_dentry, lower_new_dir_dentry->d_inode, lower_new_dentry); if (err) goto out_lock; fist_copy_attr_all(new_dir, lower_new_dir_dentry->d_inode); if (new_dir != old_dir) fist_copy_attr_all(old_dir, lower_old_dir_dentry->d_inode); out_lock: // unlock_rename will dput the new/old parent dentries whose refcnts // were incremented via dget_parent above. dput(lower_new_dentry); dput(lower_old_dentry); unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry); fist_checkinode(new_dir, "post base0fs_rename-new_dir"); print_exit_status(err); return err; } STATIC int base0fs_readlink(struct dentry *dentry, char *buf, int bufsiz) { int err; struct dentry *lower_dentry; print_entry_location(); lower_dentry = base0fs_lower_dentry(dentry);/* CPW: Moved below print_entry_location */ fist_print_dentry("base0fs_readlink IN", dentry); if (!lower_dentry->d_inode->i_op || !lower_dentry->d_inode->i_op->readlink) { err = -EINVAL; goto out; } err = lower_dentry->d_inode->i_op->readlink(lower_dentry, buf, bufsiz); if (err > 0) fist_copy_attr_atime(dentry->d_inode, lower_dentry->d_inode); out: print_exit_status(err); return err; } #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13) STATIC int base0fs_follow_link(struct dentry *dentry, struct nameidata *nd) #else /* 2.6.13 or newer */ STATIC void * base0fs_follow_link(struct dentry *dentry, struct nameidata *nd) #endif /* 2.6.13 or newer */ { char *buf; int len = PAGE_SIZE, err; mm_segment_t old_fs; struct dentry *lower_dentry; print_entry_location(); // fist_print_dentry("base0fs_follow_link dentry IN", dentry); /* buf is allocated here, and freed when VFS calls our put_link method */ err = -ENOMEM; buf = KMALLOC(len, GFP_KERNEL); if (!buf) goto out; old_fs = get_fs(); set_fs(KERNEL_DS); err = dentry->d_inode->i_op->readlink(dentry, buf, len); set_fs(old_fs); if (err < 0) goto out_free; buf[err] = 0; // terminate the buffer -- XXX still needed? err = 0; nd_set_link(nd,buf); goto out; out_free: KFREE(buf); out: #if 0 if (err < 0) { dput(nd->dentry); printk("EZK follow_link() mnt_cnt %d\n", (int) atomic_read(&nd->mnt->mnt_count)); mntput(nd->mnt); } #endif print_exit_status(err); #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13) return err; #else /* 2.6.13 or newer */ return ERR_PTR(err); #endif /* 2.6.13 or newer */ }