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; }
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_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; }
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_link(struct dentry *old_dentry, inode_t *dir, struct dentry *new_dentry) { int err; struct dentry *lower_old_dentry; struct dentry *lower_new_dentry; struct dentry *lower_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(dir, "base0fs_link-dir"); fist_checkinode(old_dentry->d_inode, "base0fs_link-oldinode"); dget(lower_old_dentry); dget(lower_new_dentry); lower_dir_dentry = base0fs_lock_parent(lower_new_dentry); err = VFS_LINK(lower_old_dentry, lower_dir_dentry->d_inode, lower_new_dentry); if (err || !lower_new_dentry->d_inode) goto out_lock; err = base0fs_interpose(lower_new_dentry, new_dentry, dir->i_sb, 0); if (err) goto out_lock; fist_copy_attr_timesizes(dir, lower_dir_dentry->d_inode); /* propagate number of hard-links */ old_dentry->d_inode->i_nlink = INODE_TO_LOWER(old_dentry->d_inode)->i_nlink; out_lock: unlock_dir(lower_dir_dentry); dput(lower_new_dentry); dput(lower_old_dentry); if (!new_dentry->d_inode) d_drop(new_dentry); print_exit_status(err); return err; }
/* * we now define delete_inode, because there are two VFS paths that may * destroy an inode: one of them calls clear inode before doing everything * else that's needed, and the other is fine. This way we truncate the inode * size (and its pages) and then clear our own inode, which will do an iput * on our and the lower inode. */ static void unionfs_delete_inode(struct inode *inode) { print_entry_location(); fist_checkinode(inode, "unionfs_delete_inode IN"); inode->i_size = 0; /* every f/s seems to do that */ clear_inode(inode); print_exit_location(); }
/* * we now define delete_inode, because there are two VFS paths that may * destroy an inode: one of them calls clear inode before doing everything * else that's needed, and the other is fine. This way we truncate the inode * size (and its pages) and then clear our own inode, which will do an iput * on our and the lower inode. */ STATIC void mini_fo_delete_inode(inode_t *inode) { print_entry_location(); fist_checkinode(inode, "mini_fo_delete_inode IN"); inode->i_size = 0; /* every f/s seems to do that */ clear_inode(inode); print_exit_location(); }
STATIC int base0fs_create(inode_t *dir, struct dentry *dentry, int mode, struct nameidata *nd) { int err; struct dentry *lower_dentry; struct vfsmount *lower_mount; struct dentry *lower_dir_dentry; FIST_ND_DECLARATIONS; print_entry_location(); lower_dentry = base0fs_lower_dentry(dentry); BUG_ON(!lower_dentry); fist_checkinode(dir, "base0fs_create"); lower_mount = DENTRY_TO_LVFSMNT(dentry); lower_dir_dentry = base0fs_lock_parent(lower_dentry); err = PTR_ERR(lower_dir_dentry); if (IS_ERR(lower_dir_dentry)) goto out; FIST_ND_SAVE_ARGS(dentry, lower_dentry, lower_mount); err = vfs_create(lower_dir_dentry->d_inode, lower_dentry, mode, nd); FIST_ND_RESTORE_ARGS; /* XXX this could potentially return a negative lower_dentry! */ if (err) goto out_lock; err = base0fs_interpose(lower_dentry, dentry, dir->i_sb, 0); if (err) goto out_lock; fist_copy_attr_timesizes(dir, lower_dir_dentry->d_inode); out_lock: unlock_dir(lower_dir_dentry); out: fist_checkinode(dir, "post base0fs_create"); print_exit_status(err); return err; }
STATIC int base0fs_symlink(inode_t *dir, struct dentry *dentry, const char *symname) { int err; struct dentry *lower_dentry; struct dentry *lower_dir_dentry; umode_t mode; print_entry_location(); lower_dentry = base0fs_lower_dentry(dentry); /* CPW: Moved below print_entry_location */ fist_checkinode(dir, "base0fs_symlink-dir"); dget(lower_dentry); lower_dir_dentry = base0fs_lock_parent(lower_dentry); mode = S_IALLUGO; err = VFS_SYMLINK(lower_dir_dentry->d_inode, lower_dentry, symname, mode); if (err || !lower_dentry->d_inode) goto out_lock; err = base0fs_interpose(lower_dentry, dentry, dir->i_sb, 0); if (err) goto out_lock; fist_copy_attr_timesizes(dir, lower_dir_dentry->d_inode); fist_checkinode(dir, "post base0fs_symlink-dir"); out_lock: unlock_dir(lower_dir_dentry); dput(lower_dentry); if (!dentry->d_inode) d_drop(dentry); print_exit_status(err); return err; }
STATIC int base0fs_mkdir(inode_t *dir, struct dentry *dentry, int mode) { 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_mkdir-dir"); lower_dir_dentry = base0fs_lock_parent(lower_dentry); err = VFS_MKDIR(lower_dir_dentry->d_inode, lower_dentry, mode); 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); /* update number of links on parent directory */ dir->i_nlink = lower_dir_dentry->d_inode->i_nlink; fist_checkinode(dir, "post base0fs_mkdir-dir"); out: unlock_dir(lower_dir_dentry); if (!dentry->d_inode) d_drop(dentry); print_exit_status(err); return err; }
STATIC int base0fs_rmdir(inode_t *dir, struct dentry *dentry) { int err = 0; 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_rmdir-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_RMDIR(lower_dir_dentry->d_inode, lower_dentry); dput(lower_dentry); if (!err) /* vfs_rmdir does that */ d_delete(lower_dentry); out_lock: fist_copy_attr_times(dir, lower_dir_dentry->d_inode); /* copy the nlink count for our dentry and our parent's dentry */ dir->i_nlink = lower_dir_dentry->d_inode->i_nlink; unlock_dir(lower_dir_dentry); /* * call d_drop so the system "forgets" about us */ if (!err) d_drop(dentry); dput(dentry); print_exit_status(err); return err; }
int unionfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry) { int err = 0; struct dentry *wh_dentry; print_entry_location(); double_lock_dentry(old_dentry, new_dentry); fist_checkinode(old_dir, "unionfs_rename-old_dir"); fist_checkinode(new_dir, "unionfs_rename-new_dir"); fist_print_dentry("IN: unionfs_rename, old_dentry", old_dentry); fist_print_dentry("IN: unionfs_rename, new_dentry", new_dentry); if (!S_ISDIR(old_dentry->d_inode->i_mode)) err = unionfs_partial_lookup(old_dentry); else err = may_rename_dir(old_dentry); if (err) goto out; err = unionfs_partial_lookup(new_dentry); if (err) goto out; /* * if new_dentry is already hidden because of whiteout, * simply override it even if the whiteouted dir is not empty. */ wh_dentry = lookup_whiteout(new_dentry); if (!IS_ERR(wh_dentry)) DPUT(wh_dentry); else if (new_dentry->d_inode) { if (S_ISDIR(old_dentry->d_inode->i_mode) != S_ISDIR(new_dentry->d_inode->i_mode)) { err = S_ISDIR(old_dentry->d_inode-> i_mode) ? -ENOTDIR : -EISDIR; goto out; } if (S_ISDIR(old_dentry->d_inode->i_mode)) { /* check if this unionfs directory is empty or not */ err = check_empty(new_dentry, NULL); if (err) goto out; /* Handle the case where we are overwriting directories * that are not really empty because of whiteout or * non-whiteout entries. */ } } #ifdef UNIONFS_DELETE_ALL if (IS_SET(old_dir->i_sb, DELETE_ALL)) err = unionfs_rename_all(old_dir, old_dentry, new_dir, new_dentry); else #endif err = unionfs_rename_whiteout(old_dir, old_dentry, new_dir, new_dentry); out: fist_checkinode(new_dir, "post unionfs_rename-new_dir"); fist_print_dentry("OUT: unionfs_rename, old_dentry", old_dentry); if (err) { /* clear the new_dentry stuff created */ d_drop(new_dentry); } else { /* force re-lookup since the dir on ro branch is not renamed, and hidden dentries still indicate the un-renamed ones. */ if (S_ISDIR(old_dentry->d_inode->i_mode)) atomic_dec(&dtopd(old_dentry)->udi_generation); fist_print_dentry("OUT: unionfs_rename, new_dentry", new_dentry); } unlock_dentry(new_dentry); unlock_dentry(old_dentry); print_exit_status(err); return err; }
struct dentry *unionfs_lookup_backend(struct dentry *dentry, int lookupmode) { int err = 0; struct dentry *hidden_dentry = NULL; struct dentry *wh_hidden_dentry = NULL; struct dentry *hidden_dir_dentry = NULL; struct dentry *parent_dentry = NULL; int bindex, bstart, bend, bopaque; int dentry_count = 0; /* Number of positive dentries. */ int first_dentry_offset = -1; struct dentry *first_hidden_dentry = NULL; int locked_parent = 0; int locked_child = 0; int opaque; char *whname = NULL; const char *name; int namelen; print_entry("mode = %d", lookupmode); /* We should already have a lock on this dentry in the case of a * partial lookup, or a revalidation. Otherwise it is returned from * new_dentry_private_data already locked. */ if (lookupmode == INTERPOSE_PARTIAL || lookupmode == INTERPOSE_REVAL || lookupmode == INTERPOSE_REVAL_NEG) { verify_locked(dentry); } else { BUG_ON(dtopd_nocheck(dentry) != NULL); locked_child = 1; } if (lookupmode != INTERPOSE_PARTIAL) if ((err = new_dentry_private_data(dentry))) goto out; /* must initialize dentry operations */ dentry->d_op = &unionfs_dops; parent_dentry = GET_PARENT(dentry); /* We never partial lookup the root directory. */ if (parent_dentry != dentry) { lock_dentry(parent_dentry); locked_parent = 1; } else { DPUT(parent_dentry); parent_dentry = NULL; goto out; } fist_print_dentry("IN unionfs_lookup (parent)", parent_dentry); fist_print_dentry("IN unionfs_lookup (child)", dentry); name = dentry->d_name.name; namelen = dentry->d_name.len; /* No dentries should get created for possible whiteout names. */ if (!is_validname(name)) { err = -EPERM; goto out_free; } /* Now start the actual lookup procedure. */ bstart = dbstart(parent_dentry); bend = dbend(parent_dentry); bopaque = dbopaque(parent_dentry); BUG_ON(bstart < 0); /* It would be ideal if we could convert partial lookups to only have * to do this work when they really need to. It could probably improve * performance quite a bit, and maybe simplify the rest of the code. */ if (lookupmode == INTERPOSE_PARTIAL) { bstart++; if ((bopaque != -1) && (bopaque < bend)) bend = bopaque; } fist_dprint(8, "bstart = %d, bend = %d\n", bstart, bend); for (bindex = bstart; bindex <= bend; bindex++) { hidden_dentry = dtohd_index(dentry, bindex); if (lookupmode == INTERPOSE_PARTIAL && hidden_dentry) continue; BUG_ON(hidden_dentry != NULL); hidden_dir_dentry = dtohd_index(parent_dentry, bindex); /* if the parent hidden dentry does not exist skip this */ if (!(hidden_dir_dentry && hidden_dir_dentry->d_inode)) continue; /* also skip it if the parent isn't a directory. */ if (!S_ISDIR(hidden_dir_dentry->d_inode->i_mode)) continue; /* Reuse the whiteout name because its value doesn't change. */ if (!whname) { whname = alloc_whname(name, namelen); if (IS_ERR(whname)) { err = PTR_ERR(whname); goto out_free; } } /* check if whiteout exists in this branch: lookup .wh.foo */ wh_hidden_dentry = LOOKUP_ONE_LEN(whname, hidden_dir_dentry, namelen + WHLEN); if (IS_ERR(wh_hidden_dentry)) { DPUT(first_hidden_dentry); err = PTR_ERR(wh_hidden_dentry); goto out_free; } if (wh_hidden_dentry->d_inode) { /* We found a whiteout so lets give up. */ fist_dprint(8, "whiteout found in %d\n", bindex); if (S_ISREG(wh_hidden_dentry->d_inode->i_mode)) { set_dbend(dentry, bindex); set_dbopaque(dentry, bindex); DPUT(wh_hidden_dentry); break; } err = -EIO; printk(KERN_NOTICE "EIO: Invalid whiteout entry type" " %d.\n", wh_hidden_dentry->d_inode->i_mode); DPUT(wh_hidden_dentry); DPUT(first_hidden_dentry); goto out_free; } DPUT(wh_hidden_dentry); wh_hidden_dentry = NULL; /* Now do regular lookup; lookup foo */ hidden_dentry = LOOKUP_ONE_LEN(name, hidden_dir_dentry, namelen); fist_print_generic_dentry("hidden result", hidden_dentry); if (IS_ERR(hidden_dentry)) { DPUT(first_hidden_dentry); err = PTR_ERR(hidden_dentry); goto out_free; } /* Store the first negative dentry specially, because if they * are all negative we need this for future creates. */ if (!hidden_dentry->d_inode) { if (!first_hidden_dentry && (dbstart(dentry) == -1)) { first_hidden_dentry = hidden_dentry; first_dentry_offset = bindex; } else { DPUT(hidden_dentry); } continue; } /* number of positive dentries */ dentry_count++; /* store underlying dentry */ if (dbstart(dentry) == -1) set_dbstart(dentry, bindex); set_dtohd_index(dentry, bindex, hidden_dentry); set_dbend(dentry, bindex); /* update parent directory's atime with the bindex */ fist_copy_attr_atime(parent_dentry->d_inode, hidden_dir_dentry->d_inode); /* We terminate file lookups here. */ if (!S_ISDIR(hidden_dentry->d_inode->i_mode)) { if (lookupmode == INTERPOSE_PARTIAL) continue; if (dentry_count == 1) goto out_positive; /* This can only happen with mixed D-*-F-* */ BUG_ON(!S_ISDIR(dtohd(dentry)->d_inode->i_mode)); continue; } opaque = is_opaque_dir(dentry, bindex); if (opaque < 0) { DPUT(first_hidden_dentry); err = opaque; goto out_free; } if (opaque) { set_dbend(dentry, bindex); set_dbopaque(dentry, bindex); break; } } if (dentry_count) goto out_positive; else goto out_negative; out_negative: if (lookupmode == INTERPOSE_PARTIAL) goto out; /* If we've only got negative dentries, then use the leftmost one. */ if (lookupmode == INTERPOSE_REVAL) { if (dentry->d_inode) { itopd(dentry->d_inode)->uii_stale = 1; } goto out; } /* This should only happen if we found a whiteout. */ if (first_dentry_offset == -1) { first_hidden_dentry = LOOKUP_ONE_LEN(name, hidden_dir_dentry, namelen); first_dentry_offset = bindex; if (IS_ERR(first_hidden_dentry)) { err = PTR_ERR(first_hidden_dentry); goto out; } } set_dtohd_index(dentry, first_dentry_offset, first_hidden_dentry); set_dbstart(dentry, first_dentry_offset); set_dbend(dentry, first_dentry_offset); if (lookupmode == INTERPOSE_REVAL_NEG) BUG_ON(dentry->d_inode != NULL); else d_add(dentry, NULL); goto out; /* This part of the code is for positive dentries. */ out_positive: BUG_ON(dentry_count <= 0); /* If we're holding onto the first negative dentry throw it out. */ DPUT(first_hidden_dentry); /* Partial lookups need to reinterpose, or throw away older negs. */ if (lookupmode == INTERPOSE_PARTIAL) { if (dentry->d_inode) { unionfs_reinterpose(dentry); goto out; } /* This somehow turned positive, so it is as if we had a * negative revalidation. */ lookupmode = INTERPOSE_REVAL_NEG; update_bstart(dentry); bstart = dbstart(dentry); bend = dbend(dentry); } err = unionfs_interpose(dentry, dentry->d_sb, lookupmode); if (err) goto out_drop; fist_checkinode(dentry->d_inode, "unionfs_lookup OUT: child"); fist_checkinode(parent_dentry->d_inode, "unionfs_lookup OUT: dir"); goto out; out_drop: d_drop(dentry); out_free: /* should dput all the underlying dentries on error condition */ bstart = dbstart(dentry); if (bstart >= 0) { bend = dbend(dentry); for (bindex = bstart; bindex <= bend; bindex++) DPUT(dtohd_index(dentry, bindex)); } KFREE(dtohd_ptr(dentry)); dtohd_ptr(dentry) = NULL; set_dbstart(dentry, -1); set_dbend(dentry, -1); out: if (!err && dtopd(dentry)) { BUG_ON(dbend(dentry) > dtopd(dentry)->udi_bcount); BUG_ON(dbend(dentry) > sbmax(dentry->d_sb)); BUG_ON(dbstart(dentry) < 0); } KFREE(whname); fist_print_dentry("OUT unionfs_lookup (parent)", parent_dentry); fist_print_dentry("OUT unionfs_lookup (child)", dentry); if (locked_parent) unlock_dentry(parent_dentry); DPUT(parent_dentry); if (locked_child) unlock_dentry(dentry); print_exit_status(err); return ERR_PTR(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); }
static int unionfs_setattr(struct dentry *dentry, struct iattr *ia) { int err = 0; struct dentry *hidden_dentry; struct inode *inode = NULL; struct inode *hidden_inode = NULL; int bstart, bend, bindex; int i; int copyup = 0; print_entry_location(); lock_dentry(dentry); bstart = dbstart(dentry); bend = dbend(dentry); inode = dentry->d_inode; for (bindex = bstart; (bindex <= bend) || (bindex == bstart); bindex++) { hidden_dentry = dtohd_index(dentry, bindex); if (!hidden_dentry) continue; BUG_ON(hidden_dentry->d_inode == NULL); /* If the file is on a read only branch */ if (is_robranch_super(dentry->d_sb, bindex) || IS_RDONLY(hidden_dentry->d_inode)) { if (copyup || (bindex != bstart)) continue; /* Only if its the leftmost file, copyup the file */ for (i = bstart - 1; i >= 0; i--) { size_t size = dentry->d_inode->i_size; if (ia->ia_valid & ATTR_SIZE) size = ia->ia_size; err = copyup_dentry(dentry->d_parent->d_inode, dentry, bstart, i, NULL, size); if (!err) { copyup = 1; hidden_dentry = dtohd(dentry); break; } /* if error is in the leftmost f/s, pass it up */ if (i == 0) goto out; } } err = notify_change(hidden_dentry, ia); if (err) goto out; break; } /* get the size from the first hidden inode */ hidden_inode = itohi(dentry->d_inode); fist_checkinode(inode, "unionfs_setattr"); fist_copy_attr_all(inode, hidden_inode); out: unlock_dentry(dentry); fist_checkinode(inode, "post unionfs_setattr"); print_exit_status(err); return err; }
static int unionfs_create(struct inode *parent, struct dentry *dentry, int mode, struct nameidata *nd) { int err = 0; struct dentry *hidden_dentry = NULL; struct dentry *whiteout_dentry = NULL; struct dentry *new_hidden_dentry; struct dentry *hidden_parent_dentry = NULL; int bindex = 0, bstart; char *name = NULL; print_entry_location(); lock_dentry(dentry); fist_print_dentry("IN unionfs_create", dentry); /* We start out in the leftmost branch. */ bstart = dbstart(dentry); hidden_dentry = dtohd(dentry); /* check if whiteout exists in this branch, i.e. lookup .wh.foo first */ name = alloc_whname(dentry->d_name.name, dentry->d_name.len); if (IS_ERR(name)) { err = PTR_ERR(name); goto out; } whiteout_dentry = LOOKUP_ONE_LEN(name, hidden_dentry->d_parent, dentry->d_name.len + WHLEN); if (IS_ERR(whiteout_dentry)) { err = PTR_ERR(whiteout_dentry); whiteout_dentry = NULL; goto out; } if (whiteout_dentry->d_inode) { /* .wh.foo has been found. */ /* First truncate it and then rename it to foo (hence having * the same overall effect as a normal create. * * XXX: This is not strictly correct. If we have unlinked the * file and it still has a reference count, then we should * actually unlink the whiteout so that user's data isn't * hosed over. */ struct dentry *hidden_dir_dentry; struct iattr newattrs; down(&whiteout_dentry->d_inode->i_sem); newattrs.ia_valid = ATTR_CTIME | ATTR_MODE | ATTR_ATIME | ATTR_MTIME | ATTR_UID | ATTR_GID | ATTR_FORCE | ATTR_KILL_SUID | ATTR_KILL_SGID; newattrs.ia_mode = mode & ~current->fs->umask; newattrs.ia_uid = current->fsuid; newattrs.ia_gid = current->fsgid; if (whiteout_dentry->d_inode->i_size != 0) { newattrs.ia_valid |= ATTR_SIZE; newattrs.ia_size = 0; } err = notify_change(whiteout_dentry, &newattrs); up(&whiteout_dentry->d_inode->i_sem); if (err) printk(KERN_WARNING "unionfs: %s:%d: notify_change failed: %d, ignoring..\n", __FILE__, __LINE__, err); new_hidden_dentry = dtohd(dentry); DGET(new_hidden_dentry); hidden_dir_dentry = GET_PARENT(whiteout_dentry); lock_rename(hidden_dir_dentry, hidden_dir_dentry); if (!(err = is_robranch_super(dentry->d_sb, bstart))) { err = vfs_rename(hidden_dir_dentry->d_inode, whiteout_dentry, hidden_dir_dentry->d_inode, new_hidden_dentry); } if (!err) { fist_copy_attr_timesizes(parent, new_hidden_dentry->d_parent-> d_inode); parent->i_nlink = get_nlinks(parent); } unlock_rename(hidden_dir_dentry, hidden_dir_dentry); DPUT(hidden_dir_dentry); DPUT(new_hidden_dentry); if (err) { /* exit if the error returned was NOT -EROFS */ if (!IS_COPYUP_ERR(err)) goto out; /* We were not able to create the file in this branch, * so, we try to create it in one branch to left */ bstart--; } else { /* reset the unionfs dentry to point to the .wh.foo entry. */ /* Discard any old reference. */ DPUT(dtohd(dentry)); /* Trade one reference to another. */ set_dtohd_index(dentry, bstart, whiteout_dentry); whiteout_dentry = NULL; err = unionfs_interpose(dentry, parent->i_sb, 0); goto out; } } for (bindex = bstart; bindex >= 0; bindex--) { hidden_dentry = dtohd_index(dentry, bindex); if (!hidden_dentry) { /* if hidden_dentry is NULL, create the entire * dentry directory structure in branch 'bindex'. * hidden_dentry will NOT be null when bindex == bstart * because lookup passed as a negative unionfs dentry * pointing to a lone negative underlying dentry */ hidden_dentry = create_parents(parent, dentry, bindex); if (!hidden_dentry || IS_ERR(hidden_dentry)) { if (IS_ERR(hidden_dentry)) err = PTR_ERR(hidden_dentry); continue; } } fist_checkinode(parent, "unionfs_create"); hidden_parent_dentry = lock_parent(hidden_dentry); if (IS_ERR(hidden_parent_dentry)) { err = PTR_ERR(hidden_parent_dentry); goto out; } /* We shouldn't create things in a read-only branch. */ if (!(err = is_robranch_super(dentry->d_sb, bindex))) { //DQ: vfs_create has a different prototype in 2.6 err = vfs_create(hidden_parent_dentry->d_inode, hidden_dentry, mode, nd); } if (err || !hidden_dentry->d_inode) { unlock_dir(hidden_parent_dentry); /* break out of for loop if the error wasn't -EROFS */ if (!IS_COPYUP_ERR(err)) break; } else { err = unionfs_interpose(dentry, parent->i_sb, 0); if (!err) { fist_copy_attr_timesizes(parent, hidden_parent_dentry-> d_inode); /* update number of links on parent directory */ parent->i_nlink = get_nlinks(parent); } unlock_dir(hidden_parent_dentry); break; } } out: DPUT(whiteout_dentry); KFREE(name); fist_print_dentry("OUT unionfs_create :", dentry); unlock_dentry(dentry); print_exit_status(err); return err; }