/* subset of cpup_attr() */ static noinline_for_stack int au_cpdown_attr(struct path *h_path, struct dentry *h_src) { int err, sbits; struct iattr ia; struct inode *h_isrc; h_isrc = h_src->d_inode; ia.ia_valid = ATTR_FORCE | ATTR_MODE | ATTR_UID | ATTR_GID; ia.ia_mode = h_isrc->i_mode; ia.ia_uid = h_isrc->i_uid; ia.ia_gid = h_isrc->i_gid; sbits = !!(ia.ia_mode & (S_ISUID | S_ISGID)); au_cpup_attr_flags(h_path->dentry->d_inode, h_isrc); err = vfsub_sio_notify_change(h_path, &ia); /* is this nfs only? */ if (!err && sbits && au_test_nfs(h_path->dentry->d_sb)) { ia.ia_valid = ATTR_FORCE | ATTR_MODE; ia.ia_mode = h_isrc->i_mode; err = vfsub_sio_notify_change(h_path, &ia); } return err; }
static noinline_for_stack int cpup_iattr(struct dentry *dst, aufs_bindex_t bindex, struct dentry *h_src) { int err, sbits; struct iattr ia; struct path h_path; struct inode *h_isrc; h_path.dentry = au_h_dptr(dst, bindex); h_path.mnt = au_sbr_mnt(dst->d_sb, bindex); h_isrc = h_src->d_inode; ia.ia_valid = ATTR_FORCE | ATTR_MODE | ATTR_UID | ATTR_GID | ATTR_ATIME | ATTR_MTIME | ATTR_ATIME_SET | ATTR_MTIME_SET; ia.ia_mode = h_isrc->i_mode; ia.ia_uid = h_isrc->i_uid; ia.ia_gid = h_isrc->i_gid; ia.ia_atime = h_isrc->i_atime; ia.ia_mtime = h_isrc->i_mtime; sbits = !!(ia.ia_mode & (S_ISUID | S_ISGID)); au_cpup_attr_flags(h_path.dentry->d_inode, h_isrc); err = vfsub_notify_change(&h_path, &ia); /* is this nfs only? */ if (!err && sbits && au_test_nfs(h_path.dentry->d_sb)) { ia.ia_valid = ATTR_FORCE | ATTR_MODE; ia.ia_mode = h_isrc->i_mode; err = vfsub_notify_change(&h_path, &ia); } return err; }
static void call_unlink(void *args) { struct unlink_args *a = args; struct inode *h_inode; const int stop_sillyrename = (au_test_nfs(a->dentry->d_sb) && atomic_read(&a->dentry->d_count) == 1); LKTRTrace("%.*s, stop_silly %d, cnt %d\n", AuDLNPair(a->dentry), stop_sillyrename, atomic_read(&a->dentry->d_count)); if (!stop_sillyrename) dget(a->dentry); h_inode = a->dentry->d_inode; if (h_inode) atomic_inc_return(&h_inode->i_count); vfsub_ignore(a->vargs); *a->errp = do_vfsub_unlink(a->dir, a->dentry); if (unlikely(*a->errp || (a->dentry->d_flags & DCACHE_NFSFS_RENAMED))) vfsub_unignore(a->vargs); au_dbg_hin_list(a->vargs); if (!stop_sillyrename) dput(a->dentry); if (h_inode) iput(h_inode); AuTraceErr(*a->errp); }
int au_test_h_perm_sio(struct inode *h_inode, int mask) { if (au_test_nfs(h_inode->i_sb) && (mask & MAY_WRITE) && S_ISDIR(h_inode->i_mode)) mask |= MAY_READ; /* force permission check */ return au_test_h_perm(h_inode, mask); }
static int h_permission(struct inode *h_inode, int mask, struct vfsmount *h_mnt, int brperm) { int err; const unsigned char write_mask = !!(mask & (MAY_WRITE | MAY_APPEND)); err = -EACCES; if ((write_mask && IS_IMMUTABLE(h_inode)) || ((mask & MAY_EXEC) && S_ISREG(h_inode->i_mode) && ((h_mnt->mnt_flags & MNT_NOEXEC) || !(h_inode->i_mode & S_IXUGO)))) goto out; /* * - skip the lower fs test in the case of write to ro branch. * - nfs dir permission write check is optimized, but a policy for * link/rename requires a real check. * - nfs always sets MS_POSIXACL regardless its mount option 'noacl.' * in this case, generic_permission() returns -EOPNOTSUPP. */ if ((write_mask && !au_br_writable(brperm)) || (au_test_nfs(h_inode->i_sb) && S_ISDIR(h_inode->i_mode) && write_mask && !(mask & MAY_READ)) || !h_inode->i_op->permission) { /* AuLabel(generic_permission); */ /* AuDbg("get_acl %pf\n", h_inode->i_op->get_acl); */ err = generic_permission(h_inode, mask); if (err == -EOPNOTSUPP && au_test_nfs_noacl(h_inode)) err = h_inode->i_op->permission(h_inode, mask); AuTraceErr(err); } else { /* AuLabel(h_inode->permission); */ err = h_inode->i_op->permission(h_inode, mask); AuTraceErr(err); } if (!err) err = devcgroup_inode_permission(h_inode, mask); if (!err) err = security_inode_permission(h_inode, mask); #if 0 if (!err) { /* todo: do we need to call ima_path_check()? */ struct path h_path = { .dentry = .mnt = h_mnt }; err = ima_path_check(&h_path, mask & (MAY_READ | MAY_WRITE | MAY_EXEC), IMA_COUNT_LEAVE); } #endif out: return err; }
static noinline_for_stack int cpup_iattr(struct dentry *dst, aufs_bindex_t bindex, struct dentry *h_src, struct au_cpup_reg_attr *h_src_attr) { int err, sbits; struct iattr ia; struct path h_path; struct inode *h_isrc, *h_idst; struct kstat *h_st; h_path.dentry = au_h_dptr(dst, bindex); h_idst = h_path.dentry->d_inode; h_path.mnt = au_sbr_mnt(dst->d_sb, bindex); h_isrc = h_src->d_inode; ia.ia_valid = ATTR_FORCE | ATTR_UID | ATTR_GID | ATTR_ATIME | ATTR_MTIME | ATTR_ATIME_SET | ATTR_MTIME_SET; if (h_src_attr && h_src_attr->valid) { h_st = &h_src_attr->st; ia.ia_uid = h_st->uid; ia.ia_gid = h_st->gid; ia.ia_atime = h_st->atime; ia.ia_mtime = h_st->mtime; if (h_idst->i_mode != h_st->mode && !S_ISLNK(h_idst->i_mode)) { ia.ia_valid |= ATTR_MODE; ia.ia_mode = h_st->mode; } sbits = !!(h_st->mode & (S_ISUID | S_ISGID)); au_cpup_attr_flags(h_idst, h_src_attr->iflags); } else { ia.ia_uid = h_isrc->i_uid; ia.ia_gid = h_isrc->i_gid; ia.ia_atime = h_isrc->i_atime; ia.ia_mtime = h_isrc->i_mtime; if (h_idst->i_mode != h_isrc->i_mode && !S_ISLNK(h_idst->i_mode)) { ia.ia_valid |= ATTR_MODE; ia.ia_mode = h_isrc->i_mode; } sbits = !!(h_isrc->i_mode & (S_ISUID | S_ISGID)); au_cpup_attr_flags(h_idst, h_isrc->i_flags); } err = vfsub_notify_change(&h_path, &ia); /* is this nfs only? */ if (!err && sbits && au_test_nfs(h_path.dentry->d_sb)) { ia.ia_valid = ATTR_FORCE | ATTR_MODE; ia.ia_mode = h_isrc->i_mode; err = vfsub_notify_change(&h_path, &ia); } return err; }
/* todo: should this mkdir be done in /sbin/mount.aufs helper? */ static int au_whdir(struct inode *h_dir, struct path *path) { int err; err = -EEXIST; if (!path->dentry->d_inode) { int mode = S_IRWXU; if (au_test_nfs(path->dentry->d_sb)) mode |= S_IXUGO; err = vfsub_mkdir(h_dir, path, mode); } else if (S_ISDIR(path->dentry->d_inode->i_mode)) err = 0; else pr_err("unknown %.*s exists\n", AuDLNPair(path->dentry)); return err; }
int vfsub_flush(struct file *file, fl_owner_t id) { int err; err = 0; if (file->f_op && file->f_op->flush) { if (!au_test_nfs(file->f_dentry->d_sb)) err = file->f_op->flush(file, id); else { lockdep_off(); err = file->f_op->flush(file, id); lockdep_on(); } if (!err) vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/ } return err; }
static noinline_for_stack int cpup_iattr(struct dentry *dst, aufs_bindex_t bindex, struct dentry *h_src, struct au_hinode *hdir, struct vfsub_args *vargs) { int err, sbits; struct dentry *h_dst; struct iattr ia; struct inode *h_isrc, *h_idst; h_dst = au_h_dptr(dst, bindex); LKTRTrace("%.*s\n", AuDLNPair(h_dst)); h_idst = h_dst->d_inode; /* todo? IMustLock(h_idst); */ h_isrc = h_src->d_inode; /* todo? IMustLock(h_isrc); */ ia.ia_valid = ATTR_FORCE | ATTR_MODE | ATTR_UID | ATTR_GID | ATTR_ATIME | ATTR_MTIME | ATTR_ATIME_SET | ATTR_MTIME_SET; ia.ia_mode = h_isrc->i_mode; ia.ia_uid = h_isrc->i_uid; ia.ia_gid = h_isrc->i_gid; ia.ia_atime = h_isrc->i_atime; ia.ia_mtime = h_isrc->i_mtime; sbits = !!(ia.ia_mode & (S_ISUID | S_ISGID)); au_cpup_attr_flags(h_idst, h_isrc); vfsub_args_reinit(vargs); vfsub_ign_hinode(vargs, IN_ATTRIB, hdir); err = vfsub_notify_change(h_dst, &ia, vargs); /* is this nfs only? */ if (!err && sbits && au_test_nfs(h_dst->d_sb)) { ia.ia_valid = ATTR_FORCE | ATTR_MODE; ia.ia_mode = h_isrc->i_mode; vfsub_args_reinit(vargs); vfsub_ign_hinode(vargs, IN_ATTRIB, hdir); err = vfsub_notify_change(h_dst, &ia, vargs); } AuTraceErr(err); return err; }
static int au_h_verify_dentry(struct dentry *h_dentry, struct dentry *h_parent, struct au_branch *br) { int err; struct au_iattr ia; struct inode *h_inode; struct dentry *h_d; struct super_block *h_sb; err = 0; memset(&ia, -1, sizeof(ia)); h_sb = h_dentry->d_sb; h_inode = NULL; if (d_is_positive(h_dentry)) { h_inode = d_inode(h_dentry); au_iattr_save(&ia, h_inode); } else if (au_test_nfs(h_sb) || au_test_fuse(h_sb)) /* nfs d_revalidate may return 0 for negative dentry */ /* fuse d_revalidate always return 0 for negative dentry */ goto out; /* main purpose is namei.c:cached_lookup() and d_revalidate */ h_d = vfsub_lkup_one(&h_dentry->d_name, h_parent); err = PTR_ERR(h_d); if (IS_ERR(h_d)) goto out; err = 0; if (unlikely(h_d != h_dentry || d_inode(h_d) != h_inode || (h_inode && au_iattr_test(&ia, h_inode)))) err = au_busy_or_stale(); dput(h_d); out: AuTraceErr(err); return err; }
static int au_do_copy_file(struct file *dst, struct file *src, loff_t len, char *buf, unsigned long blksize) { int err; size_t sz, rbytes, wbytes; unsigned char all_zero; char *p, *zp; struct mutex *h_mtx; /* reduce stack usage */ struct iattr *ia; zp = page_address(ZERO_PAGE(0)); if (unlikely(!zp)) return -ENOMEM; /* possible? */ err = 0; all_zero = 0; while (len) { AuDbg("len %lld\n", len); sz = blksize; if (len < blksize) sz = len; rbytes = 0; /* todo: signal_pending? */ while (!rbytes || err == -EAGAIN || err == -EINTR) { rbytes = vfsub_read_k(src, buf, sz, &src->f_pos); err = rbytes; } if (unlikely(err < 0)) break; all_zero = 0; if (len >= rbytes && rbytes == blksize) all_zero = !memcmp(buf, zp, rbytes); if (!all_zero) { wbytes = rbytes; p = buf; while (wbytes) { size_t b; b = vfsub_write_k(dst, p, wbytes, &dst->f_pos); err = b; /* todo: signal_pending? */ if (unlikely(err == -EAGAIN || err == -EINTR)) continue; if (unlikely(err < 0)) break; wbytes -= b; p += b; } } else { loff_t res; AuLabel(hole); res = vfsub_llseek(dst, rbytes, SEEK_CUR); err = res; if (unlikely(res < 0)) break; } len -= rbytes; err = 0; } /* the last block may be a hole */ if (!err && all_zero) { AuLabel(last hole); err = 1; if (au_test_nfs(dst->f_dentry->d_sb)) { /* nfs requires this step to make last hole */ /* is this only nfs? */ do { /* todo: signal_pending? */ err = vfsub_write_k(dst, "\0", 1, &dst->f_pos); } while (err == -EAGAIN || err == -EINTR); if (err == 1) dst->f_pos--; } if (err == 1) { ia = (void *)buf; ia->ia_size = dst->f_pos; ia->ia_valid = ATTR_SIZE | ATTR_FILE; ia->ia_file = dst; h_mtx = &dst->f_dentry->d_inode->i_mutex; mutex_lock_nested(h_mtx, AuLsc_I_CHILD2); err = vfsub_notify_change(&dst->f_path, ia); mutex_unlock(h_mtx); } } return err; }
/* todo: remove this */ static int h_d_revalidate(struct dentry *dentry, struct inode *inode, unsigned int flags, int do_udba) { int err; umode_t mode, h_mode; aufs_bindex_t bindex, btail, bstart, ibs, ibe; unsigned char plus, unhashed, is_root, h_plus, h_nfs, tmpfile; struct inode *h_inode, *h_cached_inode; struct dentry *h_dentry; struct qstr *name, *h_name; err = 0; plus = 0; mode = 0; ibs = -1; ibe = -1; unhashed = !!d_unhashed(dentry); is_root = !!IS_ROOT(dentry); name = &dentry->d_name; tmpfile = au_di(dentry)->di_tmpfile; /* * Theoretically, REVAL test should be unnecessary in case of * {FS,I}NOTIFY. * But {fs,i}notify doesn't fire some necessary events, * IN_ATTRIB for atime/nlink/pageio * Let's do REVAL test too. */ if (do_udba && inode) { mode = (inode->i_mode & S_IFMT); plus = (inode->i_nlink > 0); ibs = au_ibstart(inode); ibe = au_ibend(inode); } bstart = au_dbstart(dentry); btail = bstart; if (inode && S_ISDIR(inode->i_mode)) btail = au_dbtaildir(dentry); for (bindex = bstart; bindex <= btail; bindex++) { h_dentry = au_h_dptr(dentry, bindex); if (!h_dentry) continue; AuDbg("b%d, %pd\n", bindex, h_dentry); h_nfs = !!au_test_nfs(h_dentry->d_sb); spin_lock(&h_dentry->d_lock); h_name = &h_dentry->d_name; if (unlikely(do_udba && !is_root && ((!h_nfs && (unhashed != !!d_unhashed(h_dentry) || (!tmpfile && !au_qstreq(name, h_name)) )) || (h_nfs && !(flags & LOOKUP_OPEN) && (h_dentry->d_flags & DCACHE_NFSFS_RENAMED))) )) { int h_unhashed; h_unhashed = d_unhashed(h_dentry); spin_unlock(&h_dentry->d_lock); AuDbg("unhash 0x%x 0x%x, %pd %pd\n", unhashed, h_unhashed, dentry, h_dentry); goto err; } spin_unlock(&h_dentry->d_lock); err = au_do_h_d_reval(h_dentry, flags, dentry, bindex); if (unlikely(err)) /* do not goto err, to keep the errno */ break; /* todo: plink too? */ if (!do_udba) continue; /* UDBA tests */ if (unlikely(!!inode != d_is_positive(h_dentry))) goto err; h_inode = NULL; if (d_is_positive(h_dentry)) h_inode = d_inode(h_dentry); h_plus = plus; h_mode = mode; h_cached_inode = h_inode; if (h_inode) { h_mode = (h_inode->i_mode & S_IFMT); h_plus = (h_inode->i_nlink > 0); } if (inode && ibs <= bindex && bindex <= ibe) h_cached_inode = au_h_iptr(inode, bindex); if (!h_nfs) { if (unlikely(plus != h_plus && !tmpfile)) goto err; } else { if (unlikely(!(h_dentry->d_flags & DCACHE_NFSFS_RENAMED) && !is_root && !IS_ROOT(h_dentry) && unhashed != d_unhashed(h_dentry))) goto err; } if (unlikely(mode != h_mode || h_cached_inode != h_inode)) goto err; continue; err: err = -EINVAL; break; } AuTraceErr(err); return err; }
static noinline_for_stack int cpup_iattr(struct dentry *dst, aufs_bindex_t bindex, struct dentry *h_src, struct au_cpup_reg_attr *h_src_attr) { int err, sbits, icex; unsigned int mnt_flags; unsigned char verbose; struct iattr ia; struct path h_path; struct inode *h_isrc, *h_idst; struct kstat *h_st; struct au_branch *br; h_path.dentry = au_h_dptr(dst, bindex); h_idst = d_inode(h_path.dentry); br = au_sbr(dst->d_sb, bindex); h_path.mnt = au_br_mnt(br); h_isrc = d_inode(h_src); ia.ia_valid = ATTR_FORCE | ATTR_UID | ATTR_GID | ATTR_ATIME | ATTR_MTIME | ATTR_ATIME_SET | ATTR_MTIME_SET; if (h_src_attr && h_src_attr->valid) { h_st = &h_src_attr->st; ia.ia_uid = h_st->uid; ia.ia_gid = h_st->gid; ia.ia_atime = h_st->atime; ia.ia_mtime = h_st->mtime; if (h_idst->i_mode != h_st->mode && !S_ISLNK(h_idst->i_mode)) { ia.ia_valid |= ATTR_MODE; ia.ia_mode = h_st->mode; } sbits = !!(h_st->mode & (S_ISUID | S_ISGID)); au_cpup_attr_flags(h_idst, h_src_attr->iflags); } else { ia.ia_uid = h_isrc->i_uid; ia.ia_gid = h_isrc->i_gid; ia.ia_atime = h_isrc->i_atime; ia.ia_mtime = h_isrc->i_mtime; if (h_idst->i_mode != h_isrc->i_mode && !S_ISLNK(h_idst->i_mode)) { ia.ia_valid |= ATTR_MODE; ia.ia_mode = h_isrc->i_mode; } sbits = !!(h_isrc->i_mode & (S_ISUID | S_ISGID)); au_cpup_attr_flags(h_idst, h_isrc->i_flags); } /* no delegation since it is just created */ err = vfsub_notify_change(&h_path, &ia, /*delegated*/NULL); /* is this nfs only? */ if (!err && sbits && au_test_nfs(h_path.dentry->d_sb)) { ia.ia_valid = ATTR_FORCE | ATTR_MODE; ia.ia_mode = h_isrc->i_mode; err = vfsub_notify_change(&h_path, &ia, /*delegated*/NULL); } icex = br->br_perm & AuBrAttr_ICEX; if (!err) { mnt_flags = au_mntflags(dst->d_sb); verbose = !!au_opt_test(mnt_flags, VERBOSE); err = au_cpup_xattr(h_path.dentry, h_src, icex, verbose); } return err; }