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_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 unionfs_unlink_whiteout(struct inode *dir, struct dentry *dentry) { int err = 0; struct dentry *hidden_old_dentry; struct dentry *hidden_wh_dentry = NULL; struct dentry *hidden_old_dir_dentry, *hidden_new_dir_dentry; char *name = NULL; struct iattr newattrs; print_entry_location(); fist_print_dentry("IN unionfs_unlink_whiteout: ", dentry); /* create whiteout, get the leftmost underlying dentry and rename it */ hidden_old_dentry = dtohd(dentry); /* lookup .wh.foo first, MUST NOT EXIST */ name = KMALLOC(dentry->d_name.len + sizeof(".wh."), GFP_UNIONFS); if (!name) { err = -ENOMEM; goto out; } strcpy(name, ".wh."); strncat(name, dentry->d_name.name, dentry->d_name.len); name[4 + dentry->d_name.len] = '\0'; hidden_wh_dentry = LOOKUP_ONE_LEN(name, hidden_old_dentry->d_parent, dentry->d_name.len + 4); if (IS_ERR(hidden_wh_dentry)) { err = PTR_ERR(hidden_wh_dentry); goto out; } ASSERT(hidden_wh_dentry->d_inode == NULL); DGET(hidden_old_dentry); hidden_old_dir_dentry = GET_PARENT(hidden_old_dentry); hidden_new_dir_dentry = GET_PARENT(hidden_wh_dentry); double_lock(hidden_old_dir_dentry, hidden_new_dir_dentry); if (!(err = is_robranch(dentry))) { if (S_ISREG(hidden_old_dentry->d_inode->i_mode)) { err = vfs_rename(hidden_old_dir_dentry->d_inode, hidden_old_dentry, hidden_new_dir_dentry->d_inode, hidden_wh_dentry); } else { err = vfs_unlink(hidden_old_dir_dentry->d_inode, hidden_old_dentry); if (!err) #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) err = vfs_create(hidden_new_dir_dentry->d_inode, hidden_wh_dentry, 0666, NULL); #else err = vfs_create(hidden_new_dir_dentry->d_inode, hidden_wh_dentry, 0666); #endif } } double_unlock(hidden_old_dir_dentry, hidden_new_dir_dentry); DPUT(hidden_old_dentry); DPUT(hidden_wh_dentry); if (!err) { hidden_wh_dentry = LOOKUP_ONE_LEN(name, hidden_old_dentry->d_parent, dentry->d_name.len + 4); if (IS_ERR(hidden_wh_dentry)) { err = PTR_ERR(hidden_wh_dentry); goto out; } PASSERT(hidden_wh_dentry->d_inode); down(&hidden_wh_dentry->d_inode->i_sem); newattrs.ia_valid = ATTR_CTIME; if (hidden_wh_dentry->d_inode->i_size != 0) { newattrs.ia_valid |= ATTR_SIZE; newattrs.ia_size = 0; } /* We discard this error, because the entry is whited out * even if we fail here. */ notify_change(hidden_wh_dentry, &newattrs); up(&hidden_wh_dentry->d_inode->i_sem); DPUT(hidden_wh_dentry); } if (err) { if (dbstart(dentry) == 0) goto out; /* exit if the error returned was NOT -EROFS */ if (!IS_COPYUP_ERR(err)) goto out; err = create_whiteout(dentry, dbstart(dentry) - 1); } else { fist_copy_attr_all(dir, hidden_new_dir_dentry->d_inode); } out: KFREE(name); print_exit_status(err); return err; }
/* * THIS IS A BOOLEAN FUNCTION: returns 1 if valid, 0 otherwise. */ int unionfs_d_revalidate(struct dentry *dentry, struct nameidata *nd) { int valid = 1; /* default is valid (1); invalid is 0. */ struct dentry *hidden_dentry; int bindex, bstart, bend; int sbgen, dgen; int positive = 0; int locked = 0; int restart = 0; int interpose_flag; print_util_entry_location(); restart: verify_locked(dentry); /* if the dentry is unhashed, do NOT revalidate */ if (d_deleted(dentry)) { fist_dprint(6, "unhashed dentry being revalidated: %*s\n", dentry->d_name.len, dentry->d_name.name); goto out; } BUG_ON(dbstart(dentry) == -1); if (dentry->d_inode) positive = 1; dgen = atomic_read(&dtopd(dentry)->udi_generation); sbgen = atomic_read(&stopd(dentry->d_sb)->usi_generation); /* If we are working on an unconnected dentry, then there is no * revalidation to be done, because this file does not exist within the * namespace, and Unionfs operates on the namespace, not data. */ if (sbgen != dgen) { struct dentry *result; int pdgen; unionfs_read_lock(dentry->d_sb); locked = 1; /* The root entry should always be valid */ BUG_ON(IS_ROOT(dentry)); /* We can't work correctly if our parent isn't valid. */ pdgen = atomic_read(&dtopd(dentry->d_parent)->udi_generation); if (!restart && (pdgen != sbgen)) { unionfs_read_unlock(dentry->d_sb); locked = 0; /* We must be locked before our parent. */ if (! (dentry->d_parent->d_op-> d_revalidate(dentry->d_parent, nd))) { valid = 0; goto out; } restart = 1; goto restart; } BUG_ON(pdgen != sbgen); /* Free the pointers for our inodes and this dentry. */ bstart = dbstart(dentry); bend = dbend(dentry); if (bstart >= 0) { struct dentry *hidden_dentry; for (bindex = bstart; bindex <= bend; bindex++) { hidden_dentry = dtohd_index_nocheck(dentry, bindex); if (!hidden_dentry) continue; DPUT(hidden_dentry); } } set_dbstart(dentry, -1); set_dbend(dentry, -1); interpose_flag = INTERPOSE_REVAL_NEG; if (positive) { interpose_flag = INTERPOSE_REVAL; down(&dentry->d_inode->i_sem); bstart = ibstart(dentry->d_inode); bend = ibend(dentry->d_inode); if (bstart >= 0) { struct inode *hidden_inode; for (bindex = bstart; bindex <= bend; bindex++) { hidden_inode = itohi_index(dentry->d_inode, bindex); if (!hidden_inode) continue; IPUT(hidden_inode); } } KFREE(itohi_ptr(dentry->d_inode)); itohi_ptr(dentry->d_inode) = NULL; ibstart(dentry->d_inode) = -1; ibend(dentry->d_inode) = -1; up(&dentry->d_inode->i_sem); } result = unionfs_lookup_backend(dentry, interpose_flag); if (result) { if (IS_ERR(result)) { valid = 0; goto out; } /* current unionfs_lookup_backend() doesn't return a valid dentry */ DPUT(dentry); dentry = result; } if (positive && itopd(dentry->d_inode)->uii_stale) { make_stale_inode(dentry->d_inode); d_drop(dentry); valid = 0; goto out; } goto out; } /* The revalidation must occur across all branches */ bstart = dbstart(dentry); bend = dbend(dentry); BUG_ON(bstart == -1); for (bindex = bstart; bindex <= bend; bindex++) { hidden_dentry = dtohd_index(dentry, bindex); if (!hidden_dentry || !hidden_dentry->d_op || !hidden_dentry->d_op->d_revalidate) continue; if (!hidden_dentry->d_op->d_revalidate(hidden_dentry, nd)) valid = 0; } if (!dentry->d_inode) valid = 0; if (valid) fist_copy_attr_all(dentry->d_inode, itohi(dentry->d_inode)); out: if (locked) unionfs_read_unlock(dentry->d_sb); fist_print_dentry("revalidate out", dentry); print_util_exit_status(valid); return valid; }
/* 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; }
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_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry) { int err = 0; struct dentry *hidden_old_dentry = NULL; struct dentry *hidden_new_dentry = NULL; struct dentry *hidden_dir_dentry = NULL; struct dentry *whiteout_dentry; char *name = NULL; print_entry_location(); double_lock_dentry(new_dentry, old_dentry); hidden_new_dentry = dtohd(new_dentry); /* check if whiteout exists in the branch of new dentry, i.e. lookup * .wh.foo first. If present, delete it */ name = alloc_whname(new_dentry->d_name.name, new_dentry->d_name.len); if (IS_ERR(name)) { err = PTR_ERR(name); goto out; } whiteout_dentry = LOOKUP_ONE_LEN(name, hidden_new_dentry->d_parent, new_dentry->d_name.len + WHLEN); if (IS_ERR(whiteout_dentry)) { err = PTR_ERR(whiteout_dentry); goto out; } if (!whiteout_dentry->d_inode) { DPUT(whiteout_dentry); whiteout_dentry = NULL; } else { /* found a .wh.foo entry, unlink it and then call vfs_link() */ hidden_dir_dentry = lock_parent(whiteout_dentry); if (! (err = is_robranch_super(new_dentry->d_sb, dbstart(new_dentry)))) { err = vfs_unlink(hidden_dir_dentry->d_inode, whiteout_dentry); } fist_copy_attr_times(dir, hidden_dir_dentry->d_inode); dir->i_nlink = get_nlinks(dir); unlock_dir(hidden_dir_dentry); hidden_dir_dentry = NULL; DPUT(whiteout_dentry); if (err) goto out; } if (dbstart(old_dentry) != dbstart(new_dentry)) { hidden_new_dentry = create_parents(dir, new_dentry, dbstart(old_dentry)); err = PTR_ERR(hidden_new_dentry); if (IS_COPYUP_ERR(err)) goto docopyup; if (!hidden_new_dentry || IS_ERR(hidden_new_dentry)) goto out; } hidden_new_dentry = dtohd(new_dentry); hidden_old_dentry = dtohd(old_dentry); BUG_ON(dbstart(old_dentry) != dbstart(new_dentry)); hidden_dir_dentry = lock_parent(hidden_new_dentry); if (!(err = is_robranch(old_dentry))) err = vfs_link(hidden_old_dentry, hidden_dir_dentry->d_inode, hidden_new_dentry); unlock_dir(hidden_dir_dentry); docopyup: if (IS_COPYUP_ERR(err)) { int old_bstart = dbstart(old_dentry); int bindex; for (bindex = old_bstart - 1; bindex >= 0; bindex--) { err = copyup_dentry(old_dentry->d_parent-> d_inode, old_dentry, old_bstart, bindex, NULL, old_dentry->d_inode->i_size); if (!err) { hidden_new_dentry = create_parents(dir, new_dentry, bindex); hidden_old_dentry = dtohd(old_dentry); hidden_dir_dentry = lock_parent(hidden_new_dentry); /* do vfs_link */ err = vfs_link(hidden_old_dentry, hidden_dir_dentry->d_inode, hidden_new_dentry); unlock_dir(hidden_dir_dentry); goto check_link; } } goto out; } check_link: if (err || !hidden_new_dentry->d_inode) goto out; /* Its a hard link, so use the same inode */ new_dentry->d_inode = IGRAB(old_dentry->d_inode); d_instantiate(new_dentry, new_dentry->d_inode); fist_copy_attr_all(dir, hidden_new_dentry->d_parent->d_inode); /* propagate number of hard-links */ old_dentry->d_inode->i_nlink = get_nlinks(old_dentry->d_inode); out: if (!new_dentry->d_inode) d_drop(new_dentry); KFREE(name); unlock_dentry(new_dentry); unlock_dentry(old_dentry); print_exit_status(err); return err; }