STATIC int mini_fo_d_hash(dentry_t *dentry, qstr_t *name) { int err = 0; dentry_t *hidden_dentry; dentry_t *hidden_sto_dentry; /* hidden_dentry = mini_fo_hidden_dentry(dentry); * hidden_sto_dentry = mini_fo_hidden_sto_dentry(dentry); */ /* state 1, 3, 4, 5: build the hash for the storage dentry */ if((dtopd(dentry)->state == MODIFIED) || (dtopd(dentry)->state == CREATED) || (dtopd(dentry)->state == DEL_REWRITTEN) || (dtopd(dentry)->state == DELETED)) { hidden_sto_dentry = dtohd2(dentry); if(hidden_sto_dentry && hidden_sto_dentry->d_op && hidden_sto_dentry->d_op->d_hash) { err = hidden_sto_dentry->d_op->d_hash(hidden_sto_dentry, name); } goto out; } /* state 2: build the hash for the base dentry */ if(dtopd(dentry)->state == UNMODIFIED) { hidden_dentry = dtohd(dentry); if(hidden_dentry && hidden_dentry->d_op && hidden_dentry->d_op->d_hash) { err = hidden_dentry->d_op->d_hash(hidden_dentry, name); } goto out; } /* state 6: build hash for the dentry that exists */ if(dtopd(dentry)->state == NON_EXISTANT) { hidden_sto_dentry = dtohd2(dentry); if(hidden_sto_dentry && hidden_sto_dentry->d_op && hidden_sto_dentry->d_op->d_hash) { err = hidden_sto_dentry->d_op->d_hash(hidden_sto_dentry, name); goto out; } hidden_dentry = dtohd(dentry); if(hidden_dentry && hidden_dentry->d_op && hidden_dentry->d_op->d_hash) { err = hidden_dentry->d_op->d_hash(hidden_dentry, name); goto out; } } printk(KERN_CRIT "mini_fo: d_hash: invalid state detected.\n"); out: 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; }
void mini_fo_d_release(dentry_t *dentry) { dentry_t *hidden_dentry; dentry_t *hidden_sto_dentry; /* this could be a negative dentry, so check first */ if (!dtopd(dentry)) { printk(KERN_CRIT "mini_fo_d_release: no private data.\n"); goto out; } hidden_dentry = dtohd(dentry); hidden_sto_dentry = dtohd2(dentry); if(hidden_dentry) { /* decrement hidden dentry's counter and free its inode */ dput(hidden_dentry); } if(hidden_sto_dentry) { /* decrement hidden dentry's counter and free its inode */ dput(hidden_sto_dentry); } /* free private data (mini_fo_dentry_info) here */ kfree(dtopd(dentry)); __dtopd(dentry) = NULL; /* just to be safe */ out: return; }
/* 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 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; }
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; }
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; }
void kdb3fs_d_release(dentry_t *dentry) { dentry_t *hidden_dentry; print_entry_location(); #if 0 fist_print_dentry("kdb3fs_d_release IN dentry", dentry); /* 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; } hidden_dentry = dtohd(dentry); fist_print_dentry("kdb3fs_d_release IN hidden_dentry", hidden_dentry); if (hidden_dentry->d_inode) fist_dprint(6, "kdb3fs_d_release: hidden_inode->i_count %d, i_num %lu.\n", atomic_read(&hidden_dentry->d_inode->i_count), hidden_dentry->d_inode->i_ino); /* free private data (kdb3fs_dentry_info) here */ KFREE(dtopd(dentry)); dtopd(dentry) = NULL; /* just to be safe */ /* decrement hidden dentry's counter and free its inode */ dput(hidden_dentry); #endif out: print_exit_location(); }
int mini_fo_d_delete(dentry_t *dentry) { dentry_t *hidden_dentry; dentry_t *hidden_sto_dentry; int err = 0; /* this could be a negative dentry, so check first */ if (!dtopd(dentry)) { printk(KERN_CRIT "mini_fo_d_delete: negative dentry passed.\n"); goto out; } hidden_dentry = dtohd(dentry); hidden_sto_dentry = dtohd2(dentry); if(hidden_dentry) { if(hidden_dentry->d_op && hidden_dentry->d_op->d_delete) { err = hidden_dentry->d_op->d_delete(hidden_dentry); } } if(hidden_sto_dentry) { if(hidden_sto_dentry->d_op && hidden_sto_dentry->d_op->d_delete) { err = hidden_sto_dentry->d_op->d_delete(hidden_sto_dentry); } } out: return err; }
STATIC int mini_fo_d_compare(dentry_t *dentry, qstr_t *a, qstr_t *b) { int err; dentry_t *hidden_dentry=NULL; /* hidden_dentry = mini_fo_hidden_dentry(dentry); */ if(dtohd2(dentry)) hidden_dentry = dtohd2(dentry); else if(dtohd(dentry)) hidden_dentry = dtohd(dentry); if (hidden_dentry && hidden_dentry->d_op && hidden_dentry->d_op->d_compare) { err = hidden_dentry->d_op->d_compare(hidden_dentry, a, b); } else { err = ((a->len != b->len) || memcmp(a->name, b->name, b->len)); } return err; }
mini_fo_d_revalidate(dentry_t *dentry, int flags) #endif { int err1 = 1; /* valid = 1, invalid = 0 */ int err2 = 1; dentry_t *hidden_dentry; dentry_t *hidden_sto_dentry; check_mini_fo_dentry(dentry); hidden_dentry = dtohd(dentry); hidden_sto_dentry = dtohd2(dentry); if(hidden_dentry && hidden_dentry->d_op && hidden_dentry->d_op->d_revalidate) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) err1 = hidden_dentry->d_op->d_revalidate(hidden_dentry, nd); #else err1 = hidden_dentry->d_op->d_revalidate(hidden_dentry, flags); #endif } if(hidden_sto_dentry && hidden_sto_dentry->d_op && hidden_sto_dentry->d_op->d_revalidate) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) err2 = hidden_sto_dentry->d_op->d_revalidate(hidden_sto_dentry, nd); #else err2 = hidden_sto_dentry->d_op->d_revalidate(hidden_sto_dentry, flags); #endif } /* mk: if one of the lower level dentries are valid, * the mini_fo dentry is too. */ return (err1 || err2); }
static int unionfs_rmdir_first(struct inode *dir, struct dentry *dentry, struct unionfs_dir_state *namelist) { int err; struct dentry *hidden_dentry; struct dentry *hidden_dir_dentry = NULL; print_entry_location(); fist_print_dentry("IN unionfs_rmdir_first: ", dentry); /* Here we need to remove whiteout entries. */ err = delete_whiteouts(dentry, dbstart(dentry), namelist); if (err) { goto out; } hidden_dentry = dtohd(dentry); PASSERT(hidden_dentry); 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(dentry))) { err = vfs_rmdir(hidden_dir_dentry->d_inode, hidden_dentry); } DPUT(hidden_dentry); fist_copy_attr_times(dir, hidden_dir_dentry->d_inode); /* propagate number of hard-links */ dentry->d_inode->i_nlink = get_nlinks(dentry->d_inode); out: if (hidden_dir_dentry) { unlock_dir(hidden_dir_dentry); } fist_print_dentry("OUT unionfs_rmdir_first: ", dentry); print_exit_status(err); return err; }
/* BKL held by caller. * dentry->d_inode->i_sem down * ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t); */ ssize_t unionfs_getxattr(struct dentry *dentry, const char *name, void *value, size_t size) { struct dentry *hidden_dentry = NULL; int err = -EOPNOTSUPP; /* Define these anyway so we don't need as much ifdef'ed code. */ char *encoded_name = NULL; char *encoded_value = NULL; print_entry_location(); lock_dentry(dentry); hidden_dentry = dtohd(dentry); fist_dprint(8, "getxattr: name=\"%s\", value %lu bytes\n", name, (unsigned long)size); if (hidden_dentry->d_inode->i_op->getxattr) { encoded_name = (char *)name; encoded_value = (char *)value; down(&hidden_dentry->d_inode->i_sem); /* lock_kernel() already done by caller. */ err = hidden_dentry->d_inode->i_op->getxattr(hidden_dentry, encoded_name, encoded_value, size); /* unlock_kernel() will be done by caller. */ up(&hidden_dentry->d_inode->i_sem); } unlock_dentry(dentry); print_exit_status(err); return err; }
int create_sto_reg_file(dentry_t *dentry, int mode) #endif { int err = 0; inode_t *dir; dentry_t *hidden_sto_dentry; dentry_t *hidden_sto_dir_dentry; if(exists_in_storage(dentry)) { printk(KERN_CRIT "mini_fo: create_sto_file: wrong type or state.\n"); err = -EINVAL; goto out; } err = get_neg_sto_dentry(dentry); if (err) { printk(KERN_CRIT "mini_fo: create_sto_file: ERROR getting neg. sto dentry.\n"); goto out; } dir = dentry->d_parent->d_inode; hidden_sto_dentry = dtohd2(dentry); /* lock parent */ hidden_sto_dir_dentry = dget(hidden_sto_dentry->d_parent); err = PTR_ERR(hidden_sto_dir_dentry); if (IS_ERR(hidden_sto_dir_dentry)) goto out; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) err = vfs_create(hidden_sto_dir_dentry->d_inode, hidden_sto_dentry, mode, nd); #else err = vfs_create(hidden_sto_dir_dentry->d_inode, hidden_sto_dentry, mode); #endif if(err) { printk(KERN_CRIT "mini_fo: create_sto_file: ERROR creating sto file.\n"); goto out_lock; } if(!dtohd2(dentry)->d_inode) { printk(KERN_CRIT "mini_fo: create_sto_file: ERROR creating sto file [2].\n"); err = -EINVAL; goto out_lock; } /* interpose the new inode */ if(dtost(dentry) == DELETED) { dtost(dentry) = DEL_REWRITTEN; err = mini_fo_tri_interpose(NULL, hidden_sto_dentry, dentry, dir->i_sb, 0); if(err) goto out_lock; } else if(dtost(dentry) == NON_EXISTANT) { dtost(dentry) = CREATED; err = mini_fo_tri_interpose(dtohd(dentry), hidden_sto_dentry, dentry, dir->i_sb, 0); if(err) goto out_lock; } else if(dtost(dentry) == UNMODIFIED) { dtost(dentry) = MODIFIED; /* interpose on new inode */ if(itohi2(dentry->d_inode) != NULL) { printk(KERN_CRIT "mini_fo: create_sto_file: invalid inode detected.\n"); err = -EINVAL; goto out_lock; } itohi2(dentry->d_inode) = igrab(dtohd2(dentry)->d_inode); } fist_copy_attr_timesizes(dentry->d_parent->d_inode, hidden_sto_dir_dentry->d_inode); out_lock: dput(hidden_sto_dir_dentry); out: return err; }
int create_sto_nod(dentry_t *dentry, int mode, int dev) #endif { int err = 0; inode_t *dir; dentry_t *hidden_sto_dentry; dentry_t *hidden_sto_dir_dentry; if(exists_in_storage(dentry)) { err = -EEXIST; goto out; } err = get_neg_sto_dentry(dentry); if (err) { printk(KERN_CRIT "mini_fo: create_sto_nod: ERROR getting neg. sto dentry.\n"); goto out; } dir = dentry->d_parent->d_inode; hidden_sto_dentry = dtohd2(dentry); /* lock parent */ hidden_sto_dir_dentry = dget(hidden_sto_dentry->d_parent); err = PTR_ERR(hidden_sto_dir_dentry); if (IS_ERR(hidden_sto_dir_dentry)) goto out; err = vfs_mknod(hidden_sto_dir_dentry->d_inode, hidden_sto_dentry, mode, dev); if(err) goto out_lock; if(!dtohd2(dentry)->d_inode) { printk(KERN_CRIT "mini_fo: create_sto_nod: creating storage inode failed [1].\n"); err = -EINVAL; /* return something indicating failure */ goto out_lock; } /* interpose the new inode */ if(dtost(dentry) == DELETED) { dtost(dentry) = DEL_REWRITTEN; err = mini_fo_tri_interpose(NULL, hidden_sto_dentry, dentry, dir->i_sb, 0); if(err) goto out_lock; } else if(dtost(dentry) == NON_EXISTANT) { dtost(dentry) = CREATED; err = mini_fo_tri_interpose(dtohd(dentry), hidden_sto_dentry, dentry, dir->i_sb, 0); if(err) goto out_lock; } else if(dtost(dentry) == UNMODIFIED) { dtost(dentry) = MODIFIED; /* interpose on new inode */ if(itohi2(dentry->d_inode) != NULL) { printk(KERN_CRIT "mini_fo: create_sto_nod: error, invalid inode detected.\n"); err = -EINVAL; goto out_lock; } itohi2(dentry->d_inode) = igrab(dtohd2(dentry)->d_inode); } fist_copy_attr_timesizes(dir, hidden_sto_dir_dentry->d_inode); out_lock: dput(hidden_sto_dir_dentry); out: return err; }
/* create the sto dir, setup states */ int create_sto_dir(dentry_t *dentry, int mode) { int err = 0; inode_t *dir; dentry_t *hidden_sto_dentry; dentry_t *hidden_sto_dir_dentry; /* had to take the "!S_ISDIR(mode))" check out, because it failed */ if(exists_in_storage(dentry)) { printk(KERN_CRIT "mini_fo: create_sto_dir: wrong type or state.\\ n"); err = -EINVAL; goto out; } err = get_neg_sto_dentry(dentry); if(err) { err = -EINVAL; goto out; } dir = dentry->d_parent->d_inode; hidden_sto_dentry = dtohd2(dentry); /* was: hidden_sto_dir_dentry = lock_parent(hidden_sto_dentry); */ hidden_sto_dir_dentry = dget(hidden_sto_dentry->d_parent); err = PTR_ERR(hidden_sto_dir_dentry); if (IS_ERR(hidden_sto_dir_dentry)) goto out; err = vfs_mkdir(hidden_sto_dir_dentry->d_inode, hidden_sto_dentry, mode); if(err) { printk(KERN_CRIT "mini_fo: create_sto_dir: ERROR creating sto dir.\n"); goto out_lock; } if(!dtohd2(dentry)->d_inode) { printk(KERN_CRIT "mini_fo: create_sto_dir: ERROR creating sto dir [2].\n"); err = -EINVAL; goto out_lock; } /* interpose the new inode */ if(dtost(dentry) == DELETED) { dtost(dentry) = DEL_REWRITTEN; err = mini_fo_tri_interpose(NULL, hidden_sto_dentry, dentry, dir->i_sb, 0); if(err) goto out_lock; } else if(dtopd(dentry)->state == NON_EXISTANT) { dtopd(dentry)->state = CREATED; err = mini_fo_tri_interpose(dtohd(dentry), hidden_sto_dentry, dentry, dir->i_sb, 0); if(err) goto out_lock; } else if(dtopd(dentry)->state == UNMODIFIED) { dtopd(dentry)->state = MODIFIED; /* interpose on new inode */ if(itohi2(dentry->d_inode) != NULL) { printk(KERN_CRIT "mini_fo: create_sto_dir: ERROR, invalid inode detected.\n"); err = -EINVAL; goto out_lock; } itohi2(dentry->d_inode) = igrab(dtohd2(dentry)->d_inode); } fist_copy_attr_timesizes(dir, hidden_sto_dir_dentry->d_inode); /* initalize the wol list */ itopd(dentry->d_inode)->deleted_list_size = -1; itopd(dentry->d_inode)->renamed_list_size = -1; meta_build_lists(dentry); out_lock: /* was: unlock_dir(hidden_sto_dir_dentry); */ dput(hidden_sto_dir_dentry); out: 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; }
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 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_mkdir(struct inode *parent, struct dentry *dentry, int mode) { int err = 0; struct dentry *hidden_dentry = NULL, *whiteout_dentry = NULL; struct dentry *hidden_parent_dentry = NULL; int bindex = 0, bstart; char *name = NULL; int whiteout_unlinked = 0; uid_t saved_uid = current->fsuid; gid_t saved_gid = current->fsgid; print_entry_location(); lock_dentry(dentry); fist_print_dentry("IN unionfs_mkdir", dentry); 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); goto out; } if (!whiteout_dentry->d_inode) { DPUT(whiteout_dentry); whiteout_dentry = NULL; } else { hidden_parent_dentry = lock_parent(whiteout_dentry); /* Set the uid and gid to trick the fs into allowing us to create * the file */ current->fsuid = hidden_parent_dentry->d_inode->i_uid; current->fsgid = hidden_parent_dentry->d_inode->i_gid; //found a.wh.foo entry, remove it then do vfs_mkdir if (!(err = is_robranch_super(dentry->d_sb, bstart))) { err = vfs_unlink(hidden_parent_dentry->d_inode, whiteout_dentry); } DPUT(whiteout_dentry); current->fsuid = saved_uid; current->fsgid = saved_gid; unlock_dir(hidden_parent_dentry); if (err) { /* exit if the error returned was NOT -EROFS */ if (!IS_COPYUP_ERR(err)) goto out; bstart--; } else { whiteout_unlinked = 1; } } for (bindex = bstart; bindex >= 0; bindex--) { hidden_dentry = dtohd_index(dentry, bindex); if (!hidden_dentry) { hidden_dentry = create_parents(parent, dentry, bindex); if (!hidden_dentry || IS_ERR(hidden_dentry)) { fist_dprint(8, "hidden dentry NULL for bindex = %d\n", bindex); continue; } } hidden_parent_dentry = lock_parent(hidden_dentry); if (IS_ERR(hidden_parent_dentry)) { err = PTR_ERR(hidden_parent_dentry); goto out; } if (!(err = is_robranch_super(dentry->d_sb, bindex))) { err = vfs_mkdir(hidden_parent_dentry->d_inode, hidden_dentry, mode); } unlock_dir(hidden_parent_dentry); /* XXX this could potentially return a negative hidden_dentry! */ if (err || !hidden_dentry->d_inode) { /* break out of for loop if error returned was NOT -EROFS */ if (!IS_COPYUP_ERR(err)) break; } else { int i; int bend = dbend(dentry); for (i = bindex + 1; i < bend; i++) { if (dtohd_index(dentry, i)) { DPUT(dtohd_index(dentry, i)); set_dtohd_index(dentry, i, NULL); } } bend = bindex; set_dbend(dentry, bend); 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); } whiteout_dentry = LOOKUP_ONE_LEN(UNIONFS_DIR_OPAQUE, hidden_dentry, sizeof (UNIONFS_DIR_OPAQUE) - 1); if (IS_ERR(whiteout_dentry)) { err = PTR_ERR(whiteout_dentry); goto out; } down(&hidden_dentry->d_inode->i_sem); err = vfs_create(hidden_dentry->d_inode, whiteout_dentry, 0600, NULL); up(&hidden_dentry->d_inode->i_sem); DPUT(whiteout_dentry); if (err) { fist_dprint(8, "mkdir: error creating directory override entry: %d\n", err); goto out; } break; } } out: if (!dentry->d_inode) d_drop(dentry); KFREE(name); fist_print_dentry("OUT unionfs_mkdir :", dentry); unlock_dentry(dentry); print_exit_status(err); return err; }
static int unionfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) { int err = 0; struct dentry *hidden_dentry = NULL; struct dentry *whiteout_dentry = NULL; struct dentry *hidden_dir_dentry = NULL; umode_t mode; int bindex = 0, bstart; char *name = NULL; print_entry_location(); lock_dentry(dentry); fist_print_dentry("IN unionfs_symlink", 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. If present, delete it */ 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); 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_symlink() */ hidden_dir_dentry = lock_parent(whiteout_dentry); fist_print_generic_dentry("HDD", hidden_dir_dentry); fist_print_generic_dentry("WD", whiteout_dentry); if (!(err = is_robranch_super(dentry->d_sb, bstart))) { err = vfs_unlink(hidden_dir_dentry->d_inode, whiteout_dentry); } DPUT(whiteout_dentry); fist_copy_attr_times(dir, hidden_dir_dentry->d_inode); /* propagate number of hard-links */ dir->i_nlink = get_nlinks(dir); unlock_dir(hidden_dir_dentry); if (err) { /* exit if the error returned was NOT -EROFS */ if (!IS_COPYUP_ERR(err)) goto out; /* should now try to create symlink in the another branch */ bstart--; } } /* deleted whiteout if it was present, now do a normal vfs_symlink() with possible recursive directory creation */ 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(dir, dentry, bindex); if (!hidden_dentry || IS_ERR(hidden_dentry)) { if (IS_ERR(hidden_dentry)) { err = PTR_ERR(hidden_dentry); } fist_dprint(8, "hidden dentry NULL (or error) for bindex = %d\n", bindex); continue; } } hidden_dir_dentry = lock_parent(hidden_dentry); if (!(err = is_robranch_super(dentry->d_sb, bindex))) { mode = S_IALLUGO; err = vfs_symlink(hidden_dir_dentry->d_inode, hidden_dentry, symname, mode); } unlock_dir(hidden_dir_dentry); if (err || !hidden_dentry->d_inode) { /* break out of for loop if error returned was NOT -EROFS */ if (!IS_COPYUP_ERR(err)) break; } else { err = unionfs_interpose(dentry, dir->i_sb, 0); if (!err) { fist_copy_attr_timesizes(dir, hidden_dir_dentry-> d_inode); /* update number of links on parent directory */ dir->i_nlink = get_nlinks(dir); } break; } } out: if (!dentry->d_inode) d_drop(dentry); KFREE(name); fist_print_dentry("OUT unionfs_symlink :", dentry); unlock_dentry(dentry); print_exit_status(err); return err; }
static int unionfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) { int err = 0; struct dentry *hidden_dentry = NULL, *whiteout_dentry = NULL; struct dentry *hidden_parent_dentry = NULL; int bindex = 0, bstart; char *name = NULL; int whiteout_unlinked = 0; print_entry_location(); lock_dentry(dentry); fist_print_dentry("IN unionfs_mknod", dentry); 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); goto out; } if (!whiteout_dentry->d_inode) { DPUT(whiteout_dentry); whiteout_dentry = NULL; } else { /* found .wh.foo, unlink it */ hidden_parent_dentry = lock_parent(whiteout_dentry); //found a.wh.foo entry, remove it then do vfs_mkdir if (!(err = is_robranch_super(dentry->d_sb, bstart))) err = vfs_unlink(hidden_parent_dentry->d_inode, whiteout_dentry); DPUT(whiteout_dentry); unlock_dir(hidden_parent_dentry); if (err) { if (!IS_COPYUP_ERR(err)) goto out; bstart--; } else { whiteout_unlinked = 1; } } for (bindex = bstart; bindex >= 0; bindex--) { hidden_dentry = dtohd_index(dentry, bindex); if (!hidden_dentry) { hidden_dentry = create_parents(dir, dentry, bindex); if (!hidden_dentry || IS_ERR(hidden_dentry)) { fist_dprint(8, "hidden dentry NULL for bindex = %d\n", bindex); continue; } } hidden_parent_dentry = lock_parent(hidden_dentry); if (IS_ERR(hidden_parent_dentry)) { err = PTR_ERR(hidden_parent_dentry); goto out; } if (!(err = is_robranch_super(dentry->d_sb, bindex))) { err = vfs_mknod(hidden_parent_dentry->d_inode, hidden_dentry, mode, dev); } /* XXX this could potentially return a negative hidden_dentry! */ if (err || !hidden_dentry->d_inode) { unlock_dir(hidden_parent_dentry); /* break out of for, if error was NOT -EROFS */ if (!IS_COPYUP_ERR(err)) break; } else { err = unionfs_interpose(dentry, dir->i_sb, 0); if (!err) { fist_copy_attr_timesizes(dir, hidden_parent_dentry-> d_inode); /* update number of links on parent directory */ dir->i_nlink = get_nlinks(dir); } unlock_dir(hidden_parent_dentry); break; } } out: if (!dentry->d_inode) d_drop(dentry); if (name) { KFREE(name); } fist_print_dentry("OUT unionfs_mknod :", dentry); unlock_dentry(dentry); 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; }
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; }