static int ecryptfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) { int rc; struct dentry *lower_dentry; struct dentry *lower_dir_dentry; lower_dentry = ecryptfs_dentry_to_lower(dentry); lower_dir_dentry = lock_parent(lower_dentry); rc = vfs_mkdir(lower_dir_dentry->d_inode, lower_dentry, mode); if (rc || !lower_dentry->d_inode) goto out; rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb); if (rc) goto out; fsstack_copy_attr_times(dir, lower_dir_dentry->d_inode); fsstack_copy_inode_size(dir, lower_dir_dentry->d_inode); set_nlink(dir, lower_dir_dentry->d_inode->i_nlink); out: unlock_dir(lower_dir_dentry); if (!dentry->d_inode) d_drop(dentry); return rc; }
static int ecryptfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev) { int rc; struct dentry *lower_dentry; struct dentry *lower_dir_dentry; lower_dentry = ecryptfs_dentry_to_lower(dentry); lower_dir_dentry = lock_parent(lower_dentry); rc = vfs_mknod(d_inode(lower_dir_dentry), lower_dentry, mode, dev); if (rc || d_really_is_negative(lower_dentry)) goto out; rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb); if (rc) goto out; fsstack_copy_attr_times(dir, d_inode(lower_dir_dentry)); fsstack_copy_inode_size(dir, d_inode(lower_dir_dentry)); out: unlock_dir(lower_dir_dentry); if (d_really_is_negative(dentry)) d_drop(dentry); return rc; }
static int scfs_do_unlink(struct inode *dir, struct dentry *dentry, struct inode *inode) { struct dentry *lower_dentry = scfs_lower_dentry(dentry); struct inode *lower_dir_inode = scfs_lower_inode(dir); struct dentry *lower_dir_dentry; int ret; dget(lower_dentry); lower_dir_dentry = lock_parent(lower_dentry); ret = vfs_unlink(lower_dir_inode, lower_dentry); if (ret) { SCFS_PRINT_ERROR("error in vfs_unlink, ret : %d\n", ret); goto out; } fsstack_copy_attr_times(dir, lower_dir_inode); set_nlink(inode, scfs_lower_inode(inode)->i_nlink); inode->i_ctime = dir->i_ctime; d_drop(dentry); out: unlock_dir(lower_dir_dentry); dput(lower_dentry); return ret; }
/** * ecryptfs_do_create * @directory_inode: inode of the new file's dentry's parent in ecryptfs * @ecryptfs_dentry: New file's dentry in ecryptfs * @mode: The mode of the new file * @nd: nameidata of ecryptfs' parent's dentry & vfsmount * * Creates the underlying file and the eCryptfs inode which will link to * it. It will also update the eCryptfs directory inode to mimic the * stat of the lower directory inode. * * Returns the new eCryptfs inode on success; an ERR_PTR on error condition */ static struct inode * ecryptfs_do_create(struct inode *directory_inode, struct dentry *ecryptfs_dentry, umode_t mode) { int rc; struct dentry *lower_dentry; struct dentry *lower_dir_dentry; struct inode *inode; lower_dentry = ecryptfs_dentry_to_lower(ecryptfs_dentry); lower_dir_dentry = lock_parent(lower_dentry); if (IS_ERR(lower_dir_dentry)) { ecryptfs_printk(KERN_ERR, "Error locking directory of " "dentry\n"); inode = ERR_CAST(lower_dir_dentry); goto out; } rc = vfs_create(lower_dir_dentry->d_inode, lower_dentry, mode, NULL); if (rc) { printk(KERN_ERR "%s: Failure to create dentry in lower fs; " "rc = [%d]\n", __func__, rc); inode = ERR_PTR(rc); goto out_lock; } inode = __ecryptfs_get_inode(lower_dentry->d_inode, directory_inode->i_sb); if (IS_ERR(inode)) { vfs_unlink(lower_dir_dentry->d_inode, lower_dentry); goto out_lock; } fsstack_copy_attr_times(directory_inode, lower_dir_dentry->d_inode); fsstack_copy_inode_size(directory_inode, lower_dir_dentry->d_inode); out_lock: unlock_dir(lower_dir_dentry); out: return inode; }
/** * ecryptfs_do_create * @directory_inode: inode of the new file's dentry's parent in ecryptfs * @ecryptfs_dentry: New file's dentry in ecryptfs * @mode: The mode of the new file * @nd: nameidata of ecryptfs' parent's dentry & vfsmount * * Creates the underlying file and the eCryptfs inode which will link to * it. It will also update the eCryptfs directory inode to mimic the * stat of the lower directory inode. * * Returns zero on success; non-zero on error condition */ static int ecryptfs_do_create(struct inode *directory_inode, struct dentry *ecryptfs_dentry, int mode, struct nameidata *nd) { int rc; struct dentry *lower_dentry; struct dentry *lower_dir_dentry; lower_dentry = ecryptfs_dentry_to_lower(ecryptfs_dentry); lower_dir_dentry = lock_parent(lower_dentry); if (IS_ERR(lower_dir_dentry)) { ecryptfs_printk(KERN_ERR, "Error locking directory of " "dentry\n"); rc = PTR_ERR(lower_dir_dentry); goto out; } rc = ecryptfs_create_underlying_file(lower_dir_dentry->d_inode, ecryptfs_dentry, mode, nd); if (rc) { printk(KERN_ERR "%s: Failure to create dentry in lower fs; " "rc = [%d]\n", __func__, rc); goto out_lock; } rc = ecryptfs_interpose(lower_dentry, ecryptfs_dentry, directory_inode->i_sb); if (rc) { ecryptfs_printk(KERN_ERR, "Failure in ecryptfs_interpose\n"); goto out_lock; } fsstack_copy_attr_times(directory_inode, lower_dir_dentry->d_inode); fsstack_copy_inode_size(directory_inode, lower_dir_dentry->d_inode); out_lock: unlock_dir(lower_dir_dentry); out: return rc; }
static int ecryptfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) { int rc; struct dentry *lower_dentry; struct dentry *lower_dir_dentry; char *encoded_symname; int encoded_symlen; struct ecryptfs_crypt_stat *crypt_stat = NULL; lower_dentry = ecryptfs_dentry_to_lower(dentry); dget(lower_dentry); lower_dir_dentry = lock_parent(lower_dentry); encoded_symlen = ecryptfs_encode_filename(crypt_stat, symname, strlen(symname), &encoded_symname); if (encoded_symlen < 0) { rc = encoded_symlen; goto out_lock; } rc = vfs_symlink(lower_dir_dentry->d_inode, lower_dentry, encoded_symname); kfree(encoded_symname); if (rc || !lower_dentry->d_inode) goto out_lock; rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb, 0); if (rc) goto out_lock; fsstack_copy_attr_times(dir, lower_dir_dentry->d_inode); fsstack_copy_inode_size(dir, lower_dir_dentry->d_inode); out_lock: unlock_dir(lower_dir_dentry); dput(lower_dentry); if (!dentry->d_inode) d_drop(dentry); return rc; }
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 wrapfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry) { struct dentry *lower_old_dentry; struct dentry *lower_new_dentry; struct dentry *lower_dir_dentry; u64 file_size_save; int err; struct path lower_old_path, lower_new_path; file_size_save = i_size_read(old_dentry->d_inode); wrapfs_get_lower_path(old_dentry, &lower_old_path); wrapfs_get_lower_path(new_dentry, &lower_new_path); lower_old_dentry = lower_old_path.dentry; lower_new_dentry = lower_new_path.dentry; lower_dir_dentry = lock_parent(lower_new_dentry); err = vfs_link(lower_old_dentry, lower_dir_dentry->d_inode, lower_new_dentry, NULL); if (err || !lower_new_dentry->d_inode) goto out; err = wrapfs_interpose(new_dentry, dir->i_sb, &lower_new_path); if (err) goto out; fsstack_copy_attr_times(dir, lower_new_dentry->d_inode); fsstack_copy_inode_size(dir, lower_new_dentry->d_inode); set_nlink(old_dentry->d_inode, wrapfs_lower_inode(old_dentry->d_inode)->i_nlink); i_size_write(new_dentry->d_inode, file_size_save); out: unlock_dir(lower_dir_dentry); wrapfs_put_lower_path(old_dentry, &lower_old_path); wrapfs_put_lower_path(new_dentry, &lower_new_path); return err; }
static int ecryptfs_rmdir(struct inode *dir, struct dentry *dentry) { struct dentry *lower_dentry; struct vfsmount *lower_mnt; struct dentry *lower_dir_dentry; int rc; lower_dentry = ecryptfs_dentry_to_lower(dentry); lower_mnt = ecryptfs_dentry_to_lower_mnt(dentry); dget(dentry); lower_dir_dentry = lock_parent(lower_dentry); dget(lower_dentry); rc = vfs_rmdir(lower_dir_dentry->d_inode, lower_dentry, lower_mnt); dput(lower_dentry); if (!rc) d_delete(lower_dentry); fsstack_copy_attr_times(dir, lower_dir_dentry->d_inode); dir->i_nlink = lower_dir_dentry->d_inode->i_nlink; unlock_dir(lower_dir_dentry); if (!rc) d_drop(dentry); dput(dentry); return rc; }
static int ecryptfs_unlink(struct inode *dir, struct dentry *dentry) { int rc = 0; struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry); struct inode *lower_dir_inode = ecryptfs_inode_to_lower(dir); struct dentry *lower_dir_dentry; dget(lower_dentry); lower_dir_dentry = lock_parent(lower_dentry); rc = vfs_unlink(lower_dir_inode, lower_dentry); if (rc) { printk(KERN_ERR "Error in vfs_unlink; rc = [%d]\n", rc); goto out_unlock; } fsstack_copy_attr_times(dir, lower_dir_inode); set_nlink(dentry->d_inode, ecryptfs_inode_to_lower(dentry->d_inode)->i_nlink); dentry->d_inode->i_ctime = dir->i_ctime; d_drop(dentry); out_unlock: unlock_dir(lower_dir_dentry); dput(lower_dentry); return rc; }
static int diaryfs_symlink(struct inode *dir, struct dentry *dentry, const char * symname) { int err; struct dentry *lower_dentry; struct dentry *lower_parent_dentry = NULL; struct path lower_path; diaryfs_get_lower_path(dentry, &lower_path); lower_dentry = lower_path.dentry; lower_parent_dentry = lock_parent(lower_dentry); err = vfs_symlink(lower_parent_dentry->d_inode, lower_dentry, symname); if (err) goto out; err = diaryfs_interpose(dentry, dir->i_sb, &lower_path); if (err) goto out; fsstack_copy_attr_times(dir, diaryfs_lower_inode(dir)); fsstack_copy_inode_size(dir, lower_parent_dentry->d_inode); out: unlock_dir(lower_parent_dentry); diaryfs_put_lower_path(dentry, &lower_path); return err; }
static int ecryptfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry) { struct dentry *lower_old_dentry; struct dentry *lower_new_dentry; struct dentry *lower_dir_dentry; u64 file_size_save; int rc; file_size_save = i_size_read(old_dentry->d_inode); lower_old_dentry = ecryptfs_dentry_to_lower(old_dentry); lower_new_dentry = ecryptfs_dentry_to_lower(new_dentry); dget(lower_old_dentry); dget(lower_new_dentry); lower_dir_dentry = lock_parent(lower_new_dentry); rc = vfs_link(lower_old_dentry, lower_dir_dentry->d_inode, lower_new_dentry); if (rc || !lower_new_dentry->d_inode) goto out_lock; rc = ecryptfs_interpose(lower_new_dentry, new_dentry, dir->i_sb, 0); if (rc) goto out_lock; fsstack_copy_attr_times(dir, lower_new_dentry->d_inode); fsstack_copy_inode_size(dir, lower_new_dentry->d_inode); old_dentry->d_inode->i_nlink = ecryptfs_inode_to_lower(old_dentry->d_inode)->i_nlink; i_size_write(new_dentry->d_inode, file_size_save); out_lock: unlock_dir(lower_dir_dentry); dput(lower_new_dentry); dput(lower_old_dentry); d_drop(lower_old_dentry); d_drop(new_dentry); d_drop(old_dentry); return rc; }
static int ecryptfs_rmdir(struct inode *dir, struct dentry *dentry) { struct dentry *lower_dentry; struct dentry *lower_dir_dentry; int rc; dentry_unhash(dentry); lower_dentry = ecryptfs_dentry_to_lower(dentry); dget(dentry); lower_dir_dentry = lock_parent(lower_dentry); dget(lower_dentry); rc = vfs_rmdir(lower_dir_dentry->d_inode, lower_dentry); dput(lower_dentry); if (!rc && dentry->d_inode) clear_nlink(dentry->d_inode); fsstack_copy_attr_times(dir, lower_dir_dentry->d_inode); set_nlink(dir, lower_dir_dentry->d_inode->i_nlink); unlock_dir(lower_dir_dentry); if (!rc) d_drop(dentry); dput(dentry); return rc; }
static int wrapfs_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *nd) { int err = 0; struct dentry *lower_dentry; struct dentry *lower_parent_dentry = NULL; struct path lower_path, saved_path; wrapfs_get_lower_path(dentry, &lower_path); lower_dentry = lower_path.dentry; lower_parent_dentry = lock_parent(lower_dentry); err = mnt_want_write(lower_path.mnt); if (err) goto out_unlock; pathcpy(&saved_path, &nd->path); pathcpy(&nd->path, &lower_path); err = vfs_create(lower_parent_dentry->d_inode, lower_dentry, mode, nd); pathcpy(&nd->path, &saved_path); if (err) goto out; err = wrapfs_interpose(dentry, dir->i_sb, &lower_path); if (err) goto out; fsstack_copy_attr_times(dir, wrapfs_lower_inode(dir)); fsstack_copy_inode_size(dir, lower_parent_dentry->d_inode); out: mnt_drop_write(lower_path.mnt); out_unlock: unlock_dir(lower_parent_dentry); wrapfs_put_lower_path(dentry, &lower_path); return err; }
static int unionfs_rename_whiteout(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry) { int err = 0; int bindex, bwh_old; int old_bstart, old_bend; int new_bstart, new_bend; int do_copyup = -1; struct dentry *parent_dentry; int local_err = 0; int eio = 0; int revert = 0; struct dentry *wh_old = NULL; print_entry_location(); old_bstart = dbstart(old_dentry); bwh_old = old_bstart; old_bend = dbend(old_dentry); parent_dentry = old_dentry->d_parent; new_bstart = dbstart(new_dentry); new_bend = dbend(new_dentry); /* Rename source to destination. */ err = do_rename(old_dir, old_dentry, new_dir, new_dentry, old_bstart, &wh_old); if (err) { if (!IS_COPYUP_ERR(err)) { goto out; } do_copyup = old_bstart - 1; } else { revert = 1; } /* Unlink all instances of destination that exist to the left of * bstart of source. On error, revert back, goto out. */ for (bindex = old_bstart - 1; bindex >= new_bstart; bindex--) { struct dentry *unlink_dentry; struct dentry *unlink_dir_dentry; unlink_dentry = dtohd_index(new_dentry, bindex); if (!unlink_dentry) { continue; } 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); /* propagate number of hard-links */ 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 != new_bstart) { DPUT(unlink_dentry); set_dtohd_index(new_dentry, bindex, NULL); } } else if (IS_COPYUP_ERR(err)) { do_copyup = bindex - 1; } else if (revert) { DPUT(wh_old); goto revert; } } if (do_copyup != -1) { for (bindex = do_copyup; bindex >= 0; bindex--) { /* copyup the file into some left directory, so that you can rename it */ err = copyup_dentry(old_dentry->d_parent->d_inode, old_dentry, old_bstart, bindex, NULL, old_dentry->d_inode->i_size); if (!err) { DPUT(wh_old); bwh_old = bindex; err = do_rename(old_dir, old_dentry, new_dir, new_dentry, bindex, &wh_old); break; } } } /* Create whiteout for source, only if: * (1) There is more than one underlying instance of source. * (2) We did a copy_up */ if ((old_bstart != old_bend) || (do_copyup != -1)) { struct dentry *hidden_parent; BUG_ON(!wh_old || IS_ERR(wh_old) || wh_old->d_inode || bwh_old < 0); hidden_parent = lock_parent(wh_old); local_err = vfs_create(hidden_parent->d_inode, wh_old, S_IRUGO, NULL); unlock_dir(hidden_parent); if (!local_err) set_dbopaque(old_dentry, bwh_old); else { /* We can't fix anything now, so we cop-out and use -EIO. */ printk ("<0>We can't create a whiteout for the source in rename!\n"); err = -EIO; } } out: DPUT(wh_old); print_exit_status(err); return err; revert: /* Do revert here. */ local_err = unionfs_refresh_hidden_dentry(new_dentry, old_bstart); if (local_err) { printk(KERN_WARNING "Revert failed in rename: the new refresh failed.\n"); eio = -EIO; } local_err = unionfs_refresh_hidden_dentry(old_dentry, old_bstart); if (local_err) { printk(KERN_WARNING "Revert failed in rename: the old refresh failed.\n"); eio = -EIO; goto revert_out; } if (!dtohd_index(new_dentry, bindex) || !dtohd_index(new_dentry, bindex)->d_inode) { printk(KERN_WARNING "Revert failed in rename: the object disappeared from under us!\n"); eio = -EIO; goto revert_out; } if (dtohd_index(old_dentry, bindex) && dtohd_index(old_dentry, bindex)->d_inode) { printk(KERN_WARNING "Revert failed in rename: the object was created underneath us!\n"); eio = -EIO; goto revert_out; } local_err = do_rename(new_dir, new_dentry, old_dir, old_dentry, old_bstart, NULL); /* If we can't fix it, then we cop-out with -EIO. */ if (local_err) { printk(KERN_WARNING "Revert failed in rename!\n"); eio = -EIO; } local_err = unionfs_refresh_hidden_dentry(new_dentry, bindex); if (local_err) eio = -EIO; local_err = unionfs_refresh_hidden_dentry(old_dentry, bindex); if (local_err) eio = -EIO; revert_out: if (eio) err = eio; print_exit_status(err); return err; }
/* This function replicates the directory structure upto given dentry * in the bindex branch. */ struct dentry *create_parents_named(struct inode *dir, struct dentry *dentry, const char *name, int bindex) { int err; struct dentry *child_dentry; struct dentry *parent_dentry; struct dentry *hidden_parent_dentry = NULL; struct dentry *hidden_dentry = NULL; const char *childname; unsigned int childnamelen; int old_kmalloc_size; int kmalloc_size; int num_dentry; int count; int old_bstart; int old_bend; struct dentry **path = NULL; struct dentry **tmp_path; print_entry_location(); verify_locked(dentry); /* There is no sense allocating any less than the minimum. */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) kmalloc_size = malloc_sizes[0].cs_size; #else kmalloc_size = 32; #endif num_dentry = kmalloc_size / sizeof(struct dentry *); if ((err = is_robranch_super(dir->i_sb, bindex))) { hidden_dentry = ERR_PTR(err); goto out; } fist_print_dentry("IN: create_parents_named", dentry); fist_dprint(8, "name = %s\n", name); old_bstart = dbstart(dentry); old_bend = dbend(dentry); path = (struct dentry **)KMALLOC(kmalloc_size, GFP_KERNEL); memset(path, 0, kmalloc_size); /* assume the negative dentry of unionfs as the parent dentry */ parent_dentry = dentry; count = 0; /* This loop finds the first parent that exists in the given branch. * We start building the directory structure from there. At the end * of the loop, the following should hold: * child_dentry is the first nonexistent child * parent_dentry is the first existent parent * path[0] is the = deepest child * path[count] is the first child to create */ do { child_dentry = parent_dentry; /* find the parent directory dentry in unionfs */ parent_dentry = child_dentry->d_parent; lock_dentry(parent_dentry); /* find out the hidden_parent_dentry in the given branch */ hidden_parent_dentry = dtohd_index(parent_dentry, bindex); /* store the child dentry */ path[count++] = child_dentry; if (count == num_dentry) { old_kmalloc_size = kmalloc_size; kmalloc_size *= 2; num_dentry = kmalloc_size / sizeof(struct dentry *); tmp_path = (struct dentry **)KMALLOC(kmalloc_size, GFP_KERNEL); if (!tmp_path) { hidden_dentry = ERR_PTR(-ENOMEM); goto out; } memset(tmp_path, 0, kmalloc_size); memcpy(tmp_path, path, old_kmalloc_size); KFREE(path); path = tmp_path; tmp_path = NULL; } } while (!hidden_parent_dentry); count--; /* This is basically while(child_dentry != dentry). This loop is * horrible to follow and should be replaced with cleaner code. */ while (1) { PASSERT(child_dentry); PASSERT(parent_dentry); PASSERT(parent_dentry->d_inode); // get hidden parent dir in the current branch hidden_parent_dentry = dtohd_index(parent_dentry, bindex); unlock_dentry(parent_dentry); PASSERT(hidden_parent_dentry); PASSERT(hidden_parent_dentry->d_inode); // init the values to lookup childname = child_dentry->d_name.name; childnamelen = child_dentry->d_name.len; if (child_dentry != dentry) { // lookup child in the underlying file system hidden_dentry = LOOKUP_ONE_LEN(childname, hidden_parent_dentry, childnamelen); if (IS_ERR(hidden_dentry)) goto out; } else { int loop_start; int loop_end; int new_bstart = -1; int new_bend = -1; int i; /* is the name a whiteout of the childname ? */ //lookup the whiteout child in the underlying file system hidden_dentry = LOOKUP_ONE_LEN(name, hidden_parent_dentry, strlen(name)); if (IS_ERR(hidden_dentry)) goto out; /* Replace the current dentry (if any) with the new one. */ DPUT(dtohd_index(dentry, bindex)); set_dtohd_index(dentry, bindex, hidden_dentry); loop_start = (old_bstart < bindex) ? old_bstart : bindex; loop_end = (old_bend > bindex) ? old_bend : bindex; /* This loop sets the bstart and bend for the new * dentry by traversing from left to right. * It also dputs all negative dentries except * bindex (the newly looked dentry */ for (i = loop_start; i <= loop_end; i++) { if (!dtohd_index(dentry, i)) continue; if (i == bindex) { new_bend = i; if (new_bstart < 0) new_bstart = i; continue; } if (!dtohd_index(dentry, i)->d_inode) { DPUT(dtohd_index(dentry, i)); set_dtohd_index(dentry, i, NULL); } else { if (new_bstart < 0) new_bstart = i; new_bend = i; } } if (new_bstart < 0) new_bstart = bindex; if (new_bend < 0) new_bend = bindex; set_dbstart(dentry, new_bstart); set_dbend(dentry, new_bend); break; } if (hidden_dentry->d_inode) { /* since this already exists we dput to avoid * multiple references on the same dentry */ DPUT(hidden_dentry); } else { uid_t saved_uid = current->fsuid; gid_t saved_gid = current->fsgid; /* its a negative dentry, create a new dir */ hidden_parent_dentry = lock_parent(hidden_dentry); current->fsuid = hidden_parent_dentry->d_inode->i_uid; current->fsgid = hidden_parent_dentry->d_inode->i_gid; err = vfs_mkdir(hidden_parent_dentry->d_inode, hidden_dentry, S_IRWXUGO); current->fsuid = saved_uid; current->fsgid = saved_gid; unlock_dir(hidden_parent_dentry); if (err || !hidden_dentry->d_inode) { DPUT(hidden_dentry); hidden_dentry = ERR_PTR(err); goto out; } err = copyup_permissions(dir->i_sb, child_dentry, hidden_dentry); if (err) { DPUT(hidden_dentry); hidden_dentry = ERR_PTR(err); goto out; } set_itohi_index(child_dentry->d_inode, bindex, igrab(hidden_dentry->d_inode)); if (ibstart(child_dentry->d_inode) > bindex) ibstart(child_dentry->d_inode) = bindex; if (ibend(child_dentry->d_inode) < bindex) ibend(child_dentry->d_inode) = bindex; set_dtohd_index(child_dentry, bindex, hidden_dentry); if (dbstart(child_dentry) > bindex) set_dbstart(child_dentry, bindex); if (dbend(child_dentry) < bindex) set_dbend(child_dentry, bindex); } parent_dentry = child_dentry; child_dentry = path[--count]; } out: KFREE(path); fist_print_dentry("OUT: create_parents_named", dentry); print_exit_pointer(hidden_dentry); return hidden_dentry; }
int copyup_named_dentry(struct inode *dir, struct dentry *dentry, int bstart, int new_bindex, char *name, int namelen, struct file **copyup_file, int len) { struct dentry *new_hidden_dentry; struct dentry *old_hidden_dentry = NULL; struct super_block *sb; struct file *input_file = NULL; struct file *output_file = NULL; ssize_t read_bytes, write_bytes; mm_segment_t old_fs; int err = 0; char *buf; int old_bindex; int got_branch_input = -1; int got_branch_output = -1; int old_bstart; int old_bend; int size = len; struct dentry *new_hidden_parent_dentry; mm_segment_t oldfs; char *symbuf = NULL; uid_t saved_uid = current->fsuid; gid_t saved_gid = current->fsgid; print_entry_location(); verify_locked(dentry); fist_print_dentry("IN: copyup_named_dentry", dentry); old_bindex = bstart; old_bstart = dbstart(dentry); old_bend = dbend(dentry); ASSERT(new_bindex >= 0); ASSERT(new_bindex < old_bindex); PASSERT(dir); PASSERT(dentry); sb = dir->i_sb; if ((err = is_robranch_super(sb, new_bindex))) goto out; /* Create the directory structure above this dentry. */ new_hidden_dentry = create_parents_named(dir, dentry, name, new_bindex); PASSERT(new_hidden_dentry); if (IS_ERR(new_hidden_dentry)) { err = PTR_ERR(new_hidden_dentry); goto out; } fist_print_generic_dentry("Copyup Object", new_hidden_dentry); /* Now we actually create the object. */ old_hidden_dentry = dtohd_index(dentry, old_bindex); PASSERT(old_hidden_dentry); PASSERT(old_hidden_dentry->d_inode); DGET(old_hidden_dentry); /* For symlinks, we must read the link before we lock the directory. */ if (S_ISLNK(old_hidden_dentry->d_inode->i_mode)) { PASSERT(old_hidden_dentry->d_inode->i_op); PASSERT(old_hidden_dentry->d_inode->i_op->readlink); symbuf = KMALLOC(PATH_MAX, GFP_UNIONFS); if (!symbuf) { err = -ENOMEM; goto copyup_readlink_err; } oldfs = get_fs(); set_fs(KERNEL_DS); err = old_hidden_dentry->d_inode->i_op-> readlink(old_hidden_dentry, symbuf, PATH_MAX); set_fs(oldfs); if (err < 0) goto copyup_readlink_err; symbuf[err] = '\0'; } /* Now we lock the parent, and create the object in the new branch. */ new_hidden_parent_dentry = lock_parent(new_hidden_dentry); current->fsuid = new_hidden_parent_dentry->d_inode->i_uid; current->fsgid = new_hidden_parent_dentry->d_inode->i_gid; if (S_ISDIR(old_hidden_dentry->d_inode->i_mode)) { err = vfs_mkdir(new_hidden_parent_dentry->d_inode, new_hidden_dentry, S_IRWXU); } else if (S_ISLNK(old_hidden_dentry->d_inode->i_mode)) { #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) err = vfs_symlink(new_hidden_parent_dentry->d_inode, new_hidden_dentry, symbuf); #else err = vfs_symlink(new_hidden_parent_dentry->d_inode, new_hidden_dentry, symbuf, S_IRWXU); #endif } else if (S_ISBLK(old_hidden_dentry->d_inode->i_mode) || S_ISCHR(old_hidden_dentry->d_inode->i_mode) || S_ISFIFO(old_hidden_dentry->d_inode->i_mode) || S_ISSOCK(old_hidden_dentry->d_inode->i_mode)) { #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) err = vfs_mknod(new_hidden_parent_dentry->d_inode, new_hidden_dentry, old_hidden_dentry->d_inode->i_mode, kdev_t_to_nr(old_hidden_dentry->d_inode-> i_rdev)); #else err = vfs_mknod(new_hidden_parent_dentry->d_inode, new_hidden_dentry, old_hidden_dentry->d_inode->i_mode, old_hidden_dentry->d_inode->i_rdev); #endif } else if (S_ISREG(old_hidden_dentry->d_inode->i_mode)) { #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) err = vfs_create(new_hidden_parent_dentry->d_inode, new_hidden_dentry, S_IRWXU); #else err = vfs_create(new_hidden_parent_dentry->d_inode, new_hidden_dentry, S_IRWXU, NULL); #endif } else { char diemsg[100]; snprintf(diemsg, sizeof(diemsg), "Unknown inode type %d\n", old_hidden_dentry->d_inode->i_mode); FISTBUG(diemsg); } current->fsuid = saved_uid; current->fsgid = saved_gid; unlock_dir(new_hidden_parent_dentry); copyup_readlink_err: KFREE(symbuf); if (err) { /* get rid of the hidden dentry and all its traces */ DPUT(new_hidden_dentry); set_dtohd_index(dentry, new_bindex, NULL); set_dbstart(dentry, old_bstart); set_dbend(dentry, old_bend); goto out; } /* We actually copyup the file here. */ if (S_ISREG(old_hidden_dentry->d_inode->i_mode)) { mntget(stohiddenmnt_index(sb, old_bindex)); branchget(sb, old_bindex); got_branch_input = old_bindex; input_file = DENTRY_OPEN(old_hidden_dentry, stohiddenmnt_index(sb, old_bindex), O_RDONLY); if (IS_ERR(input_file)) { err = PTR_ERR(input_file); goto out; } if (!input_file->f_op || !input_file->f_op->read) { err = -EINVAL; goto out; } /* copy the new file */ DGET(new_hidden_dentry); mntget(stohiddenmnt_index(sb, new_bindex)); branchget(sb, new_bindex); got_branch_output = new_bindex; output_file = DENTRY_OPEN(new_hidden_dentry, stohiddenmnt_index(sb, new_bindex), O_WRONLY); if (IS_ERR(output_file)) { err = PTR_ERR(output_file); goto out; } if (!output_file->f_op || !output_file->f_op->write) { err = -EINVAL; goto out; } /* allocating a buffer */ buf = (char *)KMALLOC(PAGE_SIZE, GFP_UNIONFS); if (!buf) { err = -ENOMEM; goto out; } /* now read PAGE_SIZE bytes from offset 0 in a loop */ old_fs = get_fs(); input_file->f_pos = 0; output_file->f_pos = 0; set_fs(KERNEL_DS); do { if (len >= PAGE_SIZE) size = PAGE_SIZE; else if ((len < PAGE_SIZE) && (len > 0)) size = len; len -= PAGE_SIZE; read_bytes = input_file->f_op->read(input_file, buf, size, &input_file->f_pos); if (read_bytes <= 0) { err = read_bytes; break; } write_bytes = output_file->f_op->write(output_file, buf, read_bytes, &output_file->f_pos); if (write_bytes < 0 || (write_bytes < read_bytes)) { err = -EIO; break; } } while ((read_bytes > 0) && (len > 0)); set_fs(old_fs); KFREE(buf); } /* Set permissions. */ if ((err = copyup_permissions(sb, old_hidden_dentry, new_hidden_dentry))) goto out; /* Selinux uses extended attributes for permissions. */ #if defined(UNIONFS_XATTR) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20)) if ((err = copyup_xattrs(old_hidden_dentry, new_hidden_dentry))) goto out; #endif /* do not allow files getting deleted to be reinterposed */ if (!d_deleted(dentry)) unionfs_reinterpose(dentry); out: if (input_file && !IS_ERR(input_file)) { fput(input_file); } else { /* since input file was not opened, we need to explicitly * dput the old_hidden_dentry */ DPUT(old_hidden_dentry); } /* in any case, we have to branchput */ if (got_branch_input >= 0) branchput(sb, got_branch_input); if (output_file) { if (copyup_file && !err) { *copyup_file = output_file; } else { fput(output_file); branchput(sb, got_branch_output); } } fist_print_dentry("OUT: copyup_dentry", dentry); fist_print_inode("OUT: copyup_dentry", dentry->d_inode); print_exit_status(err); return err; }
static int do_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry, int bindex, struct dentry **wh_old) { int err = 0; struct dentry *hidden_old_dentry; struct dentry *hidden_new_dentry; struct dentry *hidden_old_dir_dentry; struct dentry *hidden_new_dir_dentry; struct dentry *hidden_wh_dentry; struct dentry *hidden_wh_dir_dentry; char *wh_name = NULL; print_entry(" bindex=%d", bindex); fist_print_dentry("IN: do_rename, old_dentry", old_dentry); fist_print_dentry("IN: do_rename, new_dentry", new_dentry); fist_dprint(7, "do_rename for bindex = %d\n", bindex); hidden_new_dentry = dtohd_index(new_dentry, bindex); hidden_old_dentry = dtohd_index(old_dentry, bindex); if (!hidden_new_dentry) { hidden_new_dentry = create_parents(new_dentry->d_parent->d_inode, new_dentry, bindex); if (IS_ERR(hidden_new_dentry)) { fist_dprint(7, "error creating directory tree for rename, bindex = %d\n", bindex); err = PTR_ERR(hidden_new_dentry); goto out; } } wh_name = alloc_whname(new_dentry->d_name.name, new_dentry->d_name.len); if (IS_ERR(wh_name)) { err = PTR_ERR(wh_name); goto out; } hidden_wh_dentry = LOOKUP_ONE_LEN(wh_name, hidden_new_dentry->d_parent, new_dentry->d_name.len + WHLEN); if (IS_ERR(hidden_wh_dentry)) { err = PTR_ERR(hidden_wh_dentry); goto out; } if (hidden_wh_dentry->d_inode) { /* get rid of the whiteout that is existing */ if (hidden_new_dentry->d_inode) { printk(KERN_WARNING "Both a whiteout and a dentry exist when doing a rename!\n"); err = -EIO; DPUT(hidden_wh_dentry); goto out; } hidden_wh_dir_dentry = lock_parent(hidden_wh_dentry); if (!(err = is_robranch_super(old_dentry->d_sb, bindex))) { err = vfs_unlink(hidden_wh_dir_dentry->d_inode, hidden_wh_dentry); } DPUT(hidden_wh_dentry); unlock_dir(hidden_wh_dir_dentry); if (err) goto out; } else DPUT(hidden_wh_dentry); DGET(hidden_old_dentry); hidden_old_dir_dentry = GET_PARENT(hidden_old_dentry); hidden_new_dir_dentry = GET_PARENT(hidden_new_dentry); lock_rename(hidden_old_dir_dentry, hidden_new_dir_dentry); err = is_robranch_super(old_dentry->d_sb, bindex); if (err) goto out_unlock; /* ready to whiteout for old_dentry. caller will create the actual whiteout, and must dput(*wh_old) */ if (wh_old) { char *whname; whname = alloc_whname(old_dentry->d_name.name, old_dentry->d_name.len); err = PTR_ERR(whname); if (IS_ERR(whname)) goto out_unlock; *wh_old = LOOKUP_ONE_LEN(whname, hidden_old_dir_dentry, old_dentry->d_name.len + WHLEN); KFREE(whname); err = PTR_ERR(*wh_old); if (IS_ERR(*wh_old)) { *wh_old = NULL; goto out_unlock; } } fist_print_dentry("NEWBEF", new_dentry); fist_print_dentry("OLDBEF", old_dentry); err = vfs_rename(hidden_old_dir_dentry->d_inode, hidden_old_dentry, hidden_new_dir_dentry->d_inode, hidden_new_dentry); fist_print_dentry("NEWAFT", new_dentry); fist_print_dentry("OLDAFT", old_dentry); out_unlock: unlock_rename(hidden_old_dir_dentry, hidden_new_dir_dentry); DPUT(hidden_old_dir_dentry); DPUT(hidden_new_dir_dentry); DPUT(hidden_old_dentry); out: if (!err) { /* Fixup the newdentry. */ if (bindex < dbstart(new_dentry)) set_dbstart(new_dentry, bindex); else if (bindex > dbend(new_dentry)) set_dbend(new_dentry, bindex); } KFREE(wh_name); fist_print_dentry("OUT: do_rename, old_dentry", old_dentry); fist_print_dentry("OUT: do_rename, new_dentry", new_dentry); print_exit_status(err); return err; }
/* * The function is nasty, nasty, nasty, but so is rename. :( */ static int unionfs_rename_all(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry) { struct dentry *parent_dentry = NULL; int err = 0; int eio; /* These variables control error handling. */ fd_set success_mask; char *name = NULL; /* unfortunately, we have to resort to this, because dbstart/dbend would return different things in different place of the rename code */ struct rename_info info; info.rename_ok = FD_SETSIZE; /* The last rename that is ok. */ info.do_copyup = -1; /* Where we should start copyup. */ info.do_whiteout = -1; /* Where we should start whiteouts of the source. */ info.wh_old = NULL; info.bwh_old = -1; print_entry_location(); parent_dentry = old_dentry->d_parent; name = KMALLOC(old_dentry->d_name.len + 1, GFP_KERNEL); if (!name) { err = -ENOMEM; goto out; } strncpy(name, old_dentry->d_name.name, old_dentry->d_name.len + 1); info.new_bstart = dbstart(new_dentry); info.new_bend = dbend(new_dentry); info.old_bstart = dbstart(old_dentry); info.old_bend = dbend(old_dentry); BUG_ON(info.new_bstart < 0); BUG_ON(info.old_bstart < 0); /* The failure mask only can deal with FD_SETSIZE entries. */ BUG_ON(info.old_bend > FD_SETSIZE); BUG_ON(info.new_bend > FD_SETSIZE); FD_ZERO(&success_mask); /* Life is simpler if the dentry doesn't exist. */ info.clobber = (dtohd_index(new_dentry, info.new_bstart)->d_inode) ? 1 : 0; info.isdir = S_ISDIR(old_dentry->d_inode->i_mode); /* rename everything we can */ err = __rename_all(old_dir, old_dentry, new_dir, new_dentry, &success_mask, &info); if (err) goto revert; /* unlink destinations even further left */ err = __rename_all_unlink(old_dir, old_dentry, new_dir, new_dentry, &info); if (err) goto revert; if (info.clobber) { /* Now we need to handle the leftmost of the destination. */ err = __rename_all_clobber(old_dir, old_dentry, new_dir, new_dentry, &info); if (err) goto revert; } /* Copy up if necessary */ if (info.do_copyup != -1) { int bindex; for (bindex = info.do_copyup; bindex >= 0; bindex--) { err = copyup_dentry(old_dentry->d_parent->d_inode, old_dentry, info.old_bstart, bindex, NULL, old_dentry->d_inode->i_size); if (!err) { DPUT(info.wh_old); info.bwh_old = bindex; err = do_rename(old_dir, old_dentry, new_dir, new_dentry, bindex, &info.wh_old); break; } } } /* Create a whiteout for the source. */ if (info.do_whiteout != -1) { struct dentry *hidden_parent; BUG_ON(info.do_whiteout < 0 || !info.wh_old || IS_ERR(info.wh_old) || info.wh_old->d_inode || info.bwh_old < 0); hidden_parent = lock_parent(info.wh_old); err = vfs_create(hidden_parent->d_inode, info.wh_old, S_IRUGO, NULL); unlock_dir(hidden_parent); if (!err) set_dbopaque(old_dentry, info.bwh_old); else { /* We can't fix anything now, so we -EIO. */ printk(KERN_WARNING "We can't create a whiteout for the" "source in rename!\n"); err = -EIO; goto out; } } /* We are at the point where reverting doesn't happen. */ goto out; revert: /* something bad happened, try to revert */ eio = __rename_all_revert(old_dir, old_dentry, new_dir, new_dentry, &success_mask, &info); if (eio) err = eio; out: DPUT(info.wh_old); KFREE(name); print_exit_status(err); return err; }
static int unionfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev) { int err = 0; struct dentry *lower_dentry = NULL; struct dentry *wh_dentry = NULL; struct dentry *lower_parent_dentry = NULL; struct dentry *parent; char *name = NULL; int valid = 0; unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD); parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); valid = __unionfs_d_revalidate(dentry, parent, false, 0); if (unlikely(!valid)) { err = -ESTALE; goto out; } /* * It's only a bug if this dentry was not negative and couldn't be * revalidated (shouldn't happen). */ BUG_ON(!valid && dentry->d_inode); lower_dentry = find_writeable_branch(dir, dentry); if (IS_ERR(lower_dentry)) { err = PTR_ERR(lower_dentry); goto out; } lower_parent_dentry = lock_parent(lower_dentry); if (IS_ERR(lower_parent_dentry)) { err = PTR_ERR(lower_parent_dentry); goto out_unlock; } err = vfs_mknod(lower_parent_dentry->d_inode, lower_dentry, mode, dev); if (!err) { err = PTR_ERR(unionfs_interpose(dentry, dir->i_sb, 0)); if (!err) { unionfs_copy_attr_times(dir); fsstack_copy_inode_size(dir, lower_parent_dentry->d_inode); /* update no. of links on parent directory */ set_nlink(dir, unionfs_get_nlinks(dir)); } } out_unlock: unlock_dir(lower_parent_dentry); out: dput(wh_dentry); kfree(name); if (!err) { unionfs_postcopyup_setmnt(dentry); unionfs_check_inode(dir); unionfs_check_dentry(dentry); } unionfs_unlock_dentry(dentry); unionfs_unlock_parent(dentry, parent); unionfs_read_unlock(dentry->d_sb); return err; }
static int unionfs_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *nd_unused) { int err = 0; struct dentry *lower_dentry = NULL; struct dentry *lower_parent_dentry = NULL; struct dentry *parent; int valid = 0; struct nameidata lower_nd; unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD); parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); valid = __unionfs_d_revalidate(dentry, parent, false); if (unlikely(!valid)) { err = -ESTALE; /* same as what real_lookup does */ goto out; } lower_dentry = find_writeable_branch(dir, dentry); if (IS_ERR(lower_dentry)) { err = PTR_ERR(lower_dentry); goto out; } lower_parent_dentry = lock_parent(lower_dentry); if (IS_ERR(lower_parent_dentry)) { err = PTR_ERR(lower_parent_dentry); goto out_unlock; } err = init_lower_nd(&lower_nd, LOOKUP_CREATE); if (unlikely(err < 0)) goto out_unlock; err = vfs_create(lower_parent_dentry->d_inode, lower_dentry, mode, &lower_nd); release_lower_nd(&lower_nd, err); if (!err) { err = PTR_ERR(unionfs_interpose(dentry, dir->i_sb, 0)); if (!err) { unionfs_copy_attr_times(dir); fsstack_copy_inode_size(dir, lower_parent_dentry->d_inode); /* update no. of links on parent directory */ dir->i_nlink = unionfs_get_nlinks(dir); } } out_unlock: unlock_dir(lower_parent_dentry); out: if (!err) { unionfs_postcopyup_setmnt(dentry); unionfs_check_inode(dir); unionfs_check_dentry(dentry); } unionfs_unlock_dentry(dentry); unionfs_unlock_parent(dentry, parent); unionfs_read_unlock(dentry->d_sb); return err; }
static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) { int err = 0; int make_nomedia_in_obb = 0; struct dentry *lower_dentry; struct dentry *lower_parent_dentry = NULL; struct path lower_path; struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); const struct cred *saved_cred = NULL; struct sdcardfs_inode_info *pi = SDCARDFS_I(dir); char *page_buf; char *nomedia_dir_name; char *nomedia_fullpath; int fullpath_namelen; int touch_err = 0; char *(*d_absolute_path_new)(const struct path *, char *, int) = (void *)kallsyms_lookup_name("d_absolute_path"); int has_rw = get_caller_has_rw_locked(sbi->pkgl_id, sbi->options.derive); if(!check_caller_access_to_name(dir, dentry->d_name.name, sbi->options.derive, 1, has_rw)) { err = -EACCES; goto out_eacces; } /* save current_cred and override it */ OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred); /* check disk space */ if (!check_min_free_space(dentry, 0, 1)) { printk(KERN_INFO "sdcardfs: No minimum free space.\n"); err = -ENOSPC; goto out_revert; } /* the lower_dentry is negative here */ sdcardfs_get_lower_path(dentry, &lower_path); lower_dentry = lower_path.dentry; lower_parent_dentry = lock_parent(lower_dentry); err = mnt_want_write(lower_path.mnt); if (err) goto out_unlock; /* set last 16bytes of mode field to 0775 */ mode = (mode & S_IFMT) | 00775; err = vfs_mkdir(lower_parent_dentry->d_inode, lower_dentry, mode); if (err) goto out; /* if it is a local obb dentry, setup it with the base obbpath */ if(need_graft_path(dentry)) { err = setup_obb_dentry(dentry, &lower_path); if(err) { /* if the sbi->obbpath is not available, the lower_path won't be * changed by setup_obb_dentry() but the lower path is saved to * its orig_path. this dentry will be revalidated later. * but now, the lower_path should be NULL */ sdcardfs_put_reset_lower_path(dentry); /* the newly created lower path which saved to its orig_path or * the lower_path is the base obbpath. * therefore, an additional path_get is required */ path_get(&lower_path); } else make_nomedia_in_obb = 1; } err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path); if (err) goto out; fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir)); fsstack_copy_inode_size(dir, lower_parent_dentry->d_inode); /* update number of links on parent directory */ set_nlink(dir, sdcardfs_lower_inode(dir)->i_nlink); if ((sbi->options.derive == DERIVE_UNIFIED) && (!strcasecmp(dentry->d_name.name, "obb")) && (pi->perm == PERM_ANDROID) && (pi->userid == 0)) make_nomedia_in_obb = 1; /* When creating /Android/data and /Android/obb, mark them as .nomedia */ if (make_nomedia_in_obb || ((pi->perm == PERM_ANDROID) && (!strcasecmp(dentry->d_name.name, "data")))) { page_buf = (char *)__get_free_page(GFP_KERNEL); if (!page_buf) { printk(KERN_ERR "sdcardfs: failed to allocate page buf\n"); goto out; } nomedia_dir_name = d_absolute_path_new(&lower_path, page_buf, PAGE_SIZE); if (IS_ERR(nomedia_dir_name)) { free_page((unsigned long)page_buf); printk(KERN_ERR "sdcardfs: failed to get .nomedia dir name\n"); goto out; } fullpath_namelen = page_buf + PAGE_SIZE - nomedia_dir_name - 1; fullpath_namelen += strlen("/.nomedia"); nomedia_fullpath = kzalloc(fullpath_namelen + 1, GFP_KERNEL); if (!nomedia_fullpath) { free_page((unsigned long)page_buf); printk(KERN_ERR "sdcardfs: failed to allocate .nomedia fullpath buf\n"); goto out; } strcpy(nomedia_fullpath, nomedia_dir_name); free_page((unsigned long)page_buf); strcat(nomedia_fullpath, "/.nomedia"); touch_err = touch(nomedia_fullpath, 0664); if (touch_err) { printk(KERN_ERR "sdcardfs: failed to touch(%s): %d\n", nomedia_fullpath, touch_err); kfree(nomedia_fullpath); goto out; } kfree(nomedia_fullpath); } out: mnt_drop_write(lower_path.mnt); out_unlock: unlock_dir(lower_parent_dentry); sdcardfs_put_lower_path(dentry, &lower_path); out_revert: REVERT_CRED(saved_cred); out_eacces: return err; }
static int unionfs_rmdir_all(struct inode *dir, struct dentry *dentry, struct unionfs_dir_state *namelist) { struct dentry *hidden_dentry; struct dentry *hidden_dir_dentry; int bstart, bend, bindex; int err = 0; int global_err = 0; print_entry_location(); fist_print_dentry("IN unionfs_rmdir_all: ", dentry); bstart = dbstart(dentry); bend = dbend(dentry); for (bindex = bend; bindex >= bstart; bindex--) { hidden_dentry = dtohd_index(dentry, bindex); if (!hidden_dentry) continue; hidden_dir_dentry = lock_parent(hidden_dentry); if (S_ISDIR(hidden_dentry->d_inode->i_mode)) { delete_whiteouts(dentry, bindex, namelist); if (!(err = is_robranch_super(dentry->d_sb, bindex))) { err = vfs_rmdir(hidden_dir_dentry->d_inode, hidden_dentry); } } else { err = -EISDIR; } fist_copy_attr_times(dir, hidden_dir_dentry->d_inode); unlock_dir(hidden_dir_dentry); if (err) { int local_err = unionfs_refresh_hidden_dentry(dentry, bindex); if (local_err) { err = local_err; goto out; } if (!IS_COPYUP_ERR(err) && err != -ENOTEMPTY && err != -EISDIR) goto out; global_err = err; } } /* check if encountered error in the above loop */ if (global_err) { /* If we failed in the leftmost branch, then err will be set and we should * move one over to create the whiteout. Otherwise, we should try in the * leftmost branch. */ if (err) { if (dbstart(dentry) == 0) { goto out; } err = create_whiteout(dentry, dbstart(dentry) - 1); } else { err = create_whiteout(dentry, dbstart(dentry)); } } else { err = create_whiteout(dentry, dbstart(dentry)); } out: /* propagate number of hard-links */ dentry->d_inode->i_nlink = get_nlinks(dentry->d_inode); fist_print_dentry("OUT unionfs_rmdir_all: ", dentry); print_exit_status(err); return err; }
/* * This function replicates the directory structure up-to given dentry * in the bindex branch. */ struct dentry *create_parents(struct inode *dir, struct dentry *dentry, const char *name, int bindex) { int err; struct dentry *child_dentry; struct dentry *parent_dentry; struct dentry *lower_parent_dentry = NULL; struct dentry *lower_dentry = NULL; const char *childname; unsigned int childnamelen; int nr_dentry; int count = 0; int old_bstart; int old_bend; struct dentry **path = NULL; struct super_block *sb; verify_locked(dentry); err = is_robranch_super(dir->i_sb, bindex); if (err) { lower_dentry = ERR_PTR(err); goto out; } old_bstart = dbstart(dentry); old_bend = dbend(dentry); lower_dentry = ERR_PTR(-ENOMEM); /* There is no sense allocating any less than the minimum. */ nr_dentry = 1; path = kmalloc(nr_dentry * sizeof(struct dentry *), GFP_KERNEL); if (unlikely(!path)) goto out; /* assume the negative dentry of unionfs as the parent dentry */ parent_dentry = dentry; /* * This loop finds the first parent that exists in the given branch. * We start building the directory structure from there. At the end * of the loop, the following should hold: * - child_dentry is the first nonexistent child * - parent_dentry is the first existent parent * - path[0] is the = deepest child * - path[count] is the first child to create */ do { child_dentry = parent_dentry; /* find the parent directory dentry in unionfs */ parent_dentry = dget_parent(child_dentry); /* find out the lower_parent_dentry in the given branch */ lower_parent_dentry = unionfs_lower_dentry_idx(parent_dentry, bindex); /* grow path table */ if (count == nr_dentry) { void *p; nr_dentry *= 2; p = krealloc(path, nr_dentry * sizeof(struct dentry *), GFP_KERNEL); if (unlikely(!p)) { lower_dentry = ERR_PTR(-ENOMEM); goto out; } path = p; } /* store the child dentry */ path[count++] = child_dentry; } while (!lower_parent_dentry); count--; sb = dentry->d_sb; /* * This code goes between the begin/end labels and basically * emulates a while(child_dentry != dentry), only cleaner and * shorter than what would be a much longer while loop. */ begin: /* get lower parent dir in the current branch */ lower_parent_dentry = unionfs_lower_dentry_idx(parent_dentry, bindex); dput(parent_dentry); /* init the values to lookup */ childname = child_dentry->d_name.name; childnamelen = child_dentry->d_name.len; if (child_dentry != dentry) { /* lookup child in the underlying file system */ lower_dentry = lookup_one_len(childname, lower_parent_dentry, childnamelen); if (IS_ERR(lower_dentry)) goto out; } else { /* * Is the name a whiteout of the child name ? lookup the * whiteout child in the underlying file system */ lower_dentry = lookup_one_len(name, lower_parent_dentry, strlen(name)); if (IS_ERR(lower_dentry)) goto out; /* Replace the current dentry (if any) with the new one */ dput(unionfs_lower_dentry_idx(dentry, bindex)); unionfs_set_lower_dentry_idx(dentry, bindex, lower_dentry); __cleanup_dentry(dentry, bindex, old_bstart, old_bend); goto out; } if (lower_dentry->d_inode) { /* * since this already exists we dput to avoid * multiple references on the same dentry */ dput(lower_dentry); } else { struct sioq_args args; /* it's a negative dentry, create a new dir */ lower_parent_dentry = lock_parent(lower_dentry); args.mkdir.parent = lower_parent_dentry->d_inode; args.mkdir.dentry = lower_dentry; args.mkdir.mode = child_dentry->d_inode->i_mode; run_sioq(__unionfs_mkdir, &args); err = args.err; if (!err) err = copyup_permissions(dir->i_sb, child_dentry, lower_dentry); unlock_dir(lower_parent_dentry); if (err) { dput(lower_dentry); lower_dentry = ERR_PTR(err); goto out; } } __set_inode(child_dentry, lower_dentry, bindex); __set_dentry(child_dentry, lower_dentry, bindex); /* * update times of this dentry, but also the parent, because if * we changed, the parent may have changed too. */ fsstack_copy_attr_times(parent_dentry->d_inode, lower_parent_dentry->d_inode); unionfs_copy_attr_times(child_dentry->d_inode); parent_dentry = child_dentry; child_dentry = path[--count]; goto begin; out: /* cleanup any leftover locks from the do/while loop above */ if (IS_ERR(lower_dentry)) while (count) dput(path[count--]); kfree(path); return lower_dentry; }
/* * Copy up a dentry to a file of specified name. * * @dir: used to pull the ->i_sb to access other branches * @dentry: the non-negative dentry whose lower_inode we should copy * @bstart: the branch of the lower_inode to copy from * @new_bindex: the branch to create the new file in * @name: the name of the file to create * @namelen: length of @name * @copyup_file: the "struct file" to return (optional) * @len: how many bytes to copy-up? */ int copyup_dentry(struct inode *dir, struct dentry *dentry, int bstart, int new_bindex, const char *name, int namelen, struct file **copyup_file, loff_t len) { struct dentry *new_lower_dentry; struct dentry *old_lower_dentry = NULL; struct super_block *sb; int err = 0; int old_bindex; int old_bstart; int old_bend; struct dentry *new_lower_parent_dentry = NULL; mm_segment_t oldfs; char *symbuf = NULL; verify_locked(dentry); old_bindex = bstart; old_bstart = dbstart(dentry); old_bend = dbend(dentry); BUG_ON(new_bindex < 0); BUG_ON(new_bindex >= old_bindex); sb = dir->i_sb; err = is_robranch_super(sb, new_bindex); if (err) goto out; /* Create the directory structure above this dentry. */ new_lower_dentry = create_parents(dir, dentry, name, new_bindex); if (IS_ERR(new_lower_dentry)) { err = PTR_ERR(new_lower_dentry); goto out; } old_lower_dentry = unionfs_lower_dentry_idx(dentry, old_bindex); /* we conditionally dput this old_lower_dentry at end of function */ dget(old_lower_dentry); /* For symlinks, we must read the link before we lock the directory. */ if (S_ISLNK(old_lower_dentry->d_inode->i_mode)) { symbuf = kmalloc(PATH_MAX, GFP_KERNEL); if (unlikely(!symbuf)) { __clear(dentry, old_lower_dentry, old_bstart, old_bend, new_lower_dentry, new_bindex); err = -ENOMEM; goto out_free; } oldfs = get_fs(); set_fs(KERNEL_DS); err = old_lower_dentry->d_inode->i_op->readlink( old_lower_dentry, (char __user *)symbuf, PATH_MAX); set_fs(oldfs); if (err < 0) { __clear(dentry, old_lower_dentry, old_bstart, old_bend, new_lower_dentry, new_bindex); goto out_free; } symbuf[err] = '\0'; } /* Now we lock the parent, and create the object in the new branch. */ new_lower_parent_dentry = lock_parent(new_lower_dentry); /* create the new inode */ err = __copyup_ndentry(old_lower_dentry, new_lower_dentry, new_lower_parent_dentry, symbuf); if (err) { __clear(dentry, old_lower_dentry, old_bstart, old_bend, new_lower_dentry, new_bindex); goto out_unlock; } /* We actually copyup the file here. */ if (S_ISREG(old_lower_dentry->d_inode->i_mode)) err = __copyup_reg_data(dentry, new_lower_dentry, new_bindex, old_lower_dentry, old_bindex, copyup_file, len); if (err) goto out_unlink; /* Set permissions. */ err = copyup_permissions(sb, old_lower_dentry, new_lower_dentry); if (err) goto out_unlink; #ifdef CONFIG_UNION_FS_XATTR /* Selinux uses extended attributes for permissions. */ err = copyup_xattrs(old_lower_dentry, new_lower_dentry); if (err) goto out_unlink; #endif /* CONFIG_UNION_FS_XATTR */ /* do not allow files getting deleted to be re-interposed */ if (!d_deleted(dentry)) unionfs_reinterpose(dentry); goto out_unlock; out_unlink: /* * copyup failed, because we possibly ran out of space or * quota, or something else happened so let's unlink; we don't * really care about the return value of vfs_unlink */ vfs_unlink(new_lower_parent_dentry->d_inode, new_lower_dentry); if (copyup_file) { /* need to close the file */ fput(*copyup_file); branchput(sb, new_bindex); } /* * TODO: should we reset the error to something like -EIO? * * If we don't reset, the user may get some nonsensical errors, but * on the other hand, if we reset to EIO, we guarantee that the user * will get a "confusing" error message. */ out_unlock: unlock_dir(new_lower_parent_dentry); out_free: /* * If old_lower_dentry was not a file, then we need to dput it. If * it was a file, then it was already dput indirectly by other * functions we call above which operate on regular files. */ if (old_lower_dentry && old_lower_dentry->d_inode && !S_ISREG(old_lower_dentry->d_inode->i_mode)) dput(old_lower_dentry); kfree(symbuf); if (err) { /* * if directory creation succeeded, but inode copyup failed, * then purge new dentries. */ if (dbstart(dentry) < old_bstart && ibstart(dentry->d_inode) > dbstart(dentry)) __clear(dentry, NULL, old_bstart, old_bend, unionfs_lower_dentry(dentry), dbstart(dentry)); goto out; } if (!S_ISDIR(dentry->d_inode->i_mode)) { unionfs_postcopyup_release(dentry); if (!unionfs_lower_inode(dentry->d_inode)) { /* * If we got here, then we copied up to an * unlinked-open file, whose name is .unionfsXXXXX. */ struct inode *inode = new_lower_dentry->d_inode; atomic_inc(&inode->i_count); unionfs_set_lower_inode_idx(dentry->d_inode, ibstart(dentry->d_inode), inode); } } unionfs_postcopyup_setmnt(dentry); /* sync inode times from copied-up inode to our inode */ unionfs_copy_attr_times(dentry->d_inode); unionfs_check_inode(dir); unionfs_check_dentry(dentry); out: return err; }
static int unionfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry) { int err = 0; struct dentry *lower_old_dentry = NULL; struct dentry *lower_new_dentry = NULL; struct dentry *lower_dir_dentry = NULL; struct dentry *old_parent, *new_parent; char *name = NULL; bool valid; unionfs_read_lock(old_dentry->d_sb, UNIONFS_SMUTEX_CHILD); old_parent = dget_parent(old_dentry); new_parent = dget_parent(new_dentry); unionfs_double_lock_parents(old_parent, new_parent); unionfs_double_lock_dentry(old_dentry, new_dentry); valid = __unionfs_d_revalidate(old_dentry, old_parent, false, 0); if (unlikely(!valid)) { err = -ESTALE; goto out; } if (new_dentry->d_inode) { valid = __unionfs_d_revalidate(new_dentry, new_parent, false, 0); if (unlikely(!valid)) { err = -ESTALE; goto out; } } lower_new_dentry = unionfs_lower_dentry(new_dentry); /* check for a whiteout in new dentry branch, and delete it */ err = check_unlink_whiteout(new_dentry, lower_new_dentry, dbstart(new_dentry)); if (err > 0) { /* whiteout found and removed successfully */ lower_dir_dentry = dget_parent(lower_new_dentry); fsstack_copy_attr_times(dir, lower_dir_dentry->d_inode); dput(lower_dir_dentry); set_nlink(dir, unionfs_get_nlinks(dir)); err = 0; } if (err) goto out; /* check if parent hierachy is needed, then link in same branch */ if (dbstart(old_dentry) != dbstart(new_dentry)) { lower_new_dentry = create_parents(dir, new_dentry, new_dentry->d_name.name, dbstart(old_dentry)); err = PTR_ERR(lower_new_dentry); if (IS_COPYUP_ERR(err)) goto docopyup; if (!lower_new_dentry || IS_ERR(lower_new_dentry)) goto out; } lower_new_dentry = unionfs_lower_dentry(new_dentry); lower_old_dentry = unionfs_lower_dentry(old_dentry); BUG_ON(dbstart(old_dentry) != dbstart(new_dentry)); lower_dir_dentry = lock_parent(lower_new_dentry); err = is_robranch(old_dentry); if (!err) { /* see Documentation/filesystems/unionfs/issues.txt */ lockdep_off(); err = vfs_link(lower_old_dentry, lower_dir_dentry->d_inode, lower_new_dentry); lockdep_on(); } unlock_dir(lower_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_parent->d_inode, old_dentry, old_bstart, bindex, old_dentry->d_name.name, old_dentry->d_name.len, NULL, i_size_read(old_dentry->d_inode)); if (err) continue; lower_new_dentry = create_parents(dir, new_dentry, new_dentry->d_name.name, bindex); lower_old_dentry = unionfs_lower_dentry(old_dentry); lower_dir_dentry = lock_parent(lower_new_dentry); /* see Documentation/filesystems/unionfs/issues.txt */ lockdep_off(); /* do vfs_link */ err = vfs_link(lower_old_dentry, lower_dir_dentry->d_inode, lower_new_dentry); lockdep_on(); unlock_dir(lower_dir_dentry); goto check_link; } goto out; } check_link: if (err || !lower_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_add(new_dentry, new_dentry->d_inode); unionfs_copy_attr_all(dir, lower_new_dentry->d_parent->d_inode); fsstack_copy_inode_size(dir, lower_new_dentry->d_parent->d_inode); /* propagate number of hard-links */ set_nlink(old_dentry->d_inode, unionfs_get_nlinks(old_dentry->d_inode)); /* new dentry's ctime may have changed due to hard-link counts */ unionfs_copy_attr_times(new_dentry->d_inode); out: if (!new_dentry->d_inode) d_drop(new_dentry); kfree(name); if (!err) unionfs_postcopyup_setmnt(new_dentry); unionfs_check_inode(dir); unionfs_check_dentry(new_dentry); unionfs_check_dentry(old_dentry); unionfs_double_unlock_dentry(old_dentry, new_dentry); unionfs_double_unlock_parents(old_parent, new_parent); dput(new_parent); dput(old_parent); unionfs_read_unlock(old_dentry->d_sb); return err; }
static int unionfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) { int err = 0; struct dentry *lower_dentry = NULL; struct dentry *lower_parent_dentry = NULL; struct dentry *parent; int bindex = 0, bstart; char *name = NULL; int valid; unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD); parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); valid = __unionfs_d_revalidate(dentry, parent, false, 0); if (unlikely(!valid)) { err = -ESTALE; /* same as what real_lookup does */ goto out; } bstart = dbstart(dentry); lower_dentry = unionfs_lower_dentry(dentry); /* check for a whiteout in new dentry branch, and delete it */ err = check_unlink_whiteout(dentry, lower_dentry, bstart); if (err > 0) /* whiteout found and removed successfully */ err = 0; if (err) { /* exit if the error returned was NOT -EROFS */ if (!IS_COPYUP_ERR(err)) goto out; bstart--; } /* check if copyup's needed, and mkdir */ for (bindex = bstart; bindex >= 0; bindex--) { int i; int bend = dbend(dentry); if (is_robranch_super(dentry->d_sb, bindex)) continue; lower_dentry = unionfs_lower_dentry_idx(dentry, bindex); if (!lower_dentry) { lower_dentry = create_parents(dir, dentry, dentry->d_name.name, bindex); if (!lower_dentry || IS_ERR(lower_dentry)) { printk(KERN_ERR "unionfs: lower dentry " " NULL for bindex = %d\n", bindex); continue; } } lower_parent_dentry = lock_parent(lower_dentry); if (IS_ERR(lower_parent_dentry)) { err = PTR_ERR(lower_parent_dentry); goto out; } err = vfs_mkdir(lower_parent_dentry->d_inode, lower_dentry, mode); unlock_dir(lower_parent_dentry); /* did the mkdir succeed? */ if (err) break; for (i = bindex + 1; i <= bend; i++) { /* XXX: use path_put_lowers? */ if (unionfs_lower_dentry_idx(dentry, i)) { dput(unionfs_lower_dentry_idx(dentry, i)); unionfs_set_lower_dentry_idx(dentry, i, NULL); } } dbend(dentry) = bindex; /* * Only INTERPOSE_LOOKUP can return a value other than 0 on * err. */ err = PTR_ERR(unionfs_interpose(dentry, dir->i_sb, 0)); if (!err) { unionfs_copy_attr_times(dir); fsstack_copy_inode_size(dir, lower_parent_dentry->d_inode); /* update number of links on parent directory */ set_nlink(dir, unionfs_get_nlinks(dir)); } err = make_dir_opaque(dentry, dbstart(dentry)); if (err) { printk(KERN_ERR "unionfs: mkdir: error creating " ".wh.__dir_opaque: %d\n", err); goto out; } /* we are done! */ break; } out: if (!dentry->d_inode) d_drop(dentry); kfree(name); if (!err) { unionfs_copy_attr_times(dentry->d_inode); unionfs_postcopyup_setmnt(dentry); } unionfs_check_inode(dir); unionfs_check_dentry(dentry); unionfs_unlock_dentry(dentry); unionfs_unlock_parent(dentry, parent); unionfs_read_unlock(dentry->d_sb); return err; }
/* * Finish off the rename, by either over writing the last destination or * unlinking the last destination to the left of us */ static int __rename_all_clobber(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry, struct rename_info *info) { int err = 0; print_entry_location(); if (dtohd_index(old_dentry, info->new_bstart)) { /* rename the last source, knowing we're overwriting something */ DPUT(info->wh_old); info->bwh_old = info->new_bstart; err = do_rename(old_dir, old_dentry, new_dir, new_dentry, info->new_bstart, &info->wh_old); if (IS_COPYUP_ERR(err)) { if (info->isdir) { err = -EXDEV; goto out; } if (info->rename_ok > info->new_bstart) { if ((info->do_copyup == -1) || (info->new_bstart - 1 < info->do_copyup)) info->do_copyup = info->new_bstart - 1; } if ((info->do_whiteout == -1) || (info->new_bstart - 1 < info->do_whiteout)) { info->do_whiteout = info->new_bstart - 1; } err = 0; // reset error } } else if (info->new_bstart < info->old_bstart) { /* the newly renamed file would get hidden, let's unlink the * file to the left of it */ struct dentry *unlink_dentry; struct dentry *unlink_dir_dentry; unlink_dentry = dtohd_index(new_dentry, info->new_bstart); unlink_dir_dentry = lock_parent(unlink_dentry); if (!(err = is_robranch_super(old_dir->i_sb, info->new_bstart))) 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 (IS_COPYUP_ERR(err)) { if (info->isdir) { err = -EXDEV; goto out; } if ((info->do_copyup == -1) || (info->new_bstart - 1 < info->do_copyup)) info->do_copyup = info->new_bstart - 1; err = 0; // reset error } } out: print_exit_status(err); return err; }
static int unionfs_unlink_all(struct inode *dir, struct dentry *dentry) { struct dentry *hidden_dentry; struct dentry *hidden_dir_dentry; int bstart, bend, bindex; int err = 0; int global_err = 0; print_entry_location(); if ((err = unionfs_partial_lookup(dentry))) goto out; bstart = dbstart(dentry); bend = dbend(dentry); for (bindex = bend; bindex >= bstart; bindex--) { hidden_dentry = dtohd_index(dentry, bindex); if (!hidden_dentry) continue; 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) { /* passup the last error we got */ if (!IS_COPYUP_ERR(err)) goto out; global_err = err; } } /* check if encountered error in the above loop */ if (global_err) { /* If we failed in the leftmost branch, then err will be set * and we should move one over to create the whiteout. * Otherwise, we should try in the leftmost branch. */ if (err) { if (dbstart(dentry) == 0) { goto out; } err = create_whiteout(dentry, dbstart(dentry) - 1); } else { err = create_whiteout(dentry, dbstart(dentry)); } } else if (dbopaque(dentry) != -1) { /* There is a hidden lower-priority file with the same name. */ err = create_whiteout(dentry, dbopaque(dentry)); } out: /* propagate number of hard-links */ if (dentry->d_inode->i_nlink != 0) { dentry->d_inode->i_nlink = get_nlinks(dentry->d_inode); if (!err && global_err) dentry->d_inode->i_nlink--; } /* We don't want to leave negative leftover dentries for revalidate. */ if (!err && (global_err || dbopaque(dentry) != -1)) update_bstart(dentry); print_exit_status(err); return err; }
static int amfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry) { struct dentry *lower_old_dentry; struct dentry *lower_new_dentry; struct dentry *lower_dir_dentry; u64 file_size_save; int err; struct path lower_old_path, lower_new_path; char *value = NULL; if (old_dentry->d_inode->i_ino == AMFS_SB(old_dentry->d_sb)->inode_no) { err = -EPERM; goto out_err; } /************XATTR************/ value = kzalloc(5, __GFP_WAIT); if (value == NULL) { err = -ENOMEM; goto out_err; } if (amfs_getxattr(old_dentry, AMFS_XATTR_NAME, value, 5) > 0) { if (!strncmp(value, AMFS_BADFILE, 3)) { err = -EPERM; goto freevalue; } } else if (amfs_getxattr(old_dentry, AMFS_XATTR_NAME, value, 5) != -ENODATA){ err = amfs_getxattr(old_dentry, AMFS_XATTR_NAME, value, 5); goto freevalue; } /************XATTR***********/ file_size_save = i_size_read(old_dentry->d_inode); amfs_get_lower_path(old_dentry, &lower_old_path); amfs_get_lower_path(new_dentry, &lower_new_path); lower_old_dentry = lower_old_path.dentry; lower_new_dentry = lower_new_path.dentry; lower_dir_dentry = lock_parent(lower_new_dentry); err = vfs_link(lower_old_dentry, lower_dir_dentry->d_inode, lower_new_dentry, NULL); if (err || !lower_new_dentry->d_inode) goto out; err = amfs_interpose(new_dentry, dir->i_sb, &lower_new_path); if (err) goto out; fsstack_copy_attr_times(dir, lower_new_dentry->d_inode); fsstack_copy_inode_size(dir, lower_new_dentry->d_inode); set_nlink(old_dentry->d_inode, amfs_lower_inode(old_dentry->d_inode)->i_nlink); i_size_write(new_dentry->d_inode, file_size_save); out: unlock_dir(lower_dir_dentry); amfs_put_lower_path(old_dentry, &lower_old_path); amfs_put_lower_path(new_dentry, &lower_new_path); freevalue: kfree(value); out_err: return err; }