static int jffs2_link (struct dentry *old_dentry, struct inode *dir_i, struct dentry *dentry) { struct jffs2_sb_info *c = JFFS2_SB_INFO(old_dentry->d_sb); struct jffs2_inode_info *f = JFFS2_INODE_INFO(d_inode(old_dentry)); struct jffs2_inode_info *dir_f = JFFS2_INODE_INFO(dir_i); int ret; uint8_t type; uint32_t now; /* Don't let people make hard links to bad inodes. */ if (!f->inocache) return -EIO; if (d_is_dir(old_dentry)) return -EPERM; /* XXX: This is ugly */ type = (d_inode(old_dentry)->i_mode & S_IFMT) >> 12; if (!type) type = DT_REG; now = get_seconds(); ret = jffs2_do_link(c, dir_f, f->inocache->ino, type, dentry->d_name.name, dentry->d_name.len, now); if (!ret) { mutex_lock(&f->sem); set_nlink(d_inode(old_dentry), ++f->inocache->pino_nlink); mutex_unlock(&f->sem); d_instantiate(dentry, d_inode(old_dentry)); dir_i->i_mtime = dir_i->i_ctime = ITIME(now); ihold(d_inode(old_dentry)); } return ret; }
static int hfsplus_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry, unsigned int flags) { int res; if (flags & ~RENAME_NOREPLACE) return -EINVAL; /* Unlink destination if it already exists */ if (d_really_is_positive(new_dentry)) { if (d_is_dir(new_dentry)) res = hfsplus_rmdir(new_dir, new_dentry); else res = hfsplus_unlink(new_dir, new_dentry); if (res) return res; } res = hfsplus_rename_cat((u32)(unsigned long)old_dentry->d_fsdata, old_dir, &old_dentry->d_name, new_dir, &new_dentry->d_name); if (!res) new_dentry->d_fsdata = old_dentry->d_fsdata; return res; }
/* Caller must hold i_mutex on both workdir and dir */ int ovl_cleanup_and_whiteout(struct dentry *workdir, struct inode *dir, struct dentry *dentry) { struct inode *wdir = workdir->d_inode; struct dentry *whiteout; int err; int flags = 0; whiteout = ovl_whiteout(workdir); err = PTR_ERR(whiteout); if (IS_ERR(whiteout)) return err; if (d_is_dir(dentry)) flags = RENAME_EXCHANGE; err = ovl_do_rename(wdir, whiteout, dir, dentry, flags); if (err) goto kill_whiteout; if (flags) ovl_cleanup(wdir, dentry); out: dput(whiteout); return err; kill_whiteout: ovl_cleanup(wdir, whiteout); goto out; }
static int ovl_mount_dir_noesc(const char *name, struct path *path) { int err = -EINVAL; if (!*name) { pr_err("overlayfs: empty lowerdir\n"); goto out; } err = kern_path(name, LOOKUP_FOLLOW, path); if (err) { pr_err("overlayfs: failed to resolve '%s': %i\n", name, err); goto out; } err = -EINVAL; if (ovl_dentry_weird(path->dentry)) { pr_err("overlayfs: filesystem on '%s' not supported\n", name); goto out_put; } if (!d_is_dir(path->dentry)) { pr_err("overlayfs: '%s' not a directory\n", name); goto out_put; } return 0; out_put: path_put_init(path); out: return err; }
struct inode *ovl_d_select_inode(struct dentry *dentry, unsigned file_flags) { int err; struct path realpath; enum ovl_path_type type; if (d_is_dir(dentry)) return d_backing_inode(dentry); type = ovl_path_real(dentry, &realpath); if (ovl_open_need_copy_up(file_flags, type, realpath.dentry)) { err = ovl_want_write(dentry); if (err) return ERR_PTR(err); if (file_flags & O_TRUNC) err = ovl_copy_up_truncate(dentry); else err = ovl_copy_up(dentry); ovl_drop_write(dentry); if (err) return ERR_PTR(err); ovl_path_upper(dentry, &realpath); } if (realpath.dentry->d_flags & DCACHE_OP_SELECT_INODE) return realpath.dentry->d_op->d_select_inode(realpath.dentry, file_flags); return d_backing_inode(realpath.dentry); }
static int fuse_setattr(struct dentry *entry, struct iattr *attr) { struct inode *inode = d_inode(entry); struct fuse_conn *fc = get_fuse_conn(inode); struct file *file = (attr->ia_valid & ATTR_FILE) ? attr->ia_file : NULL; int ret; if (!fuse_allow_current_process(get_fuse_conn(inode))) return -EACCES; if (attr->ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID)) { attr->ia_valid &= ~(ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_MODE); /* * The only sane way to reliably kill suid/sgid is to do it in * the userspace filesystem * * This should be done on write(), truncate() and chown(). */ if (!fc->handle_killpriv) { /* * ia_mode calculation may have used stale i_mode. * Refresh and recalculate. */ ret = fuse_do_getattr(inode, NULL, file); if (ret) return ret; attr->ia_mode = inode->i_mode; if (inode->i_mode & S_ISUID) { attr->ia_valid |= ATTR_MODE; attr->ia_mode &= ~S_ISUID; } if ((inode->i_mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) { attr->ia_valid |= ATTR_MODE; attr->ia_mode &= ~S_ISGID; } } } if (!attr->ia_valid) return 0; ret = fuse_do_setattr(entry, attr, file); if (!ret) { /* * If filesystem supports acls it may have updated acl xattrs in * the filesystem, so forget cached acls for the inode. */ if (fc->posix_acl) forget_all_cached_acls(inode); /* Directory mode changed, may need to revalidate access */ if (d_is_dir(entry) && (attr->ia_valid & ATTR_MODE)) fuse_invalidate_entry_cache(entry); } return ret; }
loff_t au_dir_size(struct file *file, struct dentry *dentry) { loff_t sz; aufs_bindex_t bindex, bbot; struct file *h_file; struct dentry *h_dentry; sz = 0; if (file) { AuDebugOn(!d_is_dir(file->f_path.dentry)); bbot = au_fbbot_dir(file); for (bindex = au_fbtop(file); bindex <= bbot && sz < KMALLOC_MAX_SIZE; bindex++) { h_file = au_hf_dir(file, bindex); if (h_file && file_inode(h_file)) sz += vfsub_f_size_read(h_file); } } else { AuDebugOn(!dentry); AuDebugOn(!d_is_dir(dentry)); bbot = au_dbtaildir(dentry); for (bindex = au_dbtop(dentry); bindex <= bbot && sz < KMALLOC_MAX_SIZE; bindex++) { h_dentry = au_h_dptr(dentry, bindex); if (h_dentry && d_is_positive(h_dentry)) sz += i_size_read(d_inode(h_dentry)); } } if (sz < KMALLOC_MAX_SIZE) sz = roundup_pow_of_two(sz); if (sz > KMALLOC_MAX_SIZE) sz = KMALLOC_MAX_SIZE; else if (sz < NAME_MAX) { BUILD_BUG_ON(AUFS_RDBLK_DEF < NAME_MAX); sz = AUFS_RDBLK_DEF; } return sz; }
static void au_hide(struct dentry *dentry) { int err; AuDbgDentry(dentry); if (d_is_dir(dentry)) { /* shrink_dcache_parent(dentry); */ err = au_hide_children(dentry); if (unlikely(err)) AuIOErr("%pd, failed hiding children, ignored %d\n", dentry, err); } au_do_hide(dentry); }
static void hypfs_remove(struct dentry *dentry) { struct dentry *parent; parent = dentry->d_parent; inode_lock(d_inode(parent)); if (simple_positive(dentry)) { if (d_is_dir(dentry)) simple_rmdir(d_inode(parent), dentry); else simple_unlink(d_inode(parent), dentry); } d_delete(dentry); dput(dentry); inode_unlock(d_inode(parent)); }
static void hypfs_remove(struct dentry *dentry) { struct dentry *parent; parent = dentry->d_parent; mutex_lock(&parent->d_inode->i_mutex); if (hypfs_positive(dentry)) { if (d_is_dir(dentry)) simple_rmdir(parent->d_inode, dentry); else simple_unlink(parent->d_inode, dentry); } d_delete(dentry); dput(dentry); mutex_unlock(&parent->d_inode->i_mutex); }
static struct dentry *jffs2_get_parent(struct dentry *child) { struct jffs2_inode_info *f; uint32_t pino; BUG_ON(!d_is_dir(child)); f = JFFS2_INODE_INFO(d_inode(child)); pino = f->inocache->pino_nlink; JFFS2_DEBUG("Parent of directory ino #%u is #%u\n", f->inocache->ino, pino); return d_obtain_alias(jffs2_iget(d_inode(child)->i_sb, pino)); }
void ovl_cleanup(struct inode *wdir, struct dentry *wdentry) { int err; dget(wdentry); if (d_is_dir(wdentry)) err = ovl_do_rmdir(wdir, wdentry); else err = ovl_do_unlink(wdir, wdentry); dput(wdentry); if (err) { pr_err("overlayfs: cleanup of '%pd2' failed (%i)\n", wdentry, err); } }
static void au_do_hide(struct dentry *dentry) { struct inode *inode; if (d_really_is_positive(dentry)) { inode = d_inode(dentry); if (!d_is_dir(dentry)) { if (inode->i_nlink && !d_unhashed(dentry)) drop_nlink(inode); } else { clear_nlink(inode); /* stop next lookup */ inode->i_flags |= S_DEAD; } smp_mb(); /* necessary? */ } d_drop(dentry); }
static int fuse_setattr(struct dentry *entry, struct iattr *attr) { struct inode *inode = d_inode(entry); struct file *file = (attr->ia_valid & ATTR_FILE) ? attr->ia_file : NULL; int ret; if (!fuse_allow_current_process(get_fuse_conn(inode))) return -EACCES; if (attr->ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID)) { int kill; attr->ia_valid &= ~(ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_MODE); /* * ia_mode calculation may have used stale i_mode. Refresh and * recalculate. */ ret = fuse_do_getattr(inode, NULL, file); if (ret) return ret; attr->ia_mode = inode->i_mode; kill = should_remove_suid(entry); if (kill & ATTR_KILL_SUID) { attr->ia_valid |= ATTR_MODE; attr->ia_mode &= ~S_ISUID; } if (kill & ATTR_KILL_SGID) { attr->ia_valid |= ATTR_MODE; attr->ia_mode &= ~S_ISGID; } } if (!attr->ia_valid) return 0; ret = fuse_do_setattr(inode, attr, file); if (!ret) { /* Directory mode changed, may need to revalidate access */ if (d_is_dir(entry) && (attr->ia_valid & ATTR_MODE)) fuse_invalidate_entry_cache(entry); } return ret; }
__u32 gr_acl_handle_creat(const struct dentry * dentry, const struct dentry * p_dentry, const struct vfsmount * p_mnt, int open_flags, int acc_mode, const int imode) { __u32 reqmode = GR_WRITE | GR_CREATE; __u32 mode; if (acc_mode & MAY_APPEND) reqmode |= GR_APPEND; // if a directory was required or the directory already exists, then // don't count this open as a read if ((acc_mode & MAY_READ) && !((open_flags & O_DIRECTORY) || d_is_dir(dentry))) reqmode |= GR_READ; if ((open_flags & O_CREAT) && ((imode & S_ISUID) || ((imode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)))) reqmode |= GR_SETID; mode = gr_check_create(dentry, p_dentry, p_mnt, reqmode | to_gr_audit(reqmode) | GR_SUPPRESS); if (unlikely(((mode & reqmode) == reqmode) && mode & GR_AUDITS)) { gr_log_fs_rbac_mode2(GR_DO_AUDIT, GR_CREATE_ACL_MSG, dentry, p_mnt, reqmode & GR_READ ? " reading" : "", reqmode & GR_WRITE ? " writing" : reqmode & GR_APPEND ? " appending" : ""); return reqmode; } else if (unlikely((mode & reqmode) != reqmode && !(mode & GR_SUPPRESS))) { gr_log_fs_rbac_mode2(GR_DONT_AUDIT, GR_CREATE_ACL_MSG, dentry, p_mnt, reqmode & GR_READ ? " reading" : "", reqmode & GR_WRITE ? " writing" : reqmode & GR_APPEND ? " appending" : ""); return 0; } else if (unlikely((mode & reqmode) != reqmode)) return 0; return reqmode; }
/** * securityfs_remove - removes a file or directory from the securityfs filesystem * * @dentry: a pointer to a the dentry of the file or directory to be removed. * * This function removes a file or directory in securityfs that was previously * created with a call to another securityfs function (like * securityfs_create_file() or variants thereof.) * * This function is required to be called in order for the file to be * removed. No automatic cleanup of files will happen when a module is * removed; you are responsible here. */ void securityfs_remove(struct dentry *dentry) { struct inode *dir; if (!dentry || IS_ERR(dentry)) return; dir = d_inode(dentry->d_parent); inode_lock(dir); if (simple_positive(dentry)) { if (d_is_dir(dentry)) simple_rmdir(dir, dentry); else simple_unlink(dir, dentry); dput(dentry); } inode_unlock(dir); simple_release_fs(&mount, &mount_count); }
__u32 gr_acl_handle_chmod(const struct dentry *dentry, const struct vfsmount *mnt, umode_t *modeptr) { umode_t mode; struct inode *inode = d_backing_inode(dentry); *modeptr &= ~gr_acl_umask(); mode = *modeptr; if (unlikely(inode && S_ISSOCK(inode->i_mode))) return 1; if (unlikely(!d_is_dir(dentry) && ((mode & S_ISUID) || ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP))))) { return generic_fs_handler(dentry, mnt, GR_WRITE | GR_SETID, GR_CHMOD_ACL_MSG); } else { return generic_fs_handler(dentry, mnt, GR_WRITE, GR_CHMOD_ACL_MSG); } }
__u32 gr_acl_handle_open(const struct dentry * dentry, const struct vfsmount * mnt, int acc_mode) { __u32 reqmode = GR_FIND; __u32 mode; if (unlikely(d_is_negative(dentry))) return reqmode; if (acc_mode & MAY_APPEND) reqmode |= GR_APPEND; else if (acc_mode & MAY_WRITE) reqmode |= GR_WRITE; if ((acc_mode & MAY_READ) && !d_is_dir(dentry)) reqmode |= GR_READ; mode = gr_search_file(dentry, reqmode | to_gr_audit(reqmode) | GR_SUPPRESS, mnt); if (unlikely(((mode & reqmode) == reqmode) && mode & GR_AUDITS)) { gr_log_fs_rbac_mode2(GR_DO_AUDIT, GR_OPEN_ACL_MSG, dentry, mnt, reqmode & GR_READ ? " reading" : "", reqmode & GR_WRITE ? " writing" : reqmode & GR_APPEND ? " appending" : ""); return reqmode; } else if (unlikely((mode & reqmode) != reqmode && !(mode & GR_SUPPRESS))) { gr_log_fs_rbac_mode2(GR_DONT_AUDIT, GR_OPEN_ACL_MSG, dentry, mnt, reqmode & GR_READ ? " reading" : "", reqmode & GR_WRITE ? " writing" : reqmode & GR_APPEND ? " appending" : ""); return 0; } else if (unlikely((mode & reqmode) != reqmode)) return 0; return reqmode; }
/** * securityfs_remove - removes a file or directory from the securityfs filesystem * * @dentry: a pointer to a the dentry of the file or directory to be removed. * * This function removes a file or directory in securityfs that was previously * created with a call to another securityfs function (like * securityfs_create_file() or variants thereof.) * * This function is required to be called in order for the file to be * removed. No automatic cleanup of files will happen when a module is * removed; you are responsible here. */ void securityfs_remove(struct dentry *dentry) { struct dentry *parent; if (!dentry || IS_ERR(dentry)) return; parent = dentry->d_parent; if (!parent || d_really_is_negative(parent)) return; inode_lock(d_inode(parent)); if (simple_positive(dentry)) { if (d_is_dir(dentry)) simple_rmdir(d_inode(parent), dentry); else simple_unlink(d_inode(parent), dentry); dput(dentry); } inode_unlock(d_inode(parent)); simple_release_fs(&mount, &mount_count); }
static int ovl_create_upper(struct dentry *dentry, struct inode *inode, struct ovl_cattr *attr) { struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent); struct inode *udir = upperdir->d_inode; struct dentry *newdentry; int err; if (!attr->hardlink && !IS_POSIXACL(udir)) attr->mode &= ~current_umask(); inode_lock_nested(udir, I_MUTEX_PARENT); newdentry = ovl_create_real(udir, lookup_one_len(dentry->d_name.name, upperdir, dentry->d_name.len), attr); err = PTR_ERR(newdentry); if (IS_ERR(newdentry)) goto out_unlock; if (ovl_type_merge(dentry->d_parent) && d_is_dir(newdentry)) { /* Setting opaque here is just an optimization, allow to fail */ ovl_set_opaque(dentry, newdentry); } err = ovl_instantiate(dentry, inode, newdentry, !!attr->hardlink); if (err) goto out_cleanup; out_unlock: inode_unlock(udir); return err; out_cleanup: ovl_cleanup(udir, newdentry); dput(newdentry); goto out_unlock; }
/** * securityfs_remove - removes a file or directory from the securityfs filesystem * * @dentry: a pointer to a the dentry of the file or directory to be removed. * * This function removes a file or directory in securityfs that was previously * created with a call to another securityfs function (like * securityfs_create_file() or variants thereof.) * * This function is required to be called in order for the file to be * removed. No automatic cleanup of files will happen when a module is * removed; you are responsible here. */ void securityfs_remove(struct dentry *dentry) { struct dentry *parent; if (!dentry || IS_ERR(dentry)) return; parent = dentry->d_parent; if (!parent || !parent->d_inode) return; mutex_lock(&parent->d_inode->i_mutex); if (positive(dentry)) { if (dentry->d_inode) { if (d_is_dir(dentry)) simple_rmdir(parent->d_inode, dentry); else simple_unlink(parent->d_inode, dentry); dput(dentry); } } mutex_unlock(&parent->d_inode->i_mutex); simple_release_fs(&mount, &mount_count); }
__u32 gr_acl_handle_access(const struct dentry * dentry, const struct vfsmount * mnt, const int fmode) { __u32 mode, reqmode = GR_FIND; if ((fmode & S_IXOTH) && !d_is_dir(dentry)) reqmode |= GR_EXEC; if (fmode & S_IWOTH) reqmode |= GR_WRITE; if (fmode & S_IROTH) reqmode |= GR_READ; mode = gr_search_file(dentry, reqmode | to_gr_audit(reqmode) | GR_SUPPRESS, mnt); if (unlikely(((mode & reqmode) == reqmode) && mode & GR_AUDITS)) { gr_log_fs_rbac_mode3(GR_DO_AUDIT, GR_ACCESS_ACL_MSG, dentry, mnt, reqmode & GR_READ ? " reading" : "", reqmode & GR_WRITE ? " writing" : "", reqmode & GR_EXEC ? " executing" : ""); return reqmode; } else if (unlikely((mode & reqmode) != reqmode && !(mode & GR_SUPPRESS))) { gr_log_fs_rbac_mode3(GR_DONT_AUDIT, GR_ACCESS_ACL_MSG, dentry, mnt, reqmode & GR_READ ? " reading" : "", reqmode & GR_WRITE ? " writing" : "", reqmode & GR_EXEC ? " executing" : ""); return 0; } else if (unlikely((mode & reqmode) != reqmode)) return 0; return reqmode; }
int aufs_rmdir(struct inode *dir, struct dentry *dentry) { int err, rmdir_later; aufs_bindex_t bwh, bindex, bstart; struct inode *inode; struct dentry *parent, *wh_dentry, *h_dentry; struct au_whtmp_rmdir *args; /* to reuduce stack size */ struct { struct au_dtime dt; struct au_pin pin; } *a; IMustLock(dir); err = -ENOMEM; a = kmalloc(sizeof(*a), GFP_NOFS); if (unlikely(!a)) goto out; err = aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH | AuLock_GEN); if (unlikely(err)) goto out_free; err = au_alive_dir(dentry); if (unlikely(err)) goto out_unlock; inode = d_inode(dentry); IMustLock(inode); err = -ENOTDIR; if (unlikely(!d_is_dir(dentry))) goto out_unlock; /* possible? */ err = -ENOMEM; args = au_whtmp_rmdir_alloc(dir->i_sb, GFP_NOFS); if (unlikely(!args)) goto out_unlock; parent = dentry->d_parent; /* dir inode is locked */ di_write_lock_parent(parent); err = au_test_empty(dentry, &args->whlist); if (unlikely(err)) goto out_parent; bstart = au_dbstart(dentry); bwh = au_dbwh(dentry); bindex = -1; wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/1, &bindex, &a->dt, &a->pin); err = PTR_ERR(wh_dentry); if (IS_ERR(wh_dentry)) goto out_parent; h_dentry = au_h_dptr(dentry, bstart); dget(h_dentry); rmdir_later = 0; if (bindex == bstart) { err = renwh_and_rmdir(dentry, bstart, &args->whlist, dir); if (err > 0) { rmdir_later = err; err = 0; } } else { /* stop monitoring */ au_hn_free(au_hi(inode, bstart)); /* dir inode is locked */ IMustLock(d_inode(wh_dentry->d_parent)); err = 0; } if (!err) { vfsub_dead_dir(inode); au_set_dbdiropq(dentry, -1); epilog(dir, dentry, bindex); if (rmdir_later) { au_whtmp_kick_rmdir(dir, bstart, h_dentry, args); args = NULL; } goto out_unpin; /* success */ } /* revert */ AuLabel(revert); if (wh_dentry) { int rerr; rerr = do_revert(err, dir, bindex, bwh, wh_dentry, dentry, &a->dt); if (rerr) err = rerr; } out_unpin: au_unpin(&a->pin); dput(wh_dentry); dput(h_dentry); out_parent: di_write_unlock(parent); if (args) au_whtmp_rmdir_free(args); out_unlock: aufs_read_unlock(dentry, AuLock_DW); out_free: kfree(a); out: AuTraceErr(err); return err; }
/* * when an error happened, remove the created whiteout and revert everything. */ static int do_revert(int err, struct inode *dir, aufs_bindex_t bindex, aufs_bindex_t bwh, struct dentry *wh_dentry, struct dentry *dentry, struct au_dtime *dt) { int rerr; struct path h_path = { .dentry = wh_dentry, .mnt = au_sbr_mnt(dir->i_sb, bindex) }; rerr = au_wh_unlink_dentry(au_h_iptr(dir, bindex), &h_path, dentry); if (!rerr) { au_set_dbwh(dentry, bwh); au_dtime_revert(dt); return 0; } AuIOErr("%pd reverting whiteout failed(%d, %d)\n", dentry, err, rerr); return -EIO; } /* ---------------------------------------------------------------------- */ int aufs_unlink(struct inode *dir, struct dentry *dentry) { int err; aufs_bindex_t bwh, bindex, bstart; struct inode *inode, *h_dir, *delegated; struct dentry *parent, *wh_dentry; /* to reuduce stack size */ struct { struct au_dtime dt; struct au_pin pin; struct path h_path; } *a; IMustLock(dir); err = -ENOMEM; a = kmalloc(sizeof(*a), GFP_NOFS); if (unlikely(!a)) goto out; err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN); if (unlikely(err)) goto out_free; err = au_d_hashed_positive(dentry); if (unlikely(err)) goto out_unlock; inode = d_inode(dentry); IMustLock(inode); err = -EISDIR; if (unlikely(d_is_dir(dentry))) goto out_unlock; /* possible? */ bstart = au_dbstart(dentry); bwh = au_dbwh(dentry); bindex = -1; parent = dentry->d_parent; /* dir inode is locked */ di_write_lock_parent(parent); wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/0, &bindex, &a->dt, &a->pin); err = PTR_ERR(wh_dentry); if (IS_ERR(wh_dentry)) goto out_parent; a->h_path.mnt = au_sbr_mnt(dentry->d_sb, bstart); a->h_path.dentry = au_h_dptr(dentry, bstart); dget(a->h_path.dentry); if (bindex == bstart) { h_dir = au_pinned_h_dir(&a->pin); delegated = NULL; err = vfsub_unlink(h_dir, &a->h_path, &delegated, /*force*/0); if (unlikely(err == -EWOULDBLOCK)) { pr_warn("cannot retry for NFSv4 delegation" " for an internal unlink\n"); iput(delegated); } } else { /* dir inode is locked */ h_dir = d_inode(wh_dentry->d_parent); IMustLock(h_dir); err = 0; } if (!err) { vfsub_drop_nlink(inode); epilog(dir, dentry, bindex); /* update target timestamps */ if (bindex == bstart) { vfsub_update_h_iattr(&a->h_path, /*did*/NULL); /*ignore*/ inode->i_ctime = d_inode(a->h_path.dentry)->i_ctime; } else /* todo: this timestamp may be reverted later */ inode->i_ctime = h_dir->i_ctime; goto out_unpin; /* success */ } /* revert */ if (wh_dentry) { int rerr; rerr = do_revert(err, dir, bindex, bwh, wh_dentry, dentry, &a->dt); if (rerr) err = rerr; } out_unpin: au_unpin(&a->pin); dput(wh_dentry); dput(a->h_path.dentry); out_parent: di_write_unlock(parent); out_unlock: aufs_read_unlock(dentry, AuLock_DW); out_free: kfree(a); out: return err; }
static int jffs2_rename (struct inode *old_dir_i, struct dentry *old_dentry, struct inode *new_dir_i, struct dentry *new_dentry) { int ret; struct jffs2_sb_info *c = JFFS2_SB_INFO(old_dir_i->i_sb); struct jffs2_inode_info *victim_f = NULL; uint8_t type; uint32_t now; /* The VFS will check for us and prevent trying to rename a * file over a directory and vice versa, but if it's a directory, * the VFS can't check whether the victim is empty. The filesystem * needs to do that for itself. */ if (d_really_is_positive(new_dentry)) { victim_f = JFFS2_INODE_INFO(d_inode(new_dentry)); if (d_is_dir(new_dentry)) { struct jffs2_full_dirent *fd; mutex_lock(&victim_f->sem); for (fd = victim_f->dents; fd; fd = fd->next) { if (fd->ino) { mutex_unlock(&victim_f->sem); return -ENOTEMPTY; } } mutex_unlock(&victim_f->sem); } } /* XXX: We probably ought to alloc enough space for both nodes at the same time. Writing the new link, then getting -ENOSPC, is quite bad :) */ /* Make a hard link */ /* XXX: This is ugly */ type = (d_inode(old_dentry)->i_mode & S_IFMT) >> 12; if (!type) type = DT_REG; now = get_seconds(); ret = jffs2_do_link(c, JFFS2_INODE_INFO(new_dir_i), d_inode(old_dentry)->i_ino, type, new_dentry->d_name.name, new_dentry->d_name.len, now); if (ret) return ret; if (victim_f) { /* There was a victim. Kill it off nicely */ if (d_is_dir(new_dentry)) clear_nlink(d_inode(new_dentry)); else drop_nlink(d_inode(new_dentry)); /* Don't oops if the victim was a dirent pointing to an inode which didn't exist. */ if (victim_f->inocache) { mutex_lock(&victim_f->sem); if (d_is_dir(new_dentry)) victim_f->inocache->pino_nlink = 0; else victim_f->inocache->pino_nlink--; mutex_unlock(&victim_f->sem); } } /* If it was a directory we moved, and there was no victim, increase i_nlink on its new parent */ if (d_is_dir(old_dentry) && !victim_f) inc_nlink(new_dir_i); /* Unlink the original */ ret = jffs2_do_unlink(c, JFFS2_INODE_INFO(old_dir_i), old_dentry->d_name.name, old_dentry->d_name.len, NULL, now); /* We don't touch inode->i_nlink */ if (ret) { /* Oh shit. We really ought to make a single node which can do both atomically */ struct jffs2_inode_info *f = JFFS2_INODE_INFO(d_inode(old_dentry)); mutex_lock(&f->sem); inc_nlink(d_inode(old_dentry)); if (f->inocache && !d_is_dir(old_dentry)) f->inocache->pino_nlink++; mutex_unlock(&f->sem); pr_notice("%s(): Link succeeded, unlink failed (err %d). You now have a hard link\n", __func__, ret); /* * We can't keep the target in dcache after that. * For one thing, we can't afford dentry aliases for directories. * For another, if there was a victim, we _can't_ set new inode * for that sucker and we have to trigger mount eviction - the * caller won't do it on its own since we are returning an error. */ d_invalidate(new_dentry); new_dir_i->i_mtime = new_dir_i->i_ctime = ITIME(now); return ret; } if (d_is_dir(old_dentry)) drop_nlink(old_dir_i); new_dir_i->i_mtime = new_dir_i->i_ctime = old_dir_i->i_mtime = old_dir_i->i_ctime = ITIME(now); return 0; }
/* * returns the number of lower positive dentries, * otherwise an error. * can be called at unlinking with @type is zero. */ int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type) { int npositive, err; aufs_bindex_t bindex, btail, bdiropq; unsigned char isdir, dirperm1; struct qstr whname; struct au_do_lookup_args args = { .flags = 0, .type = type }; const struct qstr *name = &dentry->d_name; struct dentry *parent; struct super_block *sb; sb = dentry->d_sb; err = au_test_shwh(sb, name); if (unlikely(err)) goto out; err = au_wh_name_alloc(&whname, name); if (unlikely(err)) goto out; isdir = !!d_is_dir(dentry); if (!type) au_fset_lkup(args.flags, ALLOW_NEG); dirperm1 = !!au_opt_test(au_mntflags(sb), DIRPERM1); npositive = 0; parent = dget_parent(dentry); btail = au_dbtaildir(parent); for (bindex = bstart; bindex <= btail; bindex++) { struct dentry *h_parent, *h_dentry; struct inode *h_inode, *h_dir; h_dentry = au_h_dptr(dentry, bindex); if (h_dentry) { if (d_is_positive(h_dentry)) npositive++; if (type != S_IFDIR) break; continue; } h_parent = au_h_dptr(parent, bindex); if (!h_parent || !d_is_dir(h_parent)) continue; h_dir = d_inode(h_parent); mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_PARENT); h_dentry = au_do_lookup(h_parent, dentry, bindex, &whname, &args); mutex_unlock(&h_dir->i_mutex); err = PTR_ERR(h_dentry); if (IS_ERR(h_dentry)) goto out_parent; if (h_dentry) au_fclr_lkup(args.flags, ALLOW_NEG); if (dirperm1) au_fset_lkup(args.flags, IGNORE_PERM); if (au_dbwh(dentry) >= 0) break; if (!h_dentry) continue; if (d_is_negative(h_dentry)) continue; h_inode = d_inode(h_dentry); npositive++; if (!args.type) args.type = h_inode->i_mode & S_IFMT; if (args.type != S_IFDIR) break; else if (isdir) { /* the type of lower may be different */ bdiropq = au_dbdiropq(dentry); if (bdiropq >= 0 && bdiropq <= bindex) break; } } if (npositive) { AuLabel(positive); au_update_dbstart(dentry); } err = npositive; if (unlikely(!au_opt_test(au_mntflags(sb), UDBA_NONE) && au_dbstart(dentry) < 0)) { err = -EIO; AuIOErr("both of real entry and whiteout found, %pd, err %d\n", dentry, err); } out_parent: dput(parent); kfree(whname.name); out: return err; } struct dentry *au_sio_lkup_one(struct qstr *name, struct dentry *parent) { struct dentry *dentry; int wkq_err; if (!au_test_h_perm_sio(d_inode(parent), MAY_EXEC)) dentry = vfsub_lkup_one(name, parent); else { struct vfsub_lkup_one_args args = { .errp = &dentry, .name = name, .parent = parent }; wkq_err = au_wkq_wait(vfsub_call_lkup_one, &args); if (unlikely(wkq_err)) dentry = ERR_PTR(wkq_err); } return dentry; } /* * lookup @dentry on @bindex which should be negative. */ int au_lkup_neg(struct dentry *dentry, aufs_bindex_t bindex, int wh) { int err; struct dentry *parent, *h_parent, *h_dentry; struct au_branch *br; parent = dget_parent(dentry); h_parent = au_h_dptr(parent, bindex); br = au_sbr(dentry->d_sb, bindex); if (wh) h_dentry = au_whtmp_lkup(h_parent, br, &dentry->d_name); else h_dentry = au_sio_lkup_one(&dentry->d_name, h_parent); err = PTR_ERR(h_dentry); if (IS_ERR(h_dentry)) goto out; if (unlikely(d_is_positive(h_dentry))) { err = -EIO; AuIOErr("%pd should be negative on b%d.\n", h_dentry, bindex); dput(h_dentry); goto out; } err = 0; if (bindex < au_dbstart(dentry)) au_set_dbstart(dentry, bindex); if (au_dbend(dentry) < bindex) au_set_dbend(dentry, bindex); au_set_h_dptr(dentry, bindex, h_dentry); out: dput(parent); return err; }
/* * returns positive/negative dentry, NULL or an error. * NULL means whiteout-ed or not-found. */ static struct dentry* au_do_lookup(struct dentry *h_parent, struct dentry *dentry, aufs_bindex_t bindex, struct qstr *wh_name, struct au_do_lookup_args *args) { struct dentry *h_dentry; struct inode *h_inode; struct au_branch *br; int wh_found, opq; unsigned char wh_able; const unsigned char allow_neg = !!au_ftest_lkup(args->flags, ALLOW_NEG); const unsigned char ignore_perm = !!au_ftest_lkup(args->flags, IGNORE_PERM); wh_found = 0; br = au_sbr(dentry->d_sb, bindex); wh_able = !!au_br_whable(br->br_perm); if (wh_able) wh_found = au_wh_test(h_parent, wh_name, /*try_sio*/0); h_dentry = ERR_PTR(wh_found); if (!wh_found) goto real_lookup; if (unlikely(wh_found < 0)) goto out; /* We found a whiteout */ /* au_set_dbend(dentry, bindex); */ au_set_dbwh(dentry, bindex); if (!allow_neg) return NULL; /* success */ real_lookup: if (!ignore_perm) h_dentry = vfsub_lkup_one(&dentry->d_name, h_parent); else h_dentry = au_sio_lkup_one(&dentry->d_name, h_parent); if (IS_ERR(h_dentry)) { if (PTR_ERR(h_dentry) == -ENAMETOOLONG && !allow_neg) h_dentry = NULL; goto out; } h_inode = d_inode(h_dentry); if (d_is_negative(h_dentry)) { if (!allow_neg) goto out_neg; } else if (wh_found || (args->type && args->type != (h_inode->i_mode & S_IFMT))) goto out_neg; if (au_dbend(dentry) <= bindex) au_set_dbend(dentry, bindex); if (au_dbstart(dentry) < 0 || bindex < au_dbstart(dentry)) au_set_dbstart(dentry, bindex); au_set_h_dptr(dentry, bindex, h_dentry); if (!d_is_dir(h_dentry) || !wh_able || (d_really_is_positive(dentry) && !d_is_dir(dentry))) goto out; /* success */ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); opq = au_diropq_test(h_dentry); mutex_unlock(&h_inode->i_mutex); if (opq > 0) au_set_dbdiropq(dentry, bindex); else if (unlikely(opq < 0)) { au_set_h_dptr(dentry, bindex, NULL); h_dentry = ERR_PTR(opq); } goto out; out_neg: dput(h_dentry); h_dentry = NULL; out: return h_dentry; }
int fuse_reverse_inval_entry(struct super_block *sb, u64 parent_nodeid, u64 child_nodeid, struct qstr *name) { int err = -ENOTDIR; struct inode *parent; struct dentry *dir; struct dentry *entry; parent = ilookup5(sb, parent_nodeid, fuse_inode_eq, &parent_nodeid); if (!parent) return -ENOENT; inode_lock(parent); if (!S_ISDIR(parent->i_mode)) goto unlock; err = -ENOENT; dir = d_find_alias(parent); if (!dir) goto unlock; name->hash = full_name_hash(dir, name->name, name->len); entry = d_lookup(dir, name); dput(dir); if (!entry) goto unlock; fuse_invalidate_attr(parent); fuse_invalidate_entry(entry); if (child_nodeid != 0 && d_really_is_positive(entry)) { inode_lock(d_inode(entry)); if (get_node_id(d_inode(entry)) != child_nodeid) { err = -ENOENT; goto badentry; } if (d_mountpoint(entry)) { err = -EBUSY; goto badentry; } if (d_is_dir(entry)) { shrink_dcache_parent(entry); if (!simple_empty(entry)) { err = -ENOTEMPTY; goto badentry; } d_inode(entry)->i_flags |= S_DEAD; } dont_mount(entry); clear_nlink(d_inode(entry)); err = 0; badentry: inode_unlock(d_inode(entry)); if (!err) d_delete(entry); } else { err = 0; } dput(entry); unlock: inode_unlock(parent); iput(parent); return err; }
/** * tomoyo_update_path_number_acl - Update ioctl/chmod/chown/chgrp ACL. * * @perm: Permission. * @param: Pointer to "struct tomoyo_acl_param". * * Returns 0 on success, negative value otherwise. */ static int tomoyo_update_path_number_acl(const u8 perm, struct tomoyo_acl_param *param) { struct tomoyo_path_number_acl e = { .head.type = TOMOYO_TYPE_PATH_NUMBER_ACL, .perm = perm }; int error; if (!tomoyo_parse_name_union(param, &e.name) || !tomoyo_parse_number_union(param, &e.number)) error = -EINVAL; else error = tomoyo_update_domain(&e.head, sizeof(e), param, tomoyo_same_path_number_acl, tomoyo_merge_path_number_acl); tomoyo_put_name_union(&e.name); tomoyo_put_number_union(&e.number); return error; } /** * tomoyo_path_number_perm - Check permission for "create", "mkdir", "mkfifo", "mksock", "ioctl", "chmod", "chown", "chgrp". * * @type: Type of operation. * @path: Pointer to "struct path". * @number: Number. * * Returns 0 on success, negative value otherwise. */ int tomoyo_path_number_perm(const u8 type, struct path *path, unsigned long number) { struct tomoyo_request_info r; struct tomoyo_obj_info obj = { .path1 = *path, }; int error = -ENOMEM; struct tomoyo_path_info buf; int idx; if (tomoyo_init_request_info(&r, NULL, tomoyo_pn2mac[type]) == TOMOYO_CONFIG_DISABLED || !path->dentry) return 0; idx = tomoyo_read_lock(); if (!tomoyo_get_realpath(&buf, path)) goto out; r.obj = &obj; if (type == TOMOYO_TYPE_MKDIR) tomoyo_add_slash(&buf); r.param_type = TOMOYO_TYPE_PATH_NUMBER_ACL; r.param.path_number.operation = type; r.param.path_number.filename = &buf; r.param.path_number.number = number; do { tomoyo_check_acl(&r, tomoyo_check_path_number_acl); error = tomoyo_audit_path_number_log(&r); } while (error == TOMOYO_RETRY_REQUEST); kfree(buf.name); out: tomoyo_read_unlock(idx); if (r.mode != TOMOYO_CONFIG_ENFORCING) error = 0; return error; } /** * tomoyo_check_open_permission - Check permission for "read" and "write". * * @domain: Pointer to "struct tomoyo_domain_info". * @path: Pointer to "struct path". * @flag: Flags for open(). * * Returns 0 on success, negative value otherwise. */ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, struct path *path, const int flag) { const u8 acc_mode = ACC_MODE(flag); int error = 0; struct tomoyo_path_info buf; struct tomoyo_request_info r; struct tomoyo_obj_info obj = { .path1 = *path, }; int idx; buf.name = NULL; r.mode = TOMOYO_CONFIG_DISABLED; idx = tomoyo_read_lock(); if (acc_mode && tomoyo_init_request_info(&r, domain, TOMOYO_MAC_FILE_OPEN) != TOMOYO_CONFIG_DISABLED) { if (!tomoyo_get_realpath(&buf, path)) { error = -ENOMEM; goto out; } r.obj = &obj; if (acc_mode & MAY_READ) error = tomoyo_path_permission(&r, TOMOYO_TYPE_READ, &buf); if (!error && (acc_mode & MAY_WRITE)) error = tomoyo_path_permission(&r, (flag & O_APPEND) ? TOMOYO_TYPE_APPEND : TOMOYO_TYPE_WRITE, &buf); } out: kfree(buf.name); tomoyo_read_unlock(idx); if (r.mode != TOMOYO_CONFIG_ENFORCING) error = 0; return error; } /** * tomoyo_path_perm - Check permission for "unlink", "rmdir", "truncate", "symlink", "append", "chroot" and "unmount". * * @operation: Type of operation. * @path: Pointer to "struct path". * @target: Symlink's target if @operation is TOMOYO_TYPE_SYMLINK, * NULL otherwise. * * Returns 0 on success, negative value otherwise. */ int tomoyo_path_perm(const u8 operation, const struct path *path, const char *target) { struct tomoyo_request_info r; struct tomoyo_obj_info obj = { .path1 = *path, }; int error; struct tomoyo_path_info buf; bool is_enforce; struct tomoyo_path_info symlink_target; int idx; if (tomoyo_init_request_info(&r, NULL, tomoyo_p2mac[operation]) == TOMOYO_CONFIG_DISABLED) return 0; is_enforce = (r.mode == TOMOYO_CONFIG_ENFORCING); error = -ENOMEM; buf.name = NULL; idx = tomoyo_read_lock(); if (!tomoyo_get_realpath(&buf, path)) goto out; r.obj = &obj; switch (operation) { case TOMOYO_TYPE_RMDIR: case TOMOYO_TYPE_CHROOT: tomoyo_add_slash(&buf); break; case TOMOYO_TYPE_SYMLINK: symlink_target.name = tomoyo_encode(target); if (!symlink_target.name) goto out; tomoyo_fill_path_info(&symlink_target); obj.symlink_target = &symlink_target; break; } error = tomoyo_path_permission(&r, operation, &buf); if (operation == TOMOYO_TYPE_SYMLINK) kfree(symlink_target.name); out: kfree(buf.name); tomoyo_read_unlock(idx); if (!is_enforce) error = 0; return error; } /** * tomoyo_mkdev_perm - Check permission for "mkblock" and "mkchar". * * @operation: Type of operation. (TOMOYO_TYPE_MKCHAR or TOMOYO_TYPE_MKBLOCK) * @path: Pointer to "struct path". * @mode: Create mode. * @dev: Device number. * * Returns 0 on success, negative value otherwise. */ int tomoyo_mkdev_perm(const u8 operation, struct path *path, const unsigned int mode, unsigned int dev) { struct tomoyo_request_info r; struct tomoyo_obj_info obj = { .path1 = *path, }; int error = -ENOMEM; struct tomoyo_path_info buf; int idx; if (tomoyo_init_request_info(&r, NULL, tomoyo_pnnn2mac[operation]) == TOMOYO_CONFIG_DISABLED) return 0; idx = tomoyo_read_lock(); error = -ENOMEM; if (tomoyo_get_realpath(&buf, path)) { r.obj = &obj; dev = new_decode_dev(dev); r.param_type = TOMOYO_TYPE_MKDEV_ACL; r.param.mkdev.filename = &buf; r.param.mkdev.operation = operation; r.param.mkdev.mode = mode; r.param.mkdev.major = MAJOR(dev); r.param.mkdev.minor = MINOR(dev); tomoyo_check_acl(&r, tomoyo_check_mkdev_acl); error = tomoyo_audit_mkdev_log(&r); kfree(buf.name); } tomoyo_read_unlock(idx); if (r.mode != TOMOYO_CONFIG_ENFORCING) error = 0; return error; } /** * tomoyo_path2_perm - Check permission for "rename", "link" and "pivot_root". * * @operation: Type of operation. * @path1: Pointer to "struct path". * @path2: Pointer to "struct path". * * Returns 0 on success, negative value otherwise. */ int tomoyo_path2_perm(const u8 operation, struct path *path1, struct path *path2) { int error = -ENOMEM; struct tomoyo_path_info buf1; struct tomoyo_path_info buf2; struct tomoyo_request_info r; struct tomoyo_obj_info obj = { .path1 = *path1, .path2 = *path2, }; int idx; if (tomoyo_init_request_info(&r, NULL, tomoyo_pp2mac[operation]) == TOMOYO_CONFIG_DISABLED) return 0; buf1.name = NULL; buf2.name = NULL; idx = tomoyo_read_lock(); if (!tomoyo_get_realpath(&buf1, path1) || !tomoyo_get_realpath(&buf2, path2)) goto out; switch (operation) { case TOMOYO_TYPE_RENAME: case TOMOYO_TYPE_LINK: if (!d_is_dir(path1->dentry)) break; /* fall through */ case TOMOYO_TYPE_PIVOT_ROOT: tomoyo_add_slash(&buf1); tomoyo_add_slash(&buf2); break; } r.obj = &obj; r.param_type = TOMOYO_TYPE_PATH2_ACL; r.param.path2.operation = operation; r.param.path2.filename1 = &buf1; r.param.path2.filename2 = &buf2; do { tomoyo_check_acl(&r, tomoyo_check_path2_acl); error = tomoyo_audit_path2_log(&r); } while (error == TOMOYO_RETRY_REQUEST); out: kfree(buf1.name); kfree(buf2.name); tomoyo_read_unlock(idx); if (r.mode != TOMOYO_CONFIG_ENFORCING) error = 0; return error; }