static int __open_dir(struct inode *inode, struct file *file, struct dentry *parent) { struct dentry *lower_dentry; struct file *lower_file; struct vfsmount *lower_mnt; struct dentry *dentry = file->f_path.dentry; int i = 0, idx = 0; for (i = 0; i <= 1; i++) { lower_dentry = wrapfs_lower_dentry_idx(dentry, i); if (!lower_dentry || !lower_dentry->d_inode) continue; dget(lower_dentry); lower_mnt = mntget(wrapfs_lower_mnt_idx(dentry, i)); if (!lower_mnt) lower_mnt = mntget(wrapfs_lower_mnt_idx(parent, i)); lower_file = dentry_open(lower_dentry, lower_mnt, file->f_flags, current_cred()); if (IS_ERR(lower_file)) return PTR_ERR(lower_file); wrapfs_set_lower_file_idx(file, i, lower_file); if (!wrapfs_lower_mnt_idx(dentry, i)) wrapfs_set_lower_mnt_idx(dentry, i, lower_mnt); branchget(inode->i_sb, i); idx = i; } fsstack_copy_attr_all(inode, wrapfs_lower_inode_idx(inode, idx)); return 0; }
/* unionfs_open helper function: open a directory */ static int __open_dir(struct inode *inode, struct file *file) { struct dentry *lower_dentry; struct file *lower_file; int bindex, bstart, bend; struct vfsmount *mnt; bstart = fbstart(file) = dbstart(file->f_path.dentry); bend = fbend(file) = dbend(file->f_path.dentry); for (bindex = bstart; bindex <= bend; bindex++) { lower_dentry = unionfs_lower_dentry_idx(file->f_path.dentry, bindex); if (!lower_dentry) continue; dget(lower_dentry); unionfs_mntget(file->f_path.dentry, bindex); mnt = unionfs_lower_mnt_idx(file->f_path.dentry, bindex); lower_file = dentry_open(lower_dentry, mnt, file->f_flags, current_cred()); if (IS_ERR(lower_file)) return PTR_ERR(lower_file); unionfs_set_lower_file_idx(file, bindex, lower_file); /* * The branchget goes after the open, because otherwise * we would miss the reference on release. */ branchget(inode->i_sb, bindex); } return 0; }
/* open all lower files for a given file */ static int open_all_files(struct file *file) { int bindex, bstart, bend, err = 0; struct file *lower_file; struct dentry *lower_dentry; struct dentry *dentry = file->f_path.dentry; struct super_block *sb = dentry->d_sb; bstart = dbstart(dentry); bend = dbend(dentry); for (bindex = bstart; bindex <= bend; bindex++) { lower_dentry = unionfs_lower_dentry_idx(dentry, bindex); if (!lower_dentry) continue; dget(lower_dentry); unionfs_mntget(dentry, bindex); branchget(sb, bindex); lower_file = dentry_open(lower_dentry, unionfs_lower_mnt_idx(dentry, bindex), file->f_flags, current_cred()); if (IS_ERR(lower_file)) { branchput(sb, bindex); err = PTR_ERR(lower_file); goto out; } else { unionfs_set_lower_file_idx(file, bindex, lower_file); } } out: return err; }
static int __open_file(struct inode *inode, struct file *file, struct dentry *parent) { struct dentry *lower_dentry; struct file *lower_file; struct vfsmount *lower_mnt; struct dentry *dentry = file->f_path.dentry; int lower_flags; int i = 0, idx = 0; for (i = 0; i <= 1; i++) { lower_dentry = wrapfs_lower_dentry_idx(dentry, i); if (!lower_dentry || !lower_dentry->d_inode) continue; lower_flags = file->f_flags; if (lower_dentry->d_inode && (i == 1)) { UDBG; if (lower_flags & O_TRUNC) { int size = 0; int err = -EROFS; UDBG; err = copyup_file(parent->d_inode, file, i, 0, size); if (!err) break; return err; } else lower_flags &= ~(OPEN_WRITE_FLAGS); } dget(lower_dentry); lower_mnt = mntget(wrapfs_lower_mnt_idx(dentry, i)); if (!lower_mnt) lower_mnt = mntget(wrapfs_lower_mnt_idx(parent, i)); lower_file = dentry_open(lower_dentry, lower_mnt, lower_flags, current_cred()); if (IS_ERR(lower_file)) return PTR_ERR(lower_file); wrapfs_set_lower_file(file, lower_file); branchget(inode->i_sb, i); idx = i; goto out; } out: if (!wrapfs_lower_inode_idx(inode, idx)) fsstack_copy_attr_all(inode, wrapfs_lower_inode_idx(inode, idx)); return 0; }
/* open the highest priority file for a given upper file */ static int open_highest_file(struct file *file, bool willwrite) { int bindex, bstart, bend, err = 0; struct file *lower_file; struct dentry *lower_dentry; struct dentry *dentry = file->f_path.dentry; struct dentry *parent = dget_parent(dentry); struct inode *parent_inode = parent->d_inode; struct super_block *sb = dentry->d_sb; bstart = dbstart(dentry); bend = dbend(dentry); lower_dentry = unionfs_lower_dentry(dentry); if (willwrite && IS_WRITE_FLAG(file->f_flags) && is_robranch(dentry)) { for (bindex = bstart - 1; bindex >= 0; bindex--) { err = copyup_file(parent_inode, file, bstart, bindex, i_size_read(dentry->d_inode)); if (!err) break; } atomic_set(&UNIONFS_F(file)->generation, atomic_read(&UNIONFS_I(dentry->d_inode)-> generation)); goto out; } dget(lower_dentry); unionfs_mntget(dentry, bstart); lower_file = dentry_open(lower_dentry, unionfs_lower_mnt_idx(dentry, bstart), file->f_flags, current_cred()); if (IS_ERR(lower_file)) { err = PTR_ERR(lower_file); goto out; } branchget(sb, bstart); unionfs_set_lower_file(file, lower_file); /* Fix up the position. */ lower_file->f_pos = file->f_pos; memcpy(&lower_file->f_ra, &file->f_ra, sizeof(struct file_ra_state)); out: dput(parent); return err; }
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; }
/* Is a directory logically empty? */ int check_empty(struct dentry *dentry, struct dentry *parent, struct unionfs_dir_state **namelist) { int err = 0; struct dentry *lower_dentry = NULL; struct vfsmount *mnt; struct super_block *sb; struct file *lower_file; struct unionfs_rdutil_callback *buf = NULL; int bindex, bstart, bend, bopaque; sb = dentry->d_sb; BUG_ON(!S_ISDIR(dentry->d_inode->i_mode)); err = unionfs_partial_lookup(dentry, parent); if (err) goto out; bstart = dbstart(dentry); bend = dbend(dentry); bopaque = dbopaque(dentry); if (0 <= bopaque && bopaque < bend) bend = bopaque; buf = kmalloc(sizeof(struct unionfs_rdutil_callback), GFP_KERNEL); if (unlikely(!buf)) { err = -ENOMEM; goto out; } buf->err = 0; buf->mode = RD_CHECK_EMPTY; buf->rdstate = alloc_rdstate(dentry->d_inode, bstart); if (unlikely(!buf->rdstate)) { err = -ENOMEM; goto out; } /* Process the lower directories with rdutil_callback as a filldir. */ for (bindex = bstart; bindex <= bend; bindex++) { lower_dentry = unionfs_lower_dentry_idx(dentry, bindex); if (!lower_dentry) continue; if (!lower_dentry->d_inode) continue; if (!S_ISDIR(lower_dentry->d_inode->i_mode)) continue; dget(lower_dentry); mnt = unionfs_mntget(dentry, bindex); branchget(sb, bindex); lower_file = dentry_open(lower_dentry, mnt, O_RDONLY); if (IS_ERR(lower_file)) { err = PTR_ERR(lower_file); branchput(sb, bindex); goto out; } do { buf->filldir_called = 0; buf->rdstate->bindex = bindex; err = vfs_readdir(lower_file, readdir_util_callback, buf); if (buf->err) err = buf->err; } while ((err >= 0) && buf->filldir_called); /* fput calls dput for lower_dentry */ fput(lower_file); branchput(sb, bindex); if (err < 0) goto out; } out: if (buf) { if (namelist && !err) *namelist = buf->rdstate; else if (buf->rdstate) free_rdstate(buf->rdstate); kfree(buf); } return err; }
static int __copyup_reg_data(struct dentry *dentry, struct dentry *new_lower_dentry, int new_bindex, struct dentry *old_lower_dentry, int old_bindex, struct file **copyup_file, loff_t len) { struct super_block *sb = dentry->d_sb; struct file *input_file; struct file *output_file; struct vfsmount *output_mnt; mm_segment_t old_fs; char *buf = NULL; ssize_t read_bytes, write_bytes; loff_t size; int err = 0; /* open old file */ unionfs_mntget(dentry, old_bindex); branchget(sb, old_bindex); /* dentry_open calls dput and mntput if it returns an error */ input_file = dentry_open(old_lower_dentry, unionfs_lower_mnt_idx(dentry, old_bindex), O_RDONLY | O_LARGEFILE); if (IS_ERR(input_file)) { dput(old_lower_dentry); err = PTR_ERR(input_file); goto out; } if (unlikely(!input_file->f_op || !input_file->f_op->read)) { err = -EINVAL; goto out_close_in; } /* open new file */ dget(new_lower_dentry); output_mnt = unionfs_mntget(sb->s_root, new_bindex); branchget(sb, new_bindex); output_file = dentry_open(new_lower_dentry, output_mnt, O_RDWR | O_LARGEFILE); if (IS_ERR(output_file)) { err = PTR_ERR(output_file); goto out_close_in2; } if (unlikely(!output_file->f_op || !output_file->f_op->write)) { err = -EINVAL; goto out_close_out; } /* allocating a buffer */ buf = kmalloc(PAGE_SIZE, GFP_KERNEL); if (unlikely(!buf)) { err = -ENOMEM; goto out_close_out; } input_file->f_pos = 0; output_file->f_pos = 0; old_fs = get_fs(); set_fs(KERNEL_DS); size = len; err = 0; 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, (char __user *)buf, size, &input_file->f_pos); if (read_bytes <= 0) { err = read_bytes; break; } /* see Documentation/filesystems/unionfs/issues.txt */ lockdep_off(); write_bytes = output_file->f_op->write(output_file, (char __user *)buf, read_bytes, &output_file->f_pos); lockdep_on(); if ((write_bytes < 0) || (write_bytes < read_bytes)) { err = write_bytes; break; } } while ((read_bytes > 0) && (len > 0)); set_fs(old_fs); kfree(buf); if (!err) err = output_file->f_op->fsync(output_file, new_lower_dentry, 0); if (err) goto out_close_out; if (copyup_file) { *copyup_file = output_file; goto out_close_in; } out_close_out: fput(output_file); out_close_in2: branchput(sb, new_bindex); out_close_in: fput(input_file); out: branchput(sb, old_bindex); return err; }
static int __copyup_reg_data(struct dentry *dentry, struct dentry *new_hidden_dentry, int new_bindex, struct dentry *old_hidden_dentry, int old_bindex, struct file **copyup_file, loff_t len) { struct super_block *sb = dentry->d_sb; struct file *input_file; struct file *output_file; mm_segment_t old_fs; char *buf = NULL; ssize_t read_bytes, write_bytes; loff_t size; int err = 0; /* open old file */ mntget(unionfs_lower_mnt_idx(dentry, old_bindex)); branchget(sb, old_bindex); input_file = dentry_open(old_hidden_dentry, unionfs_lower_mnt_idx(dentry, old_bindex), O_RDONLY | O_LARGEFILE); if (IS_ERR(input_file)) { dput(old_hidden_dentry); err = PTR_ERR(input_file); goto out; } if (!input_file->f_op || !input_file->f_op->read) { err = -EINVAL; goto out_close_in; } /* open new file */ dget(new_hidden_dentry); mntget(unionfs_lower_mnt_idx(dentry, new_bindex)); branchget(sb, new_bindex); output_file = dentry_open(new_hidden_dentry, unionfs_lower_mnt_idx(dentry, new_bindex), O_WRONLY | O_LARGEFILE); if (IS_ERR(output_file)) { err = PTR_ERR(output_file); goto out_close_in2; } if (!output_file->f_op || !output_file->f_op->write) { err = -EINVAL; goto out_close_out; } /* allocating a buffer */ buf = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!buf) { err = -ENOMEM; goto out_close_out; } input_file->f_pos = 0; output_file->f_pos = 0; old_fs = get_fs(); set_fs(KERNEL_DS); size = len; err = 0; 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, (char __user *)buf, size, &input_file->f_pos); if (read_bytes <= 0) { err = read_bytes; break; } write_bytes = output_file->f_op->write(output_file, (char __user *)buf, read_bytes, &output_file->f_pos); if ((write_bytes < 0) || (write_bytes < read_bytes)) { err = write_bytes; break; } } while ((read_bytes > 0) && (len > 0)); set_fs(old_fs); kfree(buf); if (err) goto out_close_out; if (copyup_file) { *copyup_file = output_file; goto out_close_in; } out_close_out: fput(output_file); out_close_in2: branchput(sb, new_bindex); out_close_in: fput(input_file); out: branchput(sb, old_bindex); return err; }
/* unionfs_open helper function: open a file */ static int __open_file(struct inode *inode, struct file *file, struct dentry *parent) { struct dentry *lower_dentry; struct file *lower_file; int lower_flags; int bindex, bstart, bend; lower_dentry = unionfs_lower_dentry(file->f_path.dentry); lower_flags = file->f_flags; bstart = fbstart(file) = dbstart(file->f_path.dentry); bend = fbend(file) = dbend(file->f_path.dentry); /* * check for the permission for lower file. If the error is * COPYUP_ERR, copyup the file. */ if (lower_dentry->d_inode && is_robranch(file->f_path.dentry)) { /* * if the open will change the file, copy it up otherwise * defer it. */ if (lower_flags & O_TRUNC) { int size = 0; int err = -EROFS; /* copyup the file */ for (bindex = bstart - 1; bindex >= 0; bindex--) { err = copyup_file(parent->d_inode, file, bstart, bindex, size); if (!err) { /* only one regular file open */ fbend(file) = fbstart(file); break; } } return err; } else { /* * turn off writeable flags, to force delayed copyup * by caller. */ lower_flags &= ~(OPEN_WRITE_FLAGS); } } dget(lower_dentry); /* * dentry_open will decrement mnt refcnt if err. * otherwise fput() will do an mntput() for us upon file close. */ unionfs_mntget(file->f_path.dentry, bstart); lower_file = dentry_open(lower_dentry, unionfs_lower_mnt_idx(file->f_path.dentry, bstart), lower_flags, current_cred()); if (IS_ERR(lower_file)) return PTR_ERR(lower_file); unionfs_set_lower_file(file, lower_file); branchget(inode->i_sb, bstart); return 0; }
/* Is a directory logically empty? */ int check_empty(struct dentry *dentry, struct dentry *parent, struct unionfs_dir_state **namelist) { int err = 0; struct dentry *lower_dentry = NULL; struct vfsmount *mnt; struct super_block *sb; struct file *lower_file; struct unionfs_rdutil_callback buf = { .ctx.actor = readdir_util_callback, }; int bindex, bstart, bend, bopaque; struct path path; sb = dentry->d_sb; BUG_ON(!S_ISDIR(dentry->d_inode->i_mode)); err = unionfs_partial_lookup(dentry, parent); if (err) goto out; bstart = dbstart(dentry); bend = dbend(dentry); bopaque = dbopaque(dentry); if (0 <= bopaque && bopaque < bend) bend = bopaque; buf.err = 0; buf.filldir_called = 0; buf.mode = RD_CHECK_EMPTY; buf.ctx.pos = 0; /* XXX: needed?! */ buf.rdstate = alloc_rdstate(dentry->d_inode, bstart); if (unlikely(!buf.rdstate)) { err = -ENOMEM; goto out; } /* Process the lower directories with rdutil_callback as a filldir. */ for (bindex = bstart; bindex <= bend; bindex++) { lower_dentry = unionfs_lower_dentry_idx(dentry, bindex); if (!lower_dentry) continue; if (!lower_dentry->d_inode) continue; if (!S_ISDIR(lower_dentry->d_inode->i_mode)) continue; dget(lower_dentry); mnt = unionfs_mntget(dentry, bindex); branchget(sb, bindex); path.dentry = lower_dentry; path.mnt = mnt; lower_file = dentry_open(&path, O_RDONLY, current_cred()); path_put(&path); if (IS_ERR(lower_file)) { err = PTR_ERR(lower_file); branchput(sb, bindex); goto out; } do { buf.filldir_called = 0; buf.rdstate->bindex = bindex; err = iterate_dir(lower_file, &buf.ctx); if (buf.err) err = buf.err; } while ((err >= 0) && buf.filldir_called); /* fput calls dput for lower_dentry */ fput(lower_file); branchput(sb, bindex); if (err < 0) goto out; } out: if (namelist && !err) *namelist = buf.rdstate; else if (buf.rdstate) free_rdstate(buf.rdstate); return err; }