int ovl_removexattr(struct dentry *dentry, const char *name) { int err; struct path realpath; enum ovl_path_type type; if (ovl_path_type(dentry->d_parent) == OVL_PATH_MERGE && ovl_is_private_xattr(name)) return -ENODATA; type = ovl_path_real(dentry, &realpath); if (type == OVL_PATH_LOWER) { err = vfs_getxattr(realpath.dentry, name, NULL, 0); if (err < 0) return err; err = ovl_copy_up(dentry); if (err) return err; ovl_path_upper(dentry, &realpath); } return vfs_removexattr(realpath.dentry, name); }
int ovl_removexattr(struct dentry *dentry, const char *name) { int err; struct path realpath; enum ovl_path_type type = ovl_path_real(dentry, &realpath); err = ovl_want_write(dentry); if (err) goto out; err = -ENODATA; if (ovl_is_private_xattr(name)) goto out_drop_write; if (!OVL_TYPE_UPPER(type)) { err = vfs_getxattr(realpath.dentry, name, NULL, 0); if (err < 0) goto out_drop_write; err = ovl_copy_up(dentry); if (err) goto out_drop_write; ovl_path_upper(dentry, &realpath); } err = vfs_removexattr(realpath.dentry, name); out_drop_write: ovl_drop_write(dentry); 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); }
struct inode *ovl_d_select_inode(struct dentry *dentry, unsigned file_flags) { int err; struct path realpath; enum ovl_path_type type; 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_last(dentry, NULL, true); else err = ovl_copy_up(dentry); ovl_drop_write(dentry); if (err) return ERR_PTR(err); ovl_path_upper(dentry, &realpath); } return d_backing_inode(realpath.dentry); }
static int ovl_dir_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) { int err; enum ovl_path_type type; struct path realpath; const struct cred *old_cred; type = ovl_path_real(dentry, &realpath); old_cred = ovl_override_creds(dentry->d_sb); err = vfs_getattr(&realpath, stat); revert_creds(old_cred); if (err) return err; stat->dev = dentry->d_sb->s_dev; stat->ino = dentry->d_inode->i_ino; /* * It's probably not worth it to count subdirs to get the * correct link count. nlink=1 seems to pacify 'find' and * other utilities. */ if (OVL_TYPE_MERGE(type)) stat->nlink = 1; return 0; }
ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size) { struct path realpath; enum ovl_path_type type = ovl_path_real(dentry, &realpath); ssize_t res; int off; res = vfs_listxattr(realpath.dentry, list, size); if (res <= 0 || size == 0) return res; if (!ovl_need_xattr_filter(dentry, type)) return res; /* filter out private xattrs */ for (off = 0; off < res;) { char *s = list + off; size_t slen = strlen(s) + 1; BUG_ON(off + slen > res); if (ovl_is_private_xattr(s)) { res -= slen; memmove(s, s + slen, res - off); } else { off += slen; } } return res; }
static int ovl_dentry_open(struct dentry *dentry, struct file *file, const struct cred *cred) { int err; struct path realpath; enum ovl_path_type type; bool want_write = false; type = ovl_path_real(dentry, &realpath); if (ovl_open_need_copy_up(file->f_flags, type, realpath.dentry)) { want_write = true; err = ovl_want_write(dentry); if (err) goto out; if (file->f_flags & O_TRUNC) err = ovl_copy_up_last(dentry, NULL, true); else err = ovl_copy_up(dentry); if (err) goto out_drop_write; ovl_path_upper(dentry, &realpath); } err = vfs_open(&realpath, file, cred); out_drop_write: if (want_write) ovl_drop_write(dentry); out: return err; }
static int ovl_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) { struct path realpath; ovl_path_real(dentry, &realpath); return vfs_getattr(&realpath, stat); }
ssize_t ovl_getxattr(struct dentry *dentry, const char *name, void *value, size_t size) { struct path realpath; enum ovl_path_type type = ovl_path_real(dentry, &realpath); if (ovl_need_xattr_filter(dentry, type) && ovl_is_private_xattr(name)) return -ENODATA; return vfs_getxattr(realpath.dentry, name, value, size); }
static int ovl_readlink(struct dentry *dentry, char __user *buf, int bufsiz) { struct path realpath; struct inode *realinode; ovl_path_real(dentry, &realpath); realinode = realpath.dentry->d_inode; if (!realinode->i_op->readlink) return -EINVAL; touch_atime(&realpath); return realinode->i_op->readlink(realpath.dentry, buf, bufsiz); }
/** * ovl_statfs * @sb: The overlayfs super block * @buf: The struct kstatfs to fill in with stats * * Get the filesystem statistics. As writes always target the upper layer * filesystem pass the statfs to the upper filesystem (if it exists) */ static int ovl_statfs(struct dentry *dentry, struct kstatfs *buf) { struct ovl_fs *ofs = dentry->d_sb->s_fs_info; struct dentry *root_dentry = dentry->d_sb->s_root; struct path path; int err; ovl_path_real(root_dentry, &path); err = vfs_statfs(&path, buf); if (!err) { buf->f_namelen = ofs->namelen; buf->f_type = OVERLAYFS_SUPER_MAGIC; } return err; }
int ovl_open_maybe_copy_up(struct dentry *dentry, unsigned int file_flags) { int err = 0; struct path realpath; enum ovl_path_type type; type = ovl_path_real(dentry, &realpath); if (ovl_open_need_copy_up(file_flags, type, realpath.dentry)) { err = ovl_want_write(dentry); if (!err) { err = ovl_copy_up_flags(dentry, file_flags); ovl_drop_write(dentry); } } return err; }
static struct file *ovl_open(struct dentry *dentry, struct file *file, const struct cred *cred) { int err; struct path realpath; enum ovl_path_type type; type = ovl_path_real(dentry, &realpath); if (ovl_open_need_copy_up(file->f_flags, type, realpath.dentry)) { if (file->f_flags & O_TRUNC) err = ovl_copy_up_truncate(dentry, 0); else err = ovl_copy_up(dentry); if (err) return ERR_PTR(err); ovl_path_upper(dentry, &realpath); } return vfs_open(&realpath, file, cred); }
int ovl_xattr_set(struct dentry *dentry, const char *name, const void *value, size_t size, int flags) { int err; struct path realpath; enum ovl_path_type type = ovl_path_real(dentry, &realpath); const struct cred *old_cred; err = ovl_want_write(dentry); if (err) goto out; if (!value && !OVL_TYPE_UPPER(type)) { err = vfs_getxattr(realpath.dentry, name, NULL, 0); if (err < 0) goto out_drop_write; } err = ovl_copy_up(dentry); if (err) goto out_drop_write; if (!OVL_TYPE_UPPER(type)) ovl_path_upper(dentry, &realpath); old_cred = ovl_override_creds(dentry->d_sb); if (value) err = vfs_setxattr(realpath.dentry, name, value, size, flags); else { WARN_ON(flags != XATTR_REPLACE); err = vfs_removexattr(realpath.dentry, name); } revert_creds(old_cred); out_drop_write: ovl_drop_write(dentry); out: return err; }
int ovl_getattr(const struct path *path, struct kstat *stat, u32 request_mask, unsigned int flags) { struct dentry *dentry = path->dentry; enum ovl_path_type type; struct path realpath; const struct cred *old_cred; bool is_dir = S_ISDIR(dentry->d_inode->i_mode); int err; type = ovl_path_real(dentry, &realpath); old_cred = ovl_override_creds(dentry->d_sb); err = vfs_getattr(&realpath, stat, request_mask, flags); if (err) goto out; /* * When all layers are on the same fs, all real inode number are * unique, so we use the overlay st_dev, which is friendly to du -x. * * We also use st_ino of the copy up origin, if we know it. * This guaranties constant st_dev/st_ino across copy up. * * If filesystem supports NFS export ops, this also guaranties * persistent st_ino across mount cycle. */ if (ovl_same_sb(dentry->d_sb)) { if (OVL_TYPE_ORIGIN(type)) { struct kstat lowerstat; u32 lowermask = STATX_INO | (!is_dir ? STATX_NLINK : 0); ovl_path_lower(dentry, &realpath); err = vfs_getattr(&realpath, &lowerstat, lowermask, flags); if (err) goto out; WARN_ON_ONCE(stat->dev != lowerstat.dev); /* * Lower hardlinks may be broken on copy up to different * upper files, so we cannot use the lower origin st_ino * for those different files, even for the same fs case. * With inodes index enabled, it is safe to use st_ino * of an indexed hardlinked origin. The index validates * that the upper hardlink is not broken. */ if (is_dir || lowerstat.nlink == 1 || ovl_test_flag(OVL_INDEX, d_inode(dentry))) stat->ino = lowerstat.ino; } stat->dev = dentry->d_sb->s_dev; } else if (is_dir) { /* * If not all layers are on the same fs the pair {real st_ino; * overlay st_dev} is not unique, so use the non persistent * overlay st_ino. * * Always use the overlay st_dev for directories, so 'find * -xdev' will scan the entire overlay mount and won't cross the * overlay mount boundaries. */ stat->dev = dentry->d_sb->s_dev; stat->ino = dentry->d_inode->i_ino; } /* * It's probably not worth it to count subdirs to get the * correct link count. nlink=1 seems to pacify 'find' and * other utilities. */ if (is_dir && OVL_TYPE_MERGE(type)) stat->nlink = 1; /* * Return the overlay inode nlinks for indexed upper inodes. * Overlay inode nlink counts the union of the upper hardlinks * and non-covered lower hardlinks. It does not include the upper * index hardlink. */ if (!is_dir && ovl_test_flag(OVL_INDEX, d_inode(dentry))) stat->nlink = dentry->d_inode->i_nlink; out: revert_creds(old_cred); return err; }