/** * relayfs_create_entry - create a relayfs directory or file * @name: the name of the file to create * @parent: parent directory * @mode: mode * @chan: relay channel associated with the file * * Returns the new dentry, NULL on failure * * Creates a file or directory with the specifed permissions. */ static struct dentry *relayfs_create_entry(const char *name, struct dentry *parent, int mode, struct rchan *chan) { struct dentry *d; struct inode *inode; int error = 0; BUG_ON(!name || !(S_ISREG(mode) || S_ISDIR(mode))); error = simple_pin_fs("relayfs", &relayfs_mount, &relayfs_mount_count); if (error) { printk(KERN_ERR "Couldn't mount relayfs: errcode %d\n", error); return NULL; } if (!parent && relayfs_mount && relayfs_mount->mnt_sb) parent = relayfs_mount->mnt_sb->s_root; if (!parent) { simple_release_fs(&relayfs_mount, &relayfs_mount_count); return NULL; } parent = dget(parent); down(&parent->d_inode->i_sem); d = lookup_one_len(name, parent, strlen(name)); if (IS_ERR(d)) { d = NULL; goto release_mount; } if (d->d_inode) { d = NULL; goto release_mount; } inode = relayfs_get_inode(parent->d_inode->i_sb, mode, chan); if (!inode) { d = NULL; goto release_mount; } d_instantiate(d, inode); dget(d); /* Extra count - pin the dentry in core */ if (S_ISDIR(mode)) parent->d_inode->i_nlink++; goto exit; release_mount: simple_release_fs(&relayfs_mount, &relayfs_mount_count); exit: up(&parent->d_inode->i_sem); dput(parent); return d; }
/** * sysfs_chmod_file - update the modified mode value on an object attribute. * @kobj: object we're acting for. * @attr: attribute descriptor. * @mode: file permissions. * */ int sysfs_chmod_file(struct kobject *kobj, struct attribute *attr, mode_t mode) { struct dentry *dir = kobj->dentry; struct dentry *victim; struct inode * inode; struct iattr newattrs; int res = -ENOENT; mutex_lock(&dir->d_inode->i_mutex); victim = lookup_one_len(attr->name, dir, strlen(attr->name)); if (!IS_ERR(victim)) { if (victim->d_inode && (victim->d_parent->d_inode == dir->d_inode)) { inode = victim->d_inode; mutex_lock(&inode->i_mutex); newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO); newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; res = notify_change(victim, &newattrs); mutex_unlock(&inode->i_mutex); } dput(victim); } mutex_unlock(&dir->d_inode->i_mutex); return res; }
static struct dentry *get_node(int num) { char s[10]; struct dentry *root = capifs_root; mutex_lock(&root->d_inode->i_mutex); return lookup_one_len(s, root, sprintf(s, "%d", num)); }
static int create_by_name(const char *name, mode_t mode, struct dentry *parent, struct dentry **dentry) { int error = 0; *dentry = NULL; /* If the parent is not specified, we create it in the root. * We need the root dentry to do this, which is in the super * block. A pointer to that is in the struct vfsmount that we * have around. */ if (!parent) parent = mount->mnt_sb->s_root; mutex_lock(&parent->d_inode->i_mutex); *dentry = lookup_one_len(name, parent, strlen(name)); if (!IS_ERR(*dentry)) { if ((mode & S_IFMT) == S_IFDIR) error = mkdir(parent->d_inode, *dentry, mode); else error = create(parent->d_inode, *dentry, mode); if (error) dput(*dentry); } else error = PTR_ERR(*dentry); mutex_unlock(&parent->d_inode->i_mutex); return error; }
static struct dentry *lookup_whiteout(struct dentry *dentry) { char *whname; int bindex = -1, bstart = -1, bend = -1; struct dentry *parent, *hidden_parent, *wh_dentry; whname = alloc_whname(dentry->d_name.name, dentry->d_name.len); if (IS_ERR(whname)) return (void *)whname; parent = dget_parent(dentry); unionfs_lock_dentry(parent); bstart = dbstart(parent); bend = dbend(parent); wh_dentry = ERR_PTR(-ENOENT); for (bindex = bstart; bindex <= bend; bindex++) { hidden_parent = unionfs_lower_dentry_idx(parent, bindex); if (!hidden_parent) continue; wh_dentry = lookup_one_len(whname, hidden_parent, dentry->d_name.len + UNIONFS_WHLEN); if (IS_ERR(wh_dentry)) continue; if (wh_dentry->d_inode) break; dput(wh_dentry); wh_dentry = ERR_PTR(-ENOENT); } unionfs_unlock_dentry(parent); dput(parent); kfree(whname); return wh_dentry; }
/* * Lookup one path component @name relative to a <base,mnt> path pair. * Behaves nearly the same as lookup_one_len (i.e., return negative dentry * on ENOENT), but uses the @mnt passed, so it can cross bind mounts and * other lower mounts properly. If @new_mnt is non-null, will fill in the * new mnt there. Caller is responsible to dput/mntput/path_put returned * @dentry and @new_mnt. */ struct dentry *__lookup_one(struct dentry *base, struct vfsmount *mnt, const char *name, struct vfsmount **new_mnt) { struct dentry *dentry = NULL; struct nameidata lower_nd; int err; /* we use flags=0 to get basic lookup */ err = vfs_path_lookup(base, mnt, name, 0, &lower_nd); switch (err) { case 0: /* no error */ dentry = lower_nd.path.dentry; if (new_mnt) *new_mnt = lower_nd.path.mnt; /* rc already inc'ed */ break; case -ENOENT: /* * We don't consider ENOENT an error, and we want to return * a negative dentry (ala lookup_one_len). As we know * there was no inode for this name before (-ENOENT), then * it's safe to call lookup_one_len (which doesn't take a * vfsmount). */ dentry = lookup_one_len(name, base, strlen(name)); if (new_mnt) *new_mnt = mntget(lower_nd.path.mnt); break; default: /* all other real errors */ dentry = ERR_PTR(err); break; } return dentry; }
static int ovl_create_upper(struct dentry *dentry, struct inode *inode, struct kstat *stat, const char *link, struct dentry *hardlink) { struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent); struct inode *udir = upperdir->d_inode; struct dentry *newdentry; int err; mutex_lock_nested(&udir->i_mutex, I_MUTEX_PARENT); newdentry = lookup_one_len(dentry->d_name.name, upperdir, dentry->d_name.len); err = PTR_ERR(newdentry); if (IS_ERR(newdentry)) goto out_unlock; err = ovl_create_real(udir, newdentry, stat, link, hardlink, false); if (err) goto out_dput; ovl_dentry_version_inc(dentry->d_parent); ovl_dentry_update(dentry, newdentry); ovl_copyattr(newdentry->d_inode, inode); d_instantiate(dentry, inode); newdentry = NULL; out_dput: dput(newdentry); out_unlock: mutex_unlock(&udir->i_mutex); return err; }
/** * Prepares a directory operation by taking the mutex on the parent inode of * an entry and returning a dentry for the entry. * * @param child_name path to the entry, assumed absolute from configfs * scheduler subsystem entry. * * @return a valid dentry to the target entry, or error. The valid * dentry must be released with put_child_dentry. */ static struct dentry *get_child_dentry(const char *child_name) { struct dentry *d_dir; struct dentry *d_child; const char *last_child_comp; const char *real_child_name = child_name; int err; d_dir = dget(krg_scheduler_subsys.su_group.cg_item.ci_dentry); last_child_comp = strrchr(child_name, '/'); if (last_child_comp) { struct nameidata nd; err = vfs_path_lookup(d_dir, scheduler_fs_mount, child_name, LOOKUP_PARENT, &nd); dput(d_dir); if (err) return ERR_PTR(err); d_dir = dget(nd.path.dentry); path_put(&nd.path); BUG_ON(!last_child_comp[1]); real_child_name = last_child_comp + 1; } mutex_lock_nested(&d_dir->d_inode->i_mutex, I_MUTEX_PARENT); d_child = lookup_one_len(real_child_name, d_dir, strlen(real_child_name)); if (IS_ERR(d_child)) mutex_unlock(&d_dir->d_inode->i_mutex); dput(d_dir); return d_child; }
static int lookup_and_delete_xattr(struct inode *inode, const char *name) { int err = 0; struct dentry *dentry, *xadir; xadir = open_xa_dir(inode, XATTR_REPLACE); if (IS_ERR(xadir)) return PTR_ERR(xadir); mutex_lock_nested(&xadir->d_inode->i_mutex, I_MUTEX_XATTR); dentry = lookup_one_len(name, xadir, strlen(name)); if (IS_ERR(dentry)) { err = PTR_ERR(dentry); goto out_dput; } if (dentry->d_inode) { reiserfs_write_lock(inode->i_sb); err = xattr_unlink(xadir->d_inode, dentry); reiserfs_write_unlock(inode->i_sb); update_ctime(inode); } dput(dentry); out_dput: mutex_unlock(&xadir->d_inode->i_mutex); dput(xadir); return err; }
static int ovl_create_upper(struct dentry *dentry, struct inode *inode, struct cattr *attr, struct dentry *hardlink) { struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent); struct inode *udir = upperdir->d_inode; struct dentry *newdentry; int err; if (!hardlink && !IS_POSIXACL(udir)) attr->mode &= ~current_umask(); inode_lock_nested(udir, I_MUTEX_PARENT); newdentry = lookup_one_len(dentry->d_name.name, upperdir, dentry->d_name.len); err = PTR_ERR(newdentry); if (IS_ERR(newdentry)) goto out_unlock; err = ovl_create_real(udir, newdentry, attr, hardlink, false); if (err) goto out_dput; if (ovl_type_merge(dentry->d_parent)) { /* Setting opaque here is just an optimization, allow to fail */ ovl_set_opaque(dentry, newdentry); } ovl_instantiate(dentry, inode, newdentry, !!hardlink); newdentry = NULL; out_dput: dput(newdentry); out_unlock: inode_unlock(udir); return err; }
static struct dentry *get_node(int num) { char s[12]; struct dentry *root = devpts_root; down(&root->d_inode->i_sem); return lookup_one_len(s, root, sprintf(s, "%d", num)); }
static int remove_file(struct dentry *parent, char *name) { struct dentry *tmp; int ret; tmp = lookup_one_len(name, parent, strlen(name)); if (IS_ERR(tmp)) { ret = PTR_ERR(tmp); goto bail; } spin_lock(&dcache_lock); spin_lock(&tmp->d_lock); if (!(d_unhashed(tmp) && tmp->d_inode)) { dget_locked(tmp); __d_drop(tmp); spin_unlock(&tmp->d_lock); spin_unlock(&dcache_lock); simple_unlink(parent->d_inode, tmp); } else { spin_unlock(&tmp->d_lock); spin_unlock(&dcache_lock); } ret = 0; bail: /* * We don't expect clients to care about the return value, but * it's there if they need it. */ return ret; }
static int ovl_mkdir_real(struct inode *dir, struct dentry **newdentry, umode_t mode) { int err; struct dentry *d, *dentry = *newdentry; err = ovl_do_mkdir(dir, dentry, mode); if (err) return err; if (likely(!d_unhashed(dentry))) return 0; /* * vfs_mkdir() may succeed and leave the dentry passed * to it unhashed and negative. If that happens, try to * lookup a new hashed and positive dentry. */ d = lookup_one_len(dentry->d_name.name, dentry->d_parent, dentry->d_name.len); if (IS_ERR(d)) { pr_warn("overlayfs: failed lookup after mkdir (%pd2, err=%i).\n", dentry, err); return PTR_ERR(d); } dput(dentry); *newdentry = d; return 0; }
static int ovl_create_upper(struct dentry *dentry, struct inode *inode, struct kstat *stat, const char *link, struct dentry *hardlink) { struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent); struct inode *udir = upperdir->d_inode; struct dentry *newdentry; int err; if (!hardlink && !IS_POSIXACL(udir)) stat->mode &= ~current_umask(); inode_lock_nested(udir, I_MUTEX_PARENT); newdentry = lookup_one_len(dentry->d_name.name, upperdir, dentry->d_name.len); err = PTR_ERR(newdentry); if (IS_ERR(newdentry)) goto out_unlock; err = ovl_create_real(udir, newdentry, stat, link, hardlink, false); if (err) goto out_dput; ovl_instantiate(dentry, inode, newdentry, !!hardlink); newdentry = NULL; out_dput: dput(newdentry); out_unlock: inode_unlock(udir); return err; }
static int dev_rmdir(const char *name) { struct nameidata nd; struct dentry *dentry; int err; err = kern_path_parent(name, &nd); if (err) return err; mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT); dentry = lookup_one_len(nd.last.name, nd.path.dentry, nd.last.len); if (!IS_ERR(dentry)) { if (dentry->d_inode) { if (dentry->d_inode->i_private == &thread) err = vfs_rmdir(nd.path.dentry->d_inode, dentry); else err = -EPERM; } else { err = -ENOENT; } dput(dentry); } else { err = PTR_ERR(dentry); } mutex_unlock(&nd.path.dentry->d_inode->i_mutex); path_put(&nd.path); return err; }
static int fill_with_dentries(void *buf, const char *name, int namelen, loff_t offset, u64 ino, unsigned int d_type) { struct reiserfs_dentry_buf *dbuf = buf; struct dentry *dentry; WARN_ON_ONCE(!mutex_is_locked(&dbuf->xadir->d_inode->i_mutex)); if (dbuf->count == ARRAY_SIZE(dbuf->dentries)) return -ENOSPC; if (name[0] == '.' && (namelen < 2 || (namelen == 2 && name[1] == '.'))) return 0; dentry = lookup_one_len(name, dbuf->xadir, namelen); if (IS_ERR(dentry)) { return PTR_ERR(dentry); } else if (!dentry->d_inode) { /* A directory entry exists, but no file? */ reiserfs_error(dentry->d_sb, "xattr-20003", "Corrupted directory: xattr %s listed but " "not found for file %s.\n", dentry->d_name.name, dbuf->xadir->d_name.name); dput(dentry); return -EIO; } dbuf->dentries[dbuf->count++] = dentry; return 0; }
static struct dentry *ovl_workdir_create(struct vfsmount *mnt, struct dentry *dentry) { struct inode *dir = dentry->d_inode; struct dentry *work; int err; bool retried = false; err = mnt_want_write(mnt); if (err) return ERR_PTR(err); mutex_lock_nested(&dir->i_mutex, I_MUTEX_PARENT); retry: work = lookup_one_len(OVL_WORKDIR_NAME, dentry, strlen(OVL_WORKDIR_NAME)); if (!IS_ERR(work)) { struct kstat stat = { .mode = S_IFDIR | 0, }; if (work->d_inode) { err = -EEXIST; if (retried) goto out_dput; retried = true; ovl_cleanup(dir, work); dput(work); goto retry; } err = ovl_create_real(dir, work, &stat, NULL, NULL, true); if (err) goto out_dput; } out_unlock: mutex_unlock(&dir->i_mutex); mnt_drop_write(mnt); return work; out_dput: dput(work); work = ERR_PTR(err); goto out_unlock; } static int ovl_mount_dir(const char *name, struct path *path) { int err; err = kern_path(name, LOOKUP_FOLLOW, path); if (err) { pr_err("overlayfs: failed to resolve '%s': %i\n", name, err); err = -EINVAL; } return err; }
static int remove_device_files(struct super_block *sb, struct ipath_devdata *dd) { struct dentry *dir, *root; char unit[10]; int ret; root = dget(sb->s_root); mutex_lock(&root->d_inode->i_mutex); snprintf(unit, sizeof unit, "%02d", dd->ipath_unit); dir = lookup_one_len(unit, root, strlen(unit)); if (IS_ERR(dir)) { ret = PTR_ERR(dir); printk(KERN_ERR "Lookup of %s failed\n", unit); goto bail; } remove_file(dir, "flash"); remove_file(dir, "atomic_counters"); d_delete(dir); ret = simple_rmdir(root->d_inode, dir); bail: mutex_unlock(&root->d_inode->i_mutex); dput(root); return ret; }
static struct dentry *open_xa_dir(const struct inode *inode, int flags) { struct dentry *xaroot, *xadir; char namebuf[17]; xaroot = open_xa_root(inode->i_sb, flags); if (IS_ERR(xaroot)) return xaroot; snprintf(namebuf, sizeof(namebuf), "%X.%X", le32_to_cpu(INODE_PKEY(inode)->k_objectid), inode->i_generation); mutex_lock_nested(&xaroot->d_inode->i_mutex, I_MUTEX_XATTR); xadir = lookup_one_len(namebuf, xaroot, strlen(namebuf)); if (!IS_ERR(xadir) && !xadir->d_inode) { int err = -ENODATA; if (xattr_may_create(flags)) err = xattr_mkdir(xaroot->d_inode, xadir, 0700); if (err) { dput(xadir); xadir = ERR_PTR(err); } } mutex_unlock(&xaroot->d_inode->i_mutex); dput(xaroot); return xadir; }
static int dev_rmdir(const char *name) { struct nameidata nd; struct dentry *dentry; int err; struct vfsmount *dev_mnt = ve_devmnt(); err = vfs_path_lookup(dev_mnt->mnt_root, dev_mnt, name, LOOKUP_PARENT, &nd); if (err) return err; mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT); dentry = lookup_one_len(nd.last.name, nd.path.dentry, nd.last.len); if (!IS_ERR(dentry)) { if (dentry->d_inode) err = vfs_rmdir(nd.path.dentry->d_inode, dentry); else err = -ENOENT; dput(dentry); } else { err = PTR_ERR(dentry); } mutex_unlock(&nd.path.dentry->d_inode->i_mutex); path_put(&nd.path); return err; }
/** * sysfs_update_file - update the modified timestamp on an object attribute. * @kobj: object we're acting for. * @attr: attribute descriptor. */ int sysfs_update_file(struct kobject * kobj, const struct attribute * attr) { struct dentry * dir = kobj->dentry; struct dentry * victim; int res = -ENOENT; mutex_lock(&dir->d_inode->i_mutex); victim = lookup_one_len(attr->name, dir, strlen(attr->name)); if (!IS_ERR(victim)) { /* make sure dentry is really there */ if (victim->d_inode && (victim->d_parent->d_inode == dir->d_inode)) { victim->d_inode->i_mtime = CURRENT_TIME; fsnotify_modify(victim); res = 0; } else d_drop(victim); /** * Drop the reference acquired from lookup_one_len() above. */ dput(victim); } mutex_unlock(&dir->d_inode->i_mutex); return res; }
static int ovl_whiteout(struct dentry *upperdir, struct dentry *dentry) { int err; struct dentry *newdentry; const struct cred *old_cred; struct cred *override_cred; /* FIXME: recheck lower dentry to see if whiteout is really needed */ err = -ENOMEM; override_cred = ovl_prepare_creds(dentry->d_sb); if (!override_cred) goto out; override_cred->fsuid = make_kuid(override_cred->user_ns, 0); if (!uid_valid(override_cred->fsuid)) override_cred->fsuid = GLOBAL_ROOT_UID; override_cred->fsgid = make_kgid(override_cred->user_ns, 0); if (!gid_valid(override_cred->fsgid)) override_cred->fsgid = GLOBAL_ROOT_GID; old_cred = override_creds(override_cred); newdentry = lookup_one_len(dentry->d_name.name, upperdir, dentry->d_name.len); err = PTR_ERR(newdentry); if (IS_ERR(newdentry)) goto out_put_cred; /* Just been removed within the same locked region */ WARN_ON(newdentry->d_inode); err = vfs_symlink(upperdir->d_inode, newdentry, ovl_whiteout_symlink); if (err) goto out_dput; ovl_dentry_version_inc(dentry->d_parent); err = ovl_do_setxattr(newdentry, ovl_whiteout_xattr, "y", 1, 0); if (err) vfs_unlink(upperdir->d_inode, newdentry, NULL); out_dput: dput(newdentry); out_put_cred: revert_creds(old_cred); put_cred(override_cred); out: if (err) { /* * There's no way to recover from failure to whiteout. * What should we do? Log a big fat error and... ? */ pr_err("overlayfs: ERROR - failed to whiteout '%s'\n", dentry->d_name.name); } return err; }
/* * Get root dentry from superblock according to prefix path mount option. * Return dentry with refcount + 1 on success and NULL otherwise. */ static struct dentry * cifs_get_root(struct smb_vol *vol, struct super_block *sb) { struct dentry *dentry; struct cifs_sb_info *cifs_sb = CIFS_SB(sb); char *full_path = NULL; char *s, *p; char sep; int xid; full_path = cifs_build_path_to_root(vol, cifs_sb, cifs_sb_master_tcon(cifs_sb)); if (full_path == NULL) return ERR_PTR(-ENOMEM); cFYI(1, "Get root dentry for %s", full_path); xid = GetXid(); sep = CIFS_DIR_SEP(cifs_sb); dentry = dget(sb->s_root); p = s = full_path; do { struct inode *dir = dentry->d_inode; struct dentry *child; if (!dir) { dput(dentry); dentry = ERR_PTR(-ENOENT); break; } if (!S_ISDIR(dir->i_mode)) { dput(dentry); dentry = ERR_PTR(-ENOTDIR); break; } /* skip separators */ while (*s == sep) s++; if (!*s) break; p = s++; /* next separator */ while (*s && *s != sep) s++; mutex_lock(&dir->i_mutex); child = lookup_one_len(p, dentry, s - p); mutex_unlock(&dir->i_mutex); dput(dentry); dentry = child; } while (!IS_ERR(dentry)); _FreeXid(xid); kfree(full_path); return dentry; }
static void __init aafs_remove(const char *name) { struct dentry *dentry; dentry = lookup_one_len(name, aa_fs_dentry, strlen(name)); if (!IS_ERR(dentry)) { securityfs_remove(dentry); dput(dentry); } }
int do_lookup2 (struct inode *inode, const char *name, int len, struct dentry **result, int lock_f) { struct dentry *tmp_dent; struct dentry *r_dent; int ret_code; if ( lock_f ) DOWN(&(inode->i_sem)); /* Grab a dentry for the directory. */ tmp_dent = ovlfs_inode2dentry(inode); if ( tmp_dent == NULL ) { WARN("failed to obtain dentry for dir inode %lu", inode->i_ino); return -ENOENT; } /* Perform the lookup; this will call the filesystem's */ /* lookup entry-point, if needed. */ r_dent = lookup_one_len(name, tmp_dent, len); if ( IS_ERR(r_dent) ) { ret_code = PTR_ERR(r_dent); } else if ( r_dent == NULL ) { ret_code = -ENOENT; } else if ( r_dent->d_inode == NULL ) { dput(r_dent); ret_code = -ENOENT; } else { ret_code = 0; result[0] = r_dent; } dput(tmp_dent); if ( lock_f ) UP(&(inode->i_sem)); return ret_code; }
struct dentry *__create_file(const char *name, umode_t mode, struct dentry *parent, void *data, const struct file_operations *fops) { struct dentry *dentry = NULL; int error; pr_debug("debugfs: creating file '%s'\n",name); error = simple_pin_fs(&debug_fs_type, &debugfs_mount, &debugfs_mount_count); if (error) goto exit; /* If the parent is not specified, we create it in the root. * We need the root dentry to do this, which is in the super * block. A pointer to that is in the struct vfsmount that we * have around. */ if (!parent) parent = debugfs_mount->mnt_root; dentry = NULL; mutex_lock(&parent->d_inode->i_mutex); dentry = lookup_one_len(name, parent, strlen(name)); if (!IS_ERR(dentry)) { switch (mode & S_IFMT) { case S_IFDIR: error = debugfs_mkdir(parent->d_inode, dentry, mode); break; case S_IFLNK: error = debugfs_link(parent->d_inode, dentry, mode, data); break; default: error = debugfs_create(parent->d_inode, dentry, mode, data, fops); break; } dput(dentry); } else error = PTR_ERR(dentry); mutex_unlock(&parent->d_inode->i_mutex); if (error) { dentry = NULL; simple_release_fs(&debugfs_mount, &debugfs_mount_count); } exit: return dentry; }
int devtmpfs_delete_node(struct device *dev) { const char *tmp = NULL; const char *nodename; const struct cred *curr_cred; struct nameidata nd; struct dentry *dentry; struct kstat stat; int deleted = 1; int err; if (!dev_mnt) return 0; nodename = device_get_devnode(dev, NULL, &tmp); if (!nodename) return -ENOMEM; curr_cred = override_creds(&init_cred); err = vfs_path_lookup(dev_mnt->mnt_root, dev_mnt, nodename, LOOKUP_PARENT, &nd); if (err) goto out; mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT); dentry = lookup_one_len(nd.last.name, nd.path.dentry, nd.last.len); if (!IS_ERR(dentry)) { if (dentry->d_inode) { err = vfs_getattr(nd.path.mnt, dentry, &stat); if (!err && dev_mynode(dev, dentry->d_inode, &stat)) { err = vfs_unlink(nd.path.dentry->d_inode, dentry); if (!err || err == -ENOENT) deleted = 1; } } else { err = -ENOENT; } dput(dentry); } else { err = PTR_ERR(dentry); } mutex_unlock(&nd.path.dentry->d_inode->i_mutex); path_put(&nd.path); if (deleted && strchr(nodename, '/')) delete_path(nodename); out: kfree(tmp); revert_creds(curr_cred); return err; }
struct dentry *vfsub_lookup_one_len(const char *name, struct dentry *parent, int len) { struct dentry *d; LKTRTrace("%.*s/%.*s\n", AuDLNPair(parent), len, name); IMustLock(parent->d_inode); d = lookup_one_len(name, parent, len); if (!IS_ERR(d)) au_update_fuse_h_inode(NULL, d); /*ignore*/ return d; }
/* * Create a new subvolume below @parent. This is largely modeled after * sys_mkdirat and vfs_mkdir, but we only do a single component lookup * inside this filesystem so it's quite a bit simpler. */ static noinline int btrfs_mksubvol(struct path *parent, char *name, int namelen, struct btrfs_root *snap_src) { struct inode *dir = parent->dentry->d_inode; struct dentry *dentry; int error; mutex_lock_nested(&dir->i_mutex, I_MUTEX_PARENT); dentry = lookup_one_len(name, parent->dentry, namelen); error = PTR_ERR(dentry); if (IS_ERR(dentry)) goto out_unlock; error = -EEXIST; if (dentry->d_inode) goto out_dput; error = mnt_want_write(parent->mnt); if (error) goto out_dput; error = btrfs_may_create(dir, dentry); if (error) goto out_drop_write; down_read(&BTRFS_I(dir)->root->fs_info->subvol_sem); if (btrfs_root_refs(&BTRFS_I(dir)->root->root_item) == 0) goto out_up_read; if (snap_src) { error = create_snapshot(snap_src, dentry, name, namelen); } else { error = create_subvol(BTRFS_I(dir)->root, dentry, name, namelen); } if (!error) fsnotify_mkdir(dir, dentry); out_up_read: up_read(&BTRFS_I(dir)->root->fs_info->subvol_sem); out_drop_write: mnt_drop_write(parent->mnt); out_dput: dput(dentry); out_unlock: mutex_unlock(&dir->i_mutex); return error; }
static int handle_remove(const char *nodename, struct device *dev) { struct nameidata nd; struct dentry *dentry; struct kstat stat; int deleted = 1; int err; err = kern_path_parent(nodename, &nd); if (err) return err; mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT); dentry = lookup_one_len(nd.last.name, nd.path.dentry, nd.last.len); if (!IS_ERR(dentry)) { if (dentry->d_inode) { err = vfs_getattr(nd.path.mnt, dentry, &stat); if (!err && dev_mynode(dev, dentry->d_inode, &stat)) { struct iattr newattrs; /* * before unlinking this node, reset permissions * of possible references like hardlinks */ newattrs.ia_uid = 0; newattrs.ia_gid = 0; newattrs.ia_mode = stat.mode & ~0777; newattrs.ia_valid = ATTR_UID|ATTR_GID|ATTR_MODE; mutex_lock(&dentry->d_inode->i_mutex); notify_change(dentry, &newattrs); mutex_unlock(&dentry->d_inode->i_mutex); err = vfs_unlink(nd.path.dentry->d_inode, dentry); if (!err || err == -ENOENT) deleted = 1; } } else { err = -ENOENT; } dput(dentry); } else { err = PTR_ERR(dentry); } mutex_unlock(&nd.path.dentry->d_inode->i_mutex); path_put(&nd.path); if (deleted && strchr(nodename, '/')) delete_path(nodename); return err; }