mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-07 19:30:30 +09:00
Merge 815409a12c ("Merge tag 'ovl-update-5.15' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs") into android-mainline
Steps on the way to 5.15-rc1 Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> Change-Id: Ia61d00e6e4f49501323c3f4769793529c78d3713
This commit is contained in:
@@ -84,6 +84,9 @@ cache_strategy=%s Select a strategy for cached decompression from now on:
|
||||
It still does in-place I/O decompression
|
||||
for the rest compressed physical clusters.
|
||||
========== =============================================
|
||||
dax={always,never} Use direct access (no page cache). See
|
||||
Documentation/filesystems/dax.rst.
|
||||
dax A legacy option which is an alias for ``dax=always``.
|
||||
=================== =========================================================
|
||||
|
||||
On-disk details
|
||||
@@ -153,13 +156,14 @@ may not. All metadatas can be now observed in two different spaces (views):
|
||||
|
||||
Xattrs, extents, data inline are followed by the corresponding inode with
|
||||
proper alignment, and they could be optional for different data mappings.
|
||||
_currently_ total 4 valid data mappings are supported:
|
||||
_currently_ total 5 data layouts are supported:
|
||||
|
||||
== ====================================================================
|
||||
0 flat file data without data inline (no extent);
|
||||
1 fixed-sized output data compression (with non-compacted indexes);
|
||||
2 flat file data with tail packing data inline (no extent);
|
||||
3 fixed-sized output data compression (with compacted indexes, v5.3+).
|
||||
3 fixed-sized output data compression (with compacted indexes, v5.3+);
|
||||
4 chunk-based file (v5.15+).
|
||||
== ====================================================================
|
||||
|
||||
The size of the optional xattrs is indicated by i_xattr_count in inode
|
||||
@@ -210,6 +214,17 @@ Note that apart from the offset of the first filename, nameoff0 also indicates
|
||||
the total number of directory entries in this block since it is no need to
|
||||
introduce another on-disk field at all.
|
||||
|
||||
Chunk-based file
|
||||
----------------
|
||||
In order to support chunk-based data deduplication, a new inode data layout has
|
||||
been supported since Linux v5.15: Files are split in equal-sized data chunks
|
||||
with ``extents`` area of the inode metadata indicating how to get the chunk
|
||||
data: these can be simply as a 4-byte block address array or in the 8-byte
|
||||
chunk index form (see struct erofs_inode_chunk_index in erofs_fs.h for more
|
||||
details.)
|
||||
|
||||
By the way, chunk-based files are all uncompressed for now.
|
||||
|
||||
Data compression
|
||||
----------------
|
||||
EROFS implements LZ4 fixed-sized output compression which generates fixed-sized
|
||||
|
||||
@@ -70,7 +70,7 @@ prototypes::
|
||||
const char *(*get_link) (struct dentry *, struct inode *, struct delayed_call *);
|
||||
void (*truncate) (struct inode *);
|
||||
int (*permission) (struct inode *, int, unsigned int);
|
||||
int (*get_acl)(struct inode *, int);
|
||||
struct posix_acl * (*get_acl)(struct inode *, int, bool);
|
||||
int (*setattr) (struct dentry *, struct iattr *);
|
||||
int (*getattr) (const struct path *, struct kstat *, u32, unsigned int);
|
||||
ssize_t (*listxattr) (struct dentry *, char *, size_t);
|
||||
|
||||
@@ -427,6 +427,9 @@ b) If a file residing on a lower layer is opened for read-only and then
|
||||
memory mapped with MAP_SHARED, then subsequent changes to the file are not
|
||||
reflected in the memory mapping.
|
||||
|
||||
c) If a file residing on a lower layer is being executed, then opening that
|
||||
file for write or truncating the file will not be denied with ETXTBSY.
|
||||
|
||||
The following options allow overlayfs to act more like a standards
|
||||
compliant filesystem:
|
||||
|
||||
|
||||
@@ -432,7 +432,7 @@ As of kernel 2.6.22, the following members are defined:
|
||||
const char *(*get_link) (struct dentry *, struct inode *,
|
||||
struct delayed_call *);
|
||||
int (*permission) (struct user_namespace *, struct inode *, int);
|
||||
int (*get_acl)(struct inode *, int);
|
||||
struct posix_acl * (*get_acl)(struct inode *, int, bool);
|
||||
int (*setattr) (struct user_namespace *, struct dentry *, struct iattr *);
|
||||
int (*getattr) (struct user_namespace *, const struct path *, struct kstat *, u32, unsigned int);
|
||||
ssize_t (*listxattr) (struct dentry *, char *, size_t);
|
||||
|
||||
@@ -97,10 +97,13 @@ static struct posix_acl *v9fs_get_cached_acl(struct inode *inode, int type)
|
||||
return acl;
|
||||
}
|
||||
|
||||
struct posix_acl *v9fs_iop_get_acl(struct inode *inode, int type)
|
||||
struct posix_acl *v9fs_iop_get_acl(struct inode *inode, int type, bool rcu)
|
||||
{
|
||||
struct v9fs_session_info *v9ses;
|
||||
|
||||
if (rcu)
|
||||
return ERR_PTR(-ECHILD);
|
||||
|
||||
v9ses = v9fs_inode2v9ses(inode);
|
||||
if (((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT) ||
|
||||
((v9ses->flags & V9FS_ACL_MASK) != V9FS_POSIX_ACL)) {
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
#ifdef CONFIG_9P_FS_POSIX_ACL
|
||||
extern int v9fs_get_acl(struct inode *, struct p9_fid *);
|
||||
extern struct posix_acl *v9fs_iop_get_acl(struct inode *inode, int type);
|
||||
extern struct posix_acl *v9fs_iop_get_acl(struct inode *inode, int type, bool rcu);
|
||||
extern int v9fs_acl_chmod(struct inode *, struct p9_fid *);
|
||||
extern int v9fs_set_create_acl(struct inode *, struct p9_fid *,
|
||||
struct posix_acl *, struct posix_acl *);
|
||||
|
||||
@@ -121,7 +121,7 @@ static const char *bad_inode_get_link(struct dentry *dentry,
|
||||
return ERR_PTR(-EIO);
|
||||
}
|
||||
|
||||
static struct posix_acl *bad_inode_get_acl(struct inode *inode, int type)
|
||||
static struct posix_acl *bad_inode_get_acl(struct inode *inode, int type, bool rcu)
|
||||
{
|
||||
return ERR_PTR(-EIO);
|
||||
}
|
||||
|
||||
@@ -16,13 +16,16 @@
|
||||
#include "btrfs_inode.h"
|
||||
#include "xattr.h"
|
||||
|
||||
struct posix_acl *btrfs_get_acl(struct inode *inode, int type)
|
||||
struct posix_acl *btrfs_get_acl(struct inode *inode, int type, bool rcu)
|
||||
{
|
||||
int size;
|
||||
const char *name;
|
||||
char *value = NULL;
|
||||
struct posix_acl *acl;
|
||||
|
||||
if (rcu)
|
||||
return ERR_PTR(-ECHILD);
|
||||
|
||||
switch (type) {
|
||||
case ACL_TYPE_ACCESS:
|
||||
name = XATTR_NAME_POSIX_ACL_ACCESS;
|
||||
|
||||
@@ -3706,7 +3706,7 @@ static inline int __btrfs_fs_compat_ro(struct btrfs_fs_info *fs_info, u64 flag)
|
||||
|
||||
/* acl.c */
|
||||
#ifdef CONFIG_BTRFS_FS_POSIX_ACL
|
||||
struct posix_acl *btrfs_get_acl(struct inode *inode, int type);
|
||||
struct posix_acl *btrfs_get_acl(struct inode *inode, int type, bool rcu);
|
||||
int btrfs_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
|
||||
struct posix_acl *acl, int type);
|
||||
int btrfs_init_acl(struct btrfs_trans_handle *trans,
|
||||
|
||||
@@ -29,7 +29,7 @@ static inline void ceph_set_cached_acl(struct inode *inode,
|
||||
spin_unlock(&ci->i_ceph_lock);
|
||||
}
|
||||
|
||||
struct posix_acl *ceph_get_acl(struct inode *inode, int type)
|
||||
struct posix_acl *ceph_get_acl(struct inode *inode, int type, bool rcu)
|
||||
{
|
||||
int size;
|
||||
unsigned int retry_cnt = 0;
|
||||
@@ -37,6 +37,9 @@ struct posix_acl *ceph_get_acl(struct inode *inode, int type)
|
||||
char *value = NULL;
|
||||
struct posix_acl *acl;
|
||||
|
||||
if (rcu)
|
||||
return ERR_PTR(-ECHILD);
|
||||
|
||||
switch (type) {
|
||||
case ACL_TYPE_ACCESS:
|
||||
name = XATTR_NAME_POSIX_ACL_ACCESS;
|
||||
|
||||
@@ -1088,7 +1088,7 @@ void ceph_release_acl_sec_ctx(struct ceph_acl_sec_ctx *as_ctx);
|
||||
/* acl.c */
|
||||
#ifdef CONFIG_CEPH_FS_POSIX_ACL
|
||||
|
||||
struct posix_acl *ceph_get_acl(struct inode *, int);
|
||||
struct posix_acl *ceph_get_acl(struct inode *, int, bool);
|
||||
int ceph_set_acl(struct user_namespace *mnt_userns,
|
||||
struct inode *inode, struct posix_acl *acl, int type);
|
||||
int ceph_pre_init_acls(struct inode *dir, umode_t *mode,
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
config EROFS_FS
|
||||
tristate "EROFS filesystem support"
|
||||
depends on BLOCK
|
||||
select FS_IOMAP
|
||||
select LIBCRC32C
|
||||
help
|
||||
EROFS (Enhanced Read-Only File System) is a lightweight
|
||||
|
||||
431
fs/erofs/data.c
431
fs/erofs/data.c
@@ -2,35 +2,13 @@
|
||||
/*
|
||||
* Copyright (C) 2017-2018 HUAWEI, Inc.
|
||||
* https://www.huawei.com/
|
||||
* Copyright (C) 2021, Alibaba Cloud
|
||||
*/
|
||||
#include "internal.h"
|
||||
#include <linux/prefetch.h>
|
||||
|
||||
#include <linux/dax.h>
|
||||
#include <trace/events/erofs.h>
|
||||
|
||||
static void erofs_readendio(struct bio *bio)
|
||||
{
|
||||
struct bio_vec *bvec;
|
||||
blk_status_t err = bio->bi_status;
|
||||
struct bvec_iter_all iter_all;
|
||||
|
||||
bio_for_each_segment_all(bvec, bio, iter_all) {
|
||||
struct page *page = bvec->bv_page;
|
||||
|
||||
/* page is already locked */
|
||||
DBG_BUGON(PageUptodate(page));
|
||||
|
||||
if (err)
|
||||
SetPageError(page);
|
||||
else
|
||||
SetPageUptodate(page);
|
||||
|
||||
unlock_page(page);
|
||||
/* page could be reclaimed now */
|
||||
}
|
||||
bio_put(bio);
|
||||
}
|
||||
|
||||
struct page *erofs_get_meta_page(struct super_block *sb, erofs_blk_t blkaddr)
|
||||
{
|
||||
struct address_space *const mapping = sb->s_bdev->bd_inode->i_mapping;
|
||||
@@ -59,13 +37,6 @@ static int erofs_map_blocks_flatmode(struct inode *inode,
|
||||
nblocks = DIV_ROUND_UP(inode->i_size, PAGE_SIZE);
|
||||
lastblk = nblocks - tailendpacking;
|
||||
|
||||
if (offset >= inode->i_size) {
|
||||
/* leave out-of-bound access unmapped */
|
||||
map->m_flags = 0;
|
||||
map->m_plen = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* there is no hole in flatmode */
|
||||
map->m_flags = EROFS_MAP_MAPPED;
|
||||
|
||||
@@ -100,217 +71,273 @@ static int erofs_map_blocks_flatmode(struct inode *inode,
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
out:
|
||||
map->m_llen = map->m_plen;
|
||||
|
||||
err_out:
|
||||
trace_erofs_map_blocks_flatmode_exit(inode, map, flags, 0);
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline struct bio *erofs_read_raw_page(struct bio *bio,
|
||||
struct address_space *mapping,
|
||||
struct page *page,
|
||||
erofs_off_t *last_block,
|
||||
unsigned int nblocks,
|
||||
unsigned int *eblks,
|
||||
bool ra)
|
||||
static int erofs_map_blocks(struct inode *inode,
|
||||
struct erofs_map_blocks *map, int flags)
|
||||
{
|
||||
struct inode *const inode = mapping->host;
|
||||
struct super_block *const sb = inode->i_sb;
|
||||
erofs_off_t current_block = (erofs_off_t)page->index;
|
||||
int err;
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct erofs_inode *vi = EROFS_I(inode);
|
||||
struct erofs_inode_chunk_index *idx;
|
||||
struct page *page;
|
||||
u64 chunknr;
|
||||
unsigned int unit;
|
||||
erofs_off_t pos;
|
||||
int err = 0;
|
||||
|
||||
DBG_BUGON(!nblocks);
|
||||
|
||||
if (PageUptodate(page)) {
|
||||
err = 0;
|
||||
goto has_updated;
|
||||
if (map->m_la >= inode->i_size) {
|
||||
/* leave out-of-bound access unmapped */
|
||||
map->m_flags = 0;
|
||||
map->m_plen = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* note that for readpage case, bio also equals to NULL */
|
||||
if (bio &&
|
||||
(*last_block + 1 != current_block || !*eblks)) {
|
||||
submit_bio_retry:
|
||||
submit_bio(bio);
|
||||
bio = NULL;
|
||||
}
|
||||
if (vi->datalayout != EROFS_INODE_CHUNK_BASED)
|
||||
return erofs_map_blocks_flatmode(inode, map, flags);
|
||||
|
||||
if (!bio) {
|
||||
struct erofs_map_blocks map = {
|
||||
.m_la = blknr_to_addr(current_block),
|
||||
};
|
||||
erofs_blk_t blknr;
|
||||
unsigned int blkoff;
|
||||
if (vi->chunkformat & EROFS_CHUNK_FORMAT_INDEXES)
|
||||
unit = sizeof(*idx); /* chunk index */
|
||||
else
|
||||
unit = EROFS_BLOCK_MAP_ENTRY_SIZE; /* block map */
|
||||
|
||||
err = erofs_map_blocks_flatmode(inode, &map, EROFS_GET_BLOCKS_RAW);
|
||||
if (err)
|
||||
goto err_out;
|
||||
chunknr = map->m_la >> vi->chunkbits;
|
||||
pos = ALIGN(iloc(EROFS_SB(sb), vi->nid) + vi->inode_isize +
|
||||
vi->xattr_isize, unit) + unit * chunknr;
|
||||
|
||||
/* zero out the holed page */
|
||||
if (!(map.m_flags & EROFS_MAP_MAPPED)) {
|
||||
zero_user_segment(page, 0, PAGE_SIZE);
|
||||
SetPageUptodate(page);
|
||||
page = erofs_get_meta_page(inode->i_sb, erofs_blknr(pos));
|
||||
if (IS_ERR(page))
|
||||
return PTR_ERR(page);
|
||||
|
||||
/* imply err = 0, see erofs_map_blocks */
|
||||
goto has_updated;
|
||||
map->m_la = chunknr << vi->chunkbits;
|
||||
map->m_plen = min_t(erofs_off_t, 1UL << vi->chunkbits,
|
||||
roundup(inode->i_size - map->m_la, EROFS_BLKSIZ));
|
||||
|
||||
/* handle block map */
|
||||
if (!(vi->chunkformat & EROFS_CHUNK_FORMAT_INDEXES)) {
|
||||
__le32 *blkaddr = page_address(page) + erofs_blkoff(pos);
|
||||
|
||||
if (le32_to_cpu(*blkaddr) == EROFS_NULL_ADDR) {
|
||||
map->m_flags = 0;
|
||||
} else {
|
||||
map->m_pa = blknr_to_addr(le32_to_cpu(*blkaddr));
|
||||
map->m_flags = EROFS_MAP_MAPPED;
|
||||
}
|
||||
|
||||
/* for RAW access mode, m_plen must be equal to m_llen */
|
||||
DBG_BUGON(map.m_plen != map.m_llen);
|
||||
|
||||
blknr = erofs_blknr(map.m_pa);
|
||||
blkoff = erofs_blkoff(map.m_pa);
|
||||
|
||||
/* deal with inline page */
|
||||
if (map.m_flags & EROFS_MAP_META) {
|
||||
void *vsrc, *vto;
|
||||
struct page *ipage;
|
||||
|
||||
DBG_BUGON(map.m_plen > PAGE_SIZE);
|
||||
|
||||
ipage = erofs_get_meta_page(inode->i_sb, blknr);
|
||||
|
||||
if (IS_ERR(ipage)) {
|
||||
err = PTR_ERR(ipage);
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
vsrc = kmap_atomic(ipage);
|
||||
vto = kmap_atomic(page);
|
||||
memcpy(vto, vsrc + blkoff, map.m_plen);
|
||||
memset(vto + map.m_plen, 0, PAGE_SIZE - map.m_plen);
|
||||
kunmap_atomic(vto);
|
||||
kunmap_atomic(vsrc);
|
||||
flush_dcache_page(page);
|
||||
|
||||
SetPageUptodate(page);
|
||||
/* TODO: could we unlock the page earlier? */
|
||||
unlock_page(ipage);
|
||||
put_page(ipage);
|
||||
|
||||
/* imply err = 0, see erofs_map_blocks */
|
||||
goto has_updated;
|
||||
goto out_unlock;
|
||||
}
|
||||
/* parse chunk indexes */
|
||||
idx = page_address(page) + erofs_blkoff(pos);
|
||||
switch (le32_to_cpu(idx->blkaddr)) {
|
||||
case EROFS_NULL_ADDR:
|
||||
map->m_flags = 0;
|
||||
break;
|
||||
default:
|
||||
/* only one device is supported for now */
|
||||
if (idx->device_id) {
|
||||
erofs_err(sb, "invalid device id %u @ %llu for nid %llu",
|
||||
le16_to_cpu(idx->device_id),
|
||||
chunknr, vi->nid);
|
||||
err = -EFSCORRUPTED;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
/* pa must be block-aligned for raw reading */
|
||||
DBG_BUGON(erofs_blkoff(map.m_pa));
|
||||
|
||||
/* max # of continuous pages */
|
||||
if (nblocks > DIV_ROUND_UP(map.m_plen, PAGE_SIZE))
|
||||
nblocks = DIV_ROUND_UP(map.m_plen, PAGE_SIZE);
|
||||
|
||||
*eblks = bio_max_segs(nblocks);
|
||||
bio = bio_alloc(GFP_NOIO, *eblks);
|
||||
|
||||
bio->bi_end_io = erofs_readendio;
|
||||
bio_set_dev(bio, sb->s_bdev);
|
||||
bio->bi_iter.bi_sector = (sector_t)blknr <<
|
||||
LOG_SECTORS_PER_BLOCK;
|
||||
bio->bi_opf = REQ_OP_READ | (ra ? REQ_RAHEAD : 0);
|
||||
map->m_pa = blknr_to_addr(le32_to_cpu(idx->blkaddr));
|
||||
map->m_flags = EROFS_MAP_MAPPED;
|
||||
break;
|
||||
}
|
||||
|
||||
err = bio_add_page(bio, page, PAGE_SIZE, 0);
|
||||
/* out of the extent or bio is full */
|
||||
if (err < PAGE_SIZE)
|
||||
goto submit_bio_retry;
|
||||
--*eblks;
|
||||
*last_block = current_block;
|
||||
return bio;
|
||||
|
||||
err_out:
|
||||
/* for sync reading, set page error immediately */
|
||||
if (!ra) {
|
||||
SetPageError(page);
|
||||
ClearPageUptodate(page);
|
||||
}
|
||||
has_updated:
|
||||
out_unlock:
|
||||
unlock_page(page);
|
||||
put_page(page);
|
||||
out:
|
||||
map->m_llen = map->m_plen;
|
||||
return err;
|
||||
}
|
||||
|
||||
/* if updated manually, continuous pages has a gap */
|
||||
if (bio)
|
||||
submit_bio(bio);
|
||||
return err ? ERR_PTR(err) : NULL;
|
||||
static int erofs_iomap_begin(struct inode *inode, loff_t offset, loff_t length,
|
||||
unsigned int flags, struct iomap *iomap, struct iomap *srcmap)
|
||||
{
|
||||
int ret;
|
||||
struct erofs_map_blocks map;
|
||||
|
||||
map.m_la = offset;
|
||||
map.m_llen = length;
|
||||
|
||||
ret = erofs_map_blocks(inode, &map, EROFS_GET_BLOCKS_RAW);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
iomap->bdev = inode->i_sb->s_bdev;
|
||||
iomap->dax_dev = EROFS_I_SB(inode)->dax_dev;
|
||||
iomap->offset = map.m_la;
|
||||
iomap->length = map.m_llen;
|
||||
iomap->flags = 0;
|
||||
iomap->private = NULL;
|
||||
|
||||
if (!(map.m_flags & EROFS_MAP_MAPPED)) {
|
||||
iomap->type = IOMAP_HOLE;
|
||||
iomap->addr = IOMAP_NULL_ADDR;
|
||||
if (!iomap->length)
|
||||
iomap->length = length;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (map.m_flags & EROFS_MAP_META) {
|
||||
struct page *ipage;
|
||||
|
||||
iomap->type = IOMAP_INLINE;
|
||||
ipage = erofs_get_meta_page(inode->i_sb,
|
||||
erofs_blknr(map.m_pa));
|
||||
if (IS_ERR(ipage))
|
||||
return PTR_ERR(ipage);
|
||||
iomap->inline_data = page_address(ipage) +
|
||||
erofs_blkoff(map.m_pa);
|
||||
iomap->private = ipage;
|
||||
} else {
|
||||
iomap->type = IOMAP_MAPPED;
|
||||
iomap->addr = map.m_pa;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int erofs_iomap_end(struct inode *inode, loff_t pos, loff_t length,
|
||||
ssize_t written, unsigned int flags, struct iomap *iomap)
|
||||
{
|
||||
struct page *ipage = iomap->private;
|
||||
|
||||
if (ipage) {
|
||||
DBG_BUGON(iomap->type != IOMAP_INLINE);
|
||||
unlock_page(ipage);
|
||||
put_page(ipage);
|
||||
} else {
|
||||
DBG_BUGON(iomap->type == IOMAP_INLINE);
|
||||
}
|
||||
return written;
|
||||
}
|
||||
|
||||
static const struct iomap_ops erofs_iomap_ops = {
|
||||
.iomap_begin = erofs_iomap_begin,
|
||||
.iomap_end = erofs_iomap_end,
|
||||
};
|
||||
|
||||
int erofs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
|
||||
u64 start, u64 len)
|
||||
{
|
||||
if (erofs_inode_is_data_compressed(EROFS_I(inode)->datalayout)) {
|
||||
#ifdef CONFIG_EROFS_FS_ZIP
|
||||
return iomap_fiemap(inode, fieinfo, start, len,
|
||||
&z_erofs_iomap_report_ops);
|
||||
#else
|
||||
return -EOPNOTSUPP;
|
||||
#endif
|
||||
}
|
||||
return iomap_fiemap(inode, fieinfo, start, len, &erofs_iomap_ops);
|
||||
}
|
||||
|
||||
/*
|
||||
* since we dont have write or truncate flows, so no inode
|
||||
* locking needs to be held at the moment.
|
||||
*/
|
||||
static int erofs_raw_access_readpage(struct file *file, struct page *page)
|
||||
static int erofs_readpage(struct file *file, struct page *page)
|
||||
{
|
||||
erofs_off_t last_block;
|
||||
unsigned int eblks;
|
||||
struct bio *bio;
|
||||
|
||||
trace_erofs_readpage(page, true);
|
||||
|
||||
bio = erofs_read_raw_page(NULL, page->mapping,
|
||||
page, &last_block, 1, &eblks, false);
|
||||
|
||||
if (IS_ERR(bio))
|
||||
return PTR_ERR(bio);
|
||||
|
||||
if (bio)
|
||||
submit_bio(bio);
|
||||
return 0;
|
||||
return iomap_readpage(page, &erofs_iomap_ops);
|
||||
}
|
||||
|
||||
static void erofs_raw_access_readahead(struct readahead_control *rac)
|
||||
static void erofs_readahead(struct readahead_control *rac)
|
||||
{
|
||||
erofs_off_t last_block;
|
||||
unsigned int eblks;
|
||||
struct bio *bio = NULL;
|
||||
struct page *page;
|
||||
|
||||
trace_erofs_readpages(rac->mapping->host, readahead_index(rac),
|
||||
readahead_count(rac), true);
|
||||
|
||||
while ((page = readahead_page(rac))) {
|
||||
prefetchw(&page->flags);
|
||||
|
||||
bio = erofs_read_raw_page(bio, rac->mapping, page, &last_block,
|
||||
readahead_count(rac), &eblks, true);
|
||||
|
||||
/* all the page errors are ignored when readahead */
|
||||
if (IS_ERR(bio)) {
|
||||
pr_err("%s, readahead error at page %lu of nid %llu\n",
|
||||
__func__, page->index,
|
||||
EROFS_I(rac->mapping->host)->nid);
|
||||
|
||||
bio = NULL;
|
||||
}
|
||||
|
||||
put_page(page);
|
||||
}
|
||||
|
||||
if (bio)
|
||||
submit_bio(bio);
|
||||
return iomap_readahead(rac, &erofs_iomap_ops);
|
||||
}
|
||||
|
||||
static sector_t erofs_bmap(struct address_space *mapping, sector_t block)
|
||||
{
|
||||
struct inode *inode = mapping->host;
|
||||
struct erofs_map_blocks map = {
|
||||
.m_la = blknr_to_addr(block),
|
||||
};
|
||||
return iomap_bmap(mapping, block, &erofs_iomap_ops);
|
||||
}
|
||||
|
||||
if (EROFS_I(inode)->datalayout == EROFS_INODE_FLAT_INLINE) {
|
||||
erofs_blk_t blks = i_size_read(inode) >> LOG_BLOCK_SIZE;
|
||||
static int erofs_prepare_dio(struct kiocb *iocb, struct iov_iter *to)
|
||||
{
|
||||
struct inode *inode = file_inode(iocb->ki_filp);
|
||||
loff_t align = iocb->ki_pos | iov_iter_count(to) |
|
||||
iov_iter_alignment(to);
|
||||
struct block_device *bdev = inode->i_sb->s_bdev;
|
||||
unsigned int blksize_mask;
|
||||
|
||||
if (block >> LOG_SECTORS_PER_BLOCK >= blks)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!erofs_map_blocks_flatmode(inode, &map, EROFS_GET_BLOCKS_RAW))
|
||||
return erofs_blknr(map.m_pa);
|
||||
if (bdev)
|
||||
blksize_mask = (1 << ilog2(bdev_logical_block_size(bdev))) - 1;
|
||||
else
|
||||
blksize_mask = (1 << inode->i_blkbits) - 1;
|
||||
|
||||
if (align & blksize_mask)
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t erofs_file_read_iter(struct kiocb *iocb, struct iov_iter *to)
|
||||
{
|
||||
/* no need taking (shared) inode lock since it's a ro filesystem */
|
||||
if (!iov_iter_count(to))
|
||||
return 0;
|
||||
|
||||
#ifdef CONFIG_FS_DAX
|
||||
if (IS_DAX(iocb->ki_filp->f_mapping->host))
|
||||
return dax_iomap_rw(iocb, to, &erofs_iomap_ops);
|
||||
#endif
|
||||
if (iocb->ki_flags & IOCB_DIRECT) {
|
||||
int err = erofs_prepare_dio(iocb, to);
|
||||
|
||||
if (!err)
|
||||
return iomap_dio_rw(iocb, to, &erofs_iomap_ops,
|
||||
NULL, 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
return filemap_read(iocb, to, 0);
|
||||
}
|
||||
|
||||
/* for uncompressed (aligned) files and raw access for other files */
|
||||
const struct address_space_operations erofs_raw_access_aops = {
|
||||
.readpage = erofs_raw_access_readpage,
|
||||
.readahead = erofs_raw_access_readahead,
|
||||
.readpage = erofs_readpage,
|
||||
.readahead = erofs_readahead,
|
||||
.bmap = erofs_bmap,
|
||||
.direct_IO = noop_direct_IO,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_FS_DAX
|
||||
static vm_fault_t erofs_dax_huge_fault(struct vm_fault *vmf,
|
||||
enum page_entry_size pe_size)
|
||||
{
|
||||
return dax_iomap_fault(vmf, pe_size, NULL, NULL, &erofs_iomap_ops);
|
||||
}
|
||||
|
||||
static vm_fault_t erofs_dax_fault(struct vm_fault *vmf)
|
||||
{
|
||||
return erofs_dax_huge_fault(vmf, PE_SIZE_PTE);
|
||||
}
|
||||
|
||||
static const struct vm_operations_struct erofs_dax_vm_ops = {
|
||||
.fault = erofs_dax_fault,
|
||||
.huge_fault = erofs_dax_huge_fault,
|
||||
};
|
||||
|
||||
static int erofs_file_mmap(struct file *file, struct vm_area_struct *vma)
|
||||
{
|
||||
if (!IS_DAX(file_inode(file)))
|
||||
return generic_file_readonly_mmap(file, vma);
|
||||
|
||||
if ((vma->vm_flags & VM_SHARED) && (vma->vm_flags & VM_MAYWRITE))
|
||||
return -EINVAL;
|
||||
|
||||
vma->vm_ops = &erofs_dax_vm_ops;
|
||||
vma->vm_flags |= VM_HUGEPAGE;
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define erofs_file_mmap generic_file_readonly_mmap
|
||||
#endif
|
||||
|
||||
const struct file_operations erofs_file_fops = {
|
||||
.llseek = generic_file_llseek,
|
||||
.read_iter = erofs_file_read_iter,
|
||||
.mmap = erofs_file_mmap,
|
||||
.splice_read = generic_file_splice_read,
|
||||
};
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
*
|
||||
* Copyright (C) 2017-2018 HUAWEI, Inc.
|
||||
* https://www.huawei.com/
|
||||
* Copyright (C) 2021, Alibaba Cloud
|
||||
*/
|
||||
#ifndef __EROFS_FS_H
|
||||
#define __EROFS_FS_H
|
||||
@@ -19,10 +20,12 @@
|
||||
#define EROFS_FEATURE_INCOMPAT_LZ4_0PADDING 0x00000001
|
||||
#define EROFS_FEATURE_INCOMPAT_COMPR_CFGS 0x00000002
|
||||
#define EROFS_FEATURE_INCOMPAT_BIG_PCLUSTER 0x00000002
|
||||
#define EROFS_FEATURE_INCOMPAT_CHUNKED_FILE 0x00000004
|
||||
#define EROFS_ALL_FEATURE_INCOMPAT \
|
||||
(EROFS_FEATURE_INCOMPAT_LZ4_0PADDING | \
|
||||
EROFS_FEATURE_INCOMPAT_COMPR_CFGS | \
|
||||
EROFS_FEATURE_INCOMPAT_BIG_PCLUSTER)
|
||||
EROFS_FEATURE_INCOMPAT_BIG_PCLUSTER | \
|
||||
EROFS_FEATURE_INCOMPAT_CHUNKED_FILE)
|
||||
|
||||
#define EROFS_SB_EXTSLOT_SIZE 16
|
||||
|
||||
@@ -64,13 +67,16 @@ struct erofs_super_block {
|
||||
* inode, [xattrs], last_inline_data, ... | ... | no-holed data
|
||||
* 3 - inode compression D:
|
||||
* inode, [xattrs], map_header, extents ... | ...
|
||||
* 4~7 - reserved
|
||||
* 4 - inode chunk-based E:
|
||||
* inode, [xattrs], chunk indexes ... | ...
|
||||
* 5~7 - reserved
|
||||
*/
|
||||
enum {
|
||||
EROFS_INODE_FLAT_PLAIN = 0,
|
||||
EROFS_INODE_FLAT_COMPRESSION_LEGACY = 1,
|
||||
EROFS_INODE_FLAT_INLINE = 2,
|
||||
EROFS_INODE_FLAT_COMPRESSION = 3,
|
||||
EROFS_INODE_CHUNK_BASED = 4,
|
||||
EROFS_INODE_DATALAYOUT_MAX
|
||||
};
|
||||
|
||||
@@ -90,6 +96,19 @@ static inline bool erofs_inode_is_data_compressed(unsigned int datamode)
|
||||
#define EROFS_I_ALL \
|
||||
((1 << (EROFS_I_DATALAYOUT_BIT + EROFS_I_DATALAYOUT_BITS)) - 1)
|
||||
|
||||
/* indicate chunk blkbits, thus 'chunksize = blocksize << chunk blkbits' */
|
||||
#define EROFS_CHUNK_FORMAT_BLKBITS_MASK 0x001F
|
||||
/* with chunk indexes or just a 4-byte blkaddr array */
|
||||
#define EROFS_CHUNK_FORMAT_INDEXES 0x0020
|
||||
|
||||
#define EROFS_CHUNK_FORMAT_ALL \
|
||||
(EROFS_CHUNK_FORMAT_BLKBITS_MASK | EROFS_CHUNK_FORMAT_INDEXES)
|
||||
|
||||
struct erofs_inode_chunk_info {
|
||||
__le16 format; /* chunk blkbits, etc. */
|
||||
__le16 reserved;
|
||||
};
|
||||
|
||||
/* 32-byte reduced form of an ondisk inode */
|
||||
struct erofs_inode_compact {
|
||||
__le16 i_format; /* inode format hints */
|
||||
@@ -107,6 +126,9 @@ struct erofs_inode_compact {
|
||||
|
||||
/* for device files, used to indicate old/new device # */
|
||||
__le32 rdev;
|
||||
|
||||
/* for chunk-based files, it contains the summary info */
|
||||
struct erofs_inode_chunk_info c;
|
||||
} i_u;
|
||||
__le32 i_ino; /* only used for 32-bit stat compatibility */
|
||||
__le16 i_uid;
|
||||
@@ -135,6 +157,9 @@ struct erofs_inode_extended {
|
||||
|
||||
/* for device files, used to indicate old/new device # */
|
||||
__le32 rdev;
|
||||
|
||||
/* for chunk-based files, it contains the summary info */
|
||||
struct erofs_inode_chunk_info c;
|
||||
} i_u;
|
||||
|
||||
/* only used for 32-bit stat compatibility */
|
||||
@@ -204,6 +229,19 @@ static inline unsigned int erofs_xattr_entry_size(struct erofs_xattr_entry *e)
|
||||
e->e_name_len + le16_to_cpu(e->e_value_size));
|
||||
}
|
||||
|
||||
/* represent a zeroed chunk (hole) */
|
||||
#define EROFS_NULL_ADDR -1
|
||||
|
||||
/* 4-byte block address array */
|
||||
#define EROFS_BLOCK_MAP_ENTRY_SIZE sizeof(__le32)
|
||||
|
||||
/* 8-byte inode chunk indexes */
|
||||
struct erofs_inode_chunk_index {
|
||||
__le16 advise; /* always 0, don't care for now */
|
||||
__le16 device_id; /* back-end storage id, always 0 for now */
|
||||
__le32 blkaddr; /* start block address of this inode chunk */
|
||||
};
|
||||
|
||||
/* maximum supported size of a physical compression cluster */
|
||||
#define Z_EROFS_PCLUSTER_MAX_SIZE (1024 * 1024)
|
||||
|
||||
@@ -338,9 +376,14 @@ static inline void erofs_check_ondisk_layout_definitions(void)
|
||||
BUILD_BUG_ON(sizeof(struct erofs_inode_extended) != 64);
|
||||
BUILD_BUG_ON(sizeof(struct erofs_xattr_ibody_header) != 12);
|
||||
BUILD_BUG_ON(sizeof(struct erofs_xattr_entry) != 4);
|
||||
BUILD_BUG_ON(sizeof(struct erofs_inode_chunk_info) != 4);
|
||||
BUILD_BUG_ON(sizeof(struct erofs_inode_chunk_index) != 8);
|
||||
BUILD_BUG_ON(sizeof(struct z_erofs_map_header) != 8);
|
||||
BUILD_BUG_ON(sizeof(struct z_erofs_vle_decompressed_index) != 8);
|
||||
BUILD_BUG_ON(sizeof(struct erofs_dirent) != 12);
|
||||
/* keep in sync between 2 index structures for better extendibility */
|
||||
BUILD_BUG_ON(sizeof(struct erofs_inode_chunk_index) !=
|
||||
sizeof(struct z_erofs_vle_decompressed_index));
|
||||
|
||||
BUILD_BUG_ON(BIT(Z_EROFS_VLE_DI_CLUSTER_TYPE_BITS) <
|
||||
Z_EROFS_VLE_CLUSTER_TYPE_MAX - 1);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
/*
|
||||
* Copyright (C) 2017-2018 HUAWEI, Inc.
|
||||
* https://www.huawei.com/
|
||||
* Copyright (C) 2021, Alibaba Cloud
|
||||
*/
|
||||
#include "xattr.h"
|
||||
|
||||
@@ -122,8 +123,11 @@ static struct page *erofs_read_inode(struct inode *inode,
|
||||
/* total blocks for compressed files */
|
||||
if (erofs_inode_is_data_compressed(vi->datalayout))
|
||||
nblks = le32_to_cpu(die->i_u.compressed_blocks);
|
||||
|
||||
else if (vi->datalayout == EROFS_INODE_CHUNK_BASED)
|
||||
/* fill chunked inode summary info */
|
||||
vi->chunkformat = le16_to_cpu(die->i_u.c.format);
|
||||
kfree(copied);
|
||||
copied = NULL;
|
||||
break;
|
||||
case EROFS_INODE_LAYOUT_COMPACT:
|
||||
vi->inode_isize = sizeof(struct erofs_inode_compact);
|
||||
@@ -160,6 +164,8 @@ static struct page *erofs_read_inode(struct inode *inode,
|
||||
inode->i_size = le32_to_cpu(dic->i_size);
|
||||
if (erofs_inode_is_data_compressed(vi->datalayout))
|
||||
nblks = le32_to_cpu(dic->i_u.compressed_blocks);
|
||||
else if (vi->datalayout == EROFS_INODE_CHUNK_BASED)
|
||||
vi->chunkformat = le16_to_cpu(dic->i_u.c.format);
|
||||
break;
|
||||
default:
|
||||
erofs_err(inode->i_sb,
|
||||
@@ -169,11 +175,26 @@ static struct page *erofs_read_inode(struct inode *inode,
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
if (vi->datalayout == EROFS_INODE_CHUNK_BASED) {
|
||||
if (!(vi->chunkformat & EROFS_CHUNK_FORMAT_ALL)) {
|
||||
erofs_err(inode->i_sb,
|
||||
"unsupported chunk format %x of nid %llu",
|
||||
vi->chunkformat, vi->nid);
|
||||
err = -EOPNOTSUPP;
|
||||
goto err_out;
|
||||
}
|
||||
vi->chunkbits = LOG_BLOCK_SIZE +
|
||||
(vi->chunkformat & EROFS_CHUNK_FORMAT_BLKBITS_MASK);
|
||||
}
|
||||
inode->i_mtime.tv_sec = inode->i_ctime.tv_sec;
|
||||
inode->i_atime.tv_sec = inode->i_ctime.tv_sec;
|
||||
inode->i_mtime.tv_nsec = inode->i_ctime.tv_nsec;
|
||||
inode->i_atime.tv_nsec = inode->i_ctime.tv_nsec;
|
||||
|
||||
inode->i_flags &= ~S_DAX;
|
||||
if (test_opt(&sbi->ctx, DAX_ALWAYS) && S_ISREG(inode->i_mode) &&
|
||||
vi->datalayout == EROFS_INODE_FLAT_PLAIN)
|
||||
inode->i_flags |= S_DAX;
|
||||
if (!nblks)
|
||||
/* measure inode.i_blocks as generic filesystems */
|
||||
inode->i_blocks = roundup(inode->i_size, EROFS_BLKSIZ) >> 9;
|
||||
@@ -247,7 +268,10 @@ static int erofs_fill_inode(struct inode *inode, int isdir)
|
||||
switch (inode->i_mode & S_IFMT) {
|
||||
case S_IFREG:
|
||||
inode->i_op = &erofs_generic_iops;
|
||||
inode->i_fop = &generic_ro_fops;
|
||||
if (erofs_inode_is_data_compressed(vi->datalayout))
|
||||
inode->i_fop = &generic_ro_fops;
|
||||
else
|
||||
inode->i_fop = &erofs_file_fops;
|
||||
break;
|
||||
case S_IFDIR:
|
||||
inode->i_op = &erofs_dir_iops;
|
||||
@@ -358,6 +382,7 @@ const struct inode_operations erofs_generic_iops = {
|
||||
.getattr = erofs_getattr,
|
||||
.listxattr = erofs_listxattr,
|
||||
.get_acl = erofs_get_acl,
|
||||
.fiemap = erofs_fiemap,
|
||||
};
|
||||
|
||||
const struct inode_operations erofs_symlink_iops = {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
/*
|
||||
* Copyright (C) 2017-2018 HUAWEI, Inc.
|
||||
* https://www.huawei.com/
|
||||
* Copyright (C) 2021, Alibaba Cloud
|
||||
*/
|
||||
#ifndef __EROFS_INTERNAL_H
|
||||
#define __EROFS_INTERNAL_H
|
||||
@@ -15,6 +16,7 @@
|
||||
#include <linux/magic.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/iomap.h>
|
||||
#include "erofs_fs.h"
|
||||
|
||||
/* redefine pr_fmt "erofs: " */
|
||||
@@ -83,6 +85,7 @@ struct erofs_sb_info {
|
||||
|
||||
struct erofs_sb_lz4_info lz4;
|
||||
#endif /* CONFIG_EROFS_FS_ZIP */
|
||||
struct dax_device *dax_dev;
|
||||
u32 blocks;
|
||||
u32 meta_blkaddr;
|
||||
#ifdef CONFIG_EROFS_FS_XATTR
|
||||
@@ -115,6 +118,8 @@ struct erofs_sb_info {
|
||||
/* Mount flags set via mount options or defaults */
|
||||
#define EROFS_MOUNT_XATTR_USER 0x00000010
|
||||
#define EROFS_MOUNT_POSIX_ACL 0x00000020
|
||||
#define EROFS_MOUNT_DAX_ALWAYS 0x00000040
|
||||
#define EROFS_MOUNT_DAX_NEVER 0x00000080
|
||||
|
||||
#define clear_opt(ctx, option) ((ctx)->mount_opt &= ~EROFS_MOUNT_##option)
|
||||
#define set_opt(ctx, option) ((ctx)->mount_opt |= EROFS_MOUNT_##option)
|
||||
@@ -257,6 +262,10 @@ struct erofs_inode {
|
||||
|
||||
union {
|
||||
erofs_blk_t raw_blkaddr;
|
||||
struct {
|
||||
unsigned short chunkformat;
|
||||
unsigned char chunkbits;
|
||||
};
|
||||
#ifdef CONFIG_EROFS_FS_ZIP
|
||||
struct {
|
||||
unsigned short z_advise;
|
||||
@@ -353,8 +362,15 @@ struct erofs_map_blocks {
|
||||
|
||||
/* Flags used by erofs_map_blocks_flatmode() */
|
||||
#define EROFS_GET_BLOCKS_RAW 0x0001
|
||||
/*
|
||||
* Used to get the exact decompressed length, e.g. fiemap (consider lookback
|
||||
* approach instead if possible since it's more metadata lightweight.)
|
||||
*/
|
||||
#define EROFS_GET_BLOCKS_FIEMAP 0x0002
|
||||
|
||||
/* zmap.c */
|
||||
extern const struct iomap_ops z_erofs_iomap_report_ops;
|
||||
|
||||
#ifdef CONFIG_EROFS_FS_ZIP
|
||||
int z_erofs_fill_inode(struct inode *inode);
|
||||
int z_erofs_map_blocks_iter(struct inode *inode,
|
||||
@@ -371,7 +387,10 @@ static inline int z_erofs_map_blocks_iter(struct inode *inode,
|
||||
#endif /* !CONFIG_EROFS_FS_ZIP */
|
||||
|
||||
/* data.c */
|
||||
extern const struct file_operations erofs_file_fops;
|
||||
struct page *erofs_get_meta_page(struct super_block *sb, erofs_blk_t blkaddr);
|
||||
int erofs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
|
||||
u64 start, u64 len);
|
||||
|
||||
/* inode.c */
|
||||
static inline unsigned long erofs_inode_hash(erofs_nid_t nid)
|
||||
@@ -441,8 +460,7 @@ int __init z_erofs_init_zip_subsystem(void);
|
||||
void z_erofs_exit_zip_subsystem(void);
|
||||
int erofs_try_to_free_all_cached_pages(struct erofs_sb_info *sbi,
|
||||
struct erofs_workgroup *egrp);
|
||||
int erofs_try_to_free_cached_page(struct address_space *mapping,
|
||||
struct page *page);
|
||||
int erofs_try_to_free_cached_page(struct page *page);
|
||||
int z_erofs_load_lz4_config(struct super_block *sb,
|
||||
struct erofs_super_block *dsb,
|
||||
struct z_erofs_lz4_cfgs *lz4, int len);
|
||||
|
||||
@@ -245,4 +245,5 @@ const struct inode_operations erofs_dir_iops = {
|
||||
.getattr = erofs_getattr,
|
||||
.listxattr = erofs_listxattr,
|
||||
.get_acl = erofs_get_acl,
|
||||
.fiemap = erofs_fiemap,
|
||||
};
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <linux/crc32c.h>
|
||||
#include <linux/fs_context.h>
|
||||
#include <linux/fs_parser.h>
|
||||
#include <linux/dax.h>
|
||||
#include "xattr.h"
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
@@ -355,6 +356,8 @@ enum {
|
||||
Opt_user_xattr,
|
||||
Opt_acl,
|
||||
Opt_cache_strategy,
|
||||
Opt_dax,
|
||||
Opt_dax_enum,
|
||||
Opt_err
|
||||
};
|
||||
|
||||
@@ -365,14 +368,47 @@ static const struct constant_table erofs_param_cache_strategy[] = {
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct constant_table erofs_dax_param_enums[] = {
|
||||
{"always", EROFS_MOUNT_DAX_ALWAYS},
|
||||
{"never", EROFS_MOUNT_DAX_NEVER},
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct fs_parameter_spec erofs_fs_parameters[] = {
|
||||
fsparam_flag_no("user_xattr", Opt_user_xattr),
|
||||
fsparam_flag_no("acl", Opt_acl),
|
||||
fsparam_enum("cache_strategy", Opt_cache_strategy,
|
||||
erofs_param_cache_strategy),
|
||||
fsparam_flag("dax", Opt_dax),
|
||||
fsparam_enum("dax", Opt_dax_enum, erofs_dax_param_enums),
|
||||
{}
|
||||
};
|
||||
|
||||
static bool erofs_fc_set_dax_mode(struct fs_context *fc, unsigned int mode)
|
||||
{
|
||||
#ifdef CONFIG_FS_DAX
|
||||
struct erofs_fs_context *ctx = fc->fs_private;
|
||||
|
||||
switch (mode) {
|
||||
case EROFS_MOUNT_DAX_ALWAYS:
|
||||
warnfc(fc, "DAX enabled. Warning: EXPERIMENTAL, use at your own risk");
|
||||
set_opt(ctx, DAX_ALWAYS);
|
||||
clear_opt(ctx, DAX_NEVER);
|
||||
return true;
|
||||
case EROFS_MOUNT_DAX_NEVER:
|
||||
set_opt(ctx, DAX_NEVER);
|
||||
clear_opt(ctx, DAX_ALWAYS);
|
||||
return true;
|
||||
default:
|
||||
DBG_BUGON(1);
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
errorfc(fc, "dax options not supported");
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int erofs_fc_parse_param(struct fs_context *fc,
|
||||
struct fs_parameter *param)
|
||||
{
|
||||
@@ -412,6 +448,14 @@ static int erofs_fc_parse_param(struct fs_context *fc,
|
||||
errorfc(fc, "compression not supported, cache_strategy ignored");
|
||||
#endif
|
||||
break;
|
||||
case Opt_dax:
|
||||
if (!erofs_fc_set_dax_mode(fc, EROFS_MOUNT_DAX_ALWAYS))
|
||||
return -EINVAL;
|
||||
break;
|
||||
case Opt_dax_enum:
|
||||
if (!erofs_fc_set_dax_mode(fc, result.uint_32))
|
||||
return -EINVAL;
|
||||
break;
|
||||
default:
|
||||
return -ENOPARAM;
|
||||
}
|
||||
@@ -430,7 +474,7 @@ static int erofs_managed_cache_releasepage(struct page *page, gfp_t gfp_mask)
|
||||
DBG_BUGON(mapping->a_ops != &managed_cache_aops);
|
||||
|
||||
if (PagePrivate(page))
|
||||
ret = erofs_try_to_free_cached_page(mapping, page);
|
||||
ret = erofs_try_to_free_cached_page(page);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -496,10 +540,16 @@ static int erofs_fc_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||
return -ENOMEM;
|
||||
|
||||
sb->s_fs_info = sbi;
|
||||
sbi->dax_dev = fs_dax_get_by_bdev(sb->s_bdev);
|
||||
err = erofs_read_superblock(sb);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (test_opt(ctx, DAX_ALWAYS) &&
|
||||
!bdev_dax_supported(sb->s_bdev, EROFS_BLKSIZ)) {
|
||||
errorfc(fc, "DAX unsupported by block device. Turning off DAX.");
|
||||
clear_opt(ctx, DAX_ALWAYS);
|
||||
}
|
||||
sb->s_flags |= SB_RDONLY | SB_NOATIME;
|
||||
sb->s_maxbytes = MAX_LFS_FILESIZE;
|
||||
sb->s_time_gran = 1;
|
||||
@@ -609,6 +659,7 @@ static void erofs_kill_sb(struct super_block *sb)
|
||||
sbi = EROFS_SB(sb);
|
||||
if (!sbi)
|
||||
return;
|
||||
fs_put_dax(sbi->dax_dev);
|
||||
kfree(sbi);
|
||||
sb->s_fs_info = NULL;
|
||||
}
|
||||
@@ -711,8 +762,8 @@ static int erofs_statfs(struct dentry *dentry, struct kstatfs *buf)
|
||||
|
||||
static int erofs_show_options(struct seq_file *seq, struct dentry *root)
|
||||
{
|
||||
struct erofs_sb_info *sbi __maybe_unused = EROFS_SB(root->d_sb);
|
||||
struct erofs_fs_context *ctx __maybe_unused = &sbi->ctx;
|
||||
struct erofs_sb_info *sbi = EROFS_SB(root->d_sb);
|
||||
struct erofs_fs_context *ctx = &sbi->ctx;
|
||||
|
||||
#ifdef CONFIG_EROFS_FS_XATTR
|
||||
if (test_opt(ctx, XATTR_USER))
|
||||
@@ -734,6 +785,10 @@ static int erofs_show_options(struct seq_file *seq, struct dentry *root)
|
||||
else if (ctx->cache_strategy == EROFS_ZIP_CACHE_READAROUND)
|
||||
seq_puts(seq, ",cache_strategy=readaround");
|
||||
#endif
|
||||
if (test_opt(ctx, DAX_ALWAYS))
|
||||
seq_puts(seq, ",dax=always");
|
||||
if (test_opt(ctx, DAX_NEVER))
|
||||
seq_puts(seq, ",dax=never");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -673,12 +673,15 @@ ssize_t erofs_listxattr(struct dentry *dentry,
|
||||
}
|
||||
|
||||
#ifdef CONFIG_EROFS_FS_POSIX_ACL
|
||||
struct posix_acl *erofs_get_acl(struct inode *inode, int type)
|
||||
struct posix_acl *erofs_get_acl(struct inode *inode, int type, bool rcu)
|
||||
{
|
||||
struct posix_acl *acl;
|
||||
int prefix, rc;
|
||||
char *value = NULL;
|
||||
|
||||
if (rcu)
|
||||
return ERR_PTR(-ECHILD);
|
||||
|
||||
switch (type) {
|
||||
case ACL_TYPE_ACCESS:
|
||||
prefix = EROFS_XATTR_INDEX_POSIX_ACL_ACCESS;
|
||||
|
||||
@@ -80,7 +80,7 @@ static inline int erofs_getxattr(struct inode *inode, int index,
|
||||
#endif /* !CONFIG_EROFS_FS_XATTR */
|
||||
|
||||
#ifdef CONFIG_EROFS_FS_POSIX_ACL
|
||||
struct posix_acl *erofs_get_acl(struct inode *inode, int type);
|
||||
struct posix_acl *erofs_get_acl(struct inode *inode, int type, bool rcu);
|
||||
#else
|
||||
#define erofs_get_acl (NULL)
|
||||
#endif
|
||||
|
||||
@@ -309,7 +309,6 @@ int erofs_try_to_free_all_cached_pages(struct erofs_sb_info *sbi,
|
||||
{
|
||||
struct z_erofs_pcluster *const pcl =
|
||||
container_of(grp, struct z_erofs_pcluster, obj);
|
||||
struct address_space *const mapping = MNGD_MAPPING(sbi);
|
||||
int i;
|
||||
|
||||
/*
|
||||
@@ -326,7 +325,7 @@ int erofs_try_to_free_all_cached_pages(struct erofs_sb_info *sbi,
|
||||
if (!trylock_page(page))
|
||||
return -EBUSY;
|
||||
|
||||
if (page->mapping != mapping)
|
||||
if (!erofs_page_is_managed(sbi, page))
|
||||
continue;
|
||||
|
||||
/* barrier is implied in the following 'unlock_page' */
|
||||
@@ -337,8 +336,7 @@ int erofs_try_to_free_all_cached_pages(struct erofs_sb_info *sbi,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int erofs_try_to_free_cached_page(struct address_space *mapping,
|
||||
struct page *page)
|
||||
int erofs_try_to_free_cached_page(struct page *page)
|
||||
{
|
||||
struct z_erofs_pcluster *const pcl = (void *)page_private(page);
|
||||
int ret = 0; /* 0 - busy */
|
||||
|
||||
133
fs/erofs/zmap.c
133
fs/erofs/zmap.c
@@ -212,9 +212,34 @@ static unsigned int decode_compactedbits(unsigned int lobits,
|
||||
return lo;
|
||||
}
|
||||
|
||||
static int get_compacted_la_distance(unsigned int lclusterbits,
|
||||
unsigned int encodebits,
|
||||
unsigned int vcnt, u8 *in, int i)
|
||||
{
|
||||
const unsigned int lomask = (1 << lclusterbits) - 1;
|
||||
unsigned int lo, d1 = 0;
|
||||
u8 type;
|
||||
|
||||
DBG_BUGON(i >= vcnt);
|
||||
|
||||
do {
|
||||
lo = decode_compactedbits(lclusterbits, lomask,
|
||||
in, encodebits * i, &type);
|
||||
|
||||
if (type != Z_EROFS_VLE_CLUSTER_TYPE_NONHEAD)
|
||||
return d1;
|
||||
++d1;
|
||||
} while (++i < vcnt);
|
||||
|
||||
/* vcnt - 1 (Z_EROFS_VLE_CLUSTER_TYPE_NONHEAD) item */
|
||||
if (!(lo & Z_EROFS_VLE_DI_D0_CBLKCNT))
|
||||
d1 += lo - 1;
|
||||
return d1;
|
||||
}
|
||||
|
||||
static int unpack_compacted_index(struct z_erofs_maprecorder *m,
|
||||
unsigned int amortizedshift,
|
||||
unsigned int eofs)
|
||||
unsigned int eofs, bool lookahead)
|
||||
{
|
||||
struct erofs_inode *const vi = EROFS_I(m->inode);
|
||||
const unsigned int lclusterbits = vi->z_logical_clusterbits;
|
||||
@@ -243,6 +268,11 @@ static int unpack_compacted_index(struct z_erofs_maprecorder *m,
|
||||
m->type = type;
|
||||
if (type == Z_EROFS_VLE_CLUSTER_TYPE_NONHEAD) {
|
||||
m->clusterofs = 1 << lclusterbits;
|
||||
|
||||
/* figure out lookahead_distance: delta[1] if needed */
|
||||
if (lookahead)
|
||||
m->delta[1] = get_compacted_la_distance(lclusterbits,
|
||||
encodebits, vcnt, in, i);
|
||||
if (lo & Z_EROFS_VLE_DI_D0_CBLKCNT) {
|
||||
if (!big_pcluster) {
|
||||
DBG_BUGON(1);
|
||||
@@ -313,7 +343,7 @@ static int unpack_compacted_index(struct z_erofs_maprecorder *m,
|
||||
}
|
||||
|
||||
static int compacted_load_cluster_from_disk(struct z_erofs_maprecorder *m,
|
||||
unsigned long lcn)
|
||||
unsigned long lcn, bool lookahead)
|
||||
{
|
||||
struct inode *const inode = m->inode;
|
||||
struct erofs_inode *const vi = EROFS_I(inode);
|
||||
@@ -364,11 +394,12 @@ out:
|
||||
err = z_erofs_reload_indexes(m, erofs_blknr(pos));
|
||||
if (err)
|
||||
return err;
|
||||
return unpack_compacted_index(m, amortizedshift, erofs_blkoff(pos));
|
||||
return unpack_compacted_index(m, amortizedshift, erofs_blkoff(pos),
|
||||
lookahead);
|
||||
}
|
||||
|
||||
static int z_erofs_load_cluster_from_disk(struct z_erofs_maprecorder *m,
|
||||
unsigned int lcn)
|
||||
unsigned int lcn, bool lookahead)
|
||||
{
|
||||
const unsigned int datamode = EROFS_I(m->inode)->datalayout;
|
||||
|
||||
@@ -376,7 +407,7 @@ static int z_erofs_load_cluster_from_disk(struct z_erofs_maprecorder *m,
|
||||
return legacy_load_cluster_from_disk(m, lcn);
|
||||
|
||||
if (datamode == EROFS_INODE_FLAT_COMPRESSION)
|
||||
return compacted_load_cluster_from_disk(m, lcn);
|
||||
return compacted_load_cluster_from_disk(m, lcn, lookahead);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -399,7 +430,7 @@ static int z_erofs_extent_lookback(struct z_erofs_maprecorder *m,
|
||||
|
||||
/* load extent head logical cluster if needed */
|
||||
lcn -= lookback_distance;
|
||||
err = z_erofs_load_cluster_from_disk(m, lcn);
|
||||
err = z_erofs_load_cluster_from_disk(m, lcn, false);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -450,7 +481,7 @@ static int z_erofs_get_extent_compressedlen(struct z_erofs_maprecorder *m,
|
||||
if (m->compressedlcs)
|
||||
goto out;
|
||||
|
||||
err = z_erofs_load_cluster_from_disk(m, lcn);
|
||||
err = z_erofs_load_cluster_from_disk(m, lcn, false);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -498,6 +529,48 @@ err_bonus_cblkcnt:
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
|
||||
static int z_erofs_get_extent_decompressedlen(struct z_erofs_maprecorder *m)
|
||||
{
|
||||
struct inode *inode = m->inode;
|
||||
struct erofs_inode *vi = EROFS_I(inode);
|
||||
struct erofs_map_blocks *map = m->map;
|
||||
unsigned int lclusterbits = vi->z_logical_clusterbits;
|
||||
u64 lcn = m->lcn, headlcn = map->m_la >> lclusterbits;
|
||||
int err;
|
||||
|
||||
do {
|
||||
/* handle the last EOF pcluster (no next HEAD lcluster) */
|
||||
if ((lcn << lclusterbits) >= inode->i_size) {
|
||||
map->m_llen = inode->i_size - map->m_la;
|
||||
return 0;
|
||||
}
|
||||
|
||||
err = z_erofs_load_cluster_from_disk(m, lcn, true);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (m->type == Z_EROFS_VLE_CLUSTER_TYPE_NONHEAD) {
|
||||
DBG_BUGON(!m->delta[1] &&
|
||||
m->clusterofs != 1 << lclusterbits);
|
||||
} else if (m->type == Z_EROFS_VLE_CLUSTER_TYPE_PLAIN ||
|
||||
m->type == Z_EROFS_VLE_CLUSTER_TYPE_HEAD) {
|
||||
/* go on until the next HEAD lcluster */
|
||||
if (lcn != headlcn)
|
||||
break;
|
||||
m->delta[1] = 1;
|
||||
} else {
|
||||
erofs_err(inode->i_sb, "unknown type %u @ lcn %llu of nid %llu",
|
||||
m->type, lcn, vi->nid);
|
||||
DBG_BUGON(1);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
lcn += m->delta[1];
|
||||
} while (m->delta[1]);
|
||||
|
||||
map->m_llen = (lcn << lclusterbits) + m->clusterofs - map->m_la;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int z_erofs_map_blocks_iter(struct inode *inode,
|
||||
struct erofs_map_blocks *map,
|
||||
int flags)
|
||||
@@ -531,7 +604,7 @@ int z_erofs_map_blocks_iter(struct inode *inode,
|
||||
initial_lcn = ofs >> lclusterbits;
|
||||
endoff = ofs & ((1 << lclusterbits) - 1);
|
||||
|
||||
err = z_erofs_load_cluster_from_disk(&m, initial_lcn);
|
||||
err = z_erofs_load_cluster_from_disk(&m, initial_lcn, false);
|
||||
if (err)
|
||||
goto unmap_out;
|
||||
|
||||
@@ -581,6 +654,12 @@ int z_erofs_map_blocks_iter(struct inode *inode,
|
||||
err = z_erofs_get_extent_compressedlen(&m, initial_lcn);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
if (flags & EROFS_GET_BLOCKS_FIEMAP) {
|
||||
err = z_erofs_get_extent_decompressedlen(&m);
|
||||
if (!err)
|
||||
map->m_flags |= EROFS_MAP_FULL_MAPPED;
|
||||
}
|
||||
unmap_out:
|
||||
if (m.kaddr)
|
||||
kunmap_atomic(m.kaddr);
|
||||
@@ -596,3 +675,41 @@ out:
|
||||
DBG_BUGON(err < 0 && err != -ENOMEM);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int z_erofs_iomap_begin_report(struct inode *inode, loff_t offset,
|
||||
loff_t length, unsigned int flags,
|
||||
struct iomap *iomap, struct iomap *srcmap)
|
||||
{
|
||||
int ret;
|
||||
struct erofs_map_blocks map = { .m_la = offset };
|
||||
|
||||
ret = z_erofs_map_blocks_iter(inode, &map, EROFS_GET_BLOCKS_FIEMAP);
|
||||
if (map.mpage)
|
||||
put_page(map.mpage);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
iomap->bdev = inode->i_sb->s_bdev;
|
||||
iomap->offset = map.m_la;
|
||||
iomap->length = map.m_llen;
|
||||
if (map.m_flags & EROFS_MAP_MAPPED) {
|
||||
iomap->type = IOMAP_MAPPED;
|
||||
iomap->addr = map.m_pa;
|
||||
} else {
|
||||
iomap->type = IOMAP_HOLE;
|
||||
iomap->addr = IOMAP_NULL_ADDR;
|
||||
/*
|
||||
* No strict rule how to describe extents for post EOF, yet
|
||||
* we need do like below. Otherwise, iomap itself will get
|
||||
* into an endless loop on post EOF.
|
||||
*/
|
||||
if (iomap->offset >= inode->i_size)
|
||||
iomap->length = length + map.m_la - offset;
|
||||
}
|
||||
iomap->flags = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct iomap_ops z_erofs_iomap_report_ops = {
|
||||
.iomap_begin = z_erofs_iomap_begin_report,
|
||||
};
|
||||
|
||||
@@ -141,13 +141,16 @@ fail:
|
||||
* inode->i_mutex: don't care
|
||||
*/
|
||||
struct posix_acl *
|
||||
ext2_get_acl(struct inode *inode, int type)
|
||||
ext2_get_acl(struct inode *inode, int type, bool rcu)
|
||||
{
|
||||
int name_index;
|
||||
char *value = NULL;
|
||||
struct posix_acl *acl;
|
||||
int retval;
|
||||
|
||||
if (rcu)
|
||||
return ERR_PTR(-ECHILD);
|
||||
|
||||
switch (type) {
|
||||
case ACL_TYPE_ACCESS:
|
||||
name_index = EXT2_XATTR_INDEX_POSIX_ACL_ACCESS;
|
||||
|
||||
@@ -55,7 +55,7 @@ static inline int ext2_acl_count(size_t size)
|
||||
#ifdef CONFIG_EXT2_FS_POSIX_ACL
|
||||
|
||||
/* acl.c */
|
||||
extern struct posix_acl *ext2_get_acl(struct inode *inode, int type);
|
||||
extern struct posix_acl *ext2_get_acl(struct inode *inode, int type, bool rcu);
|
||||
extern int ext2_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
|
||||
struct posix_acl *acl, int type);
|
||||
extern int ext2_init_acl (struct inode *, struct inode *);
|
||||
|
||||
@@ -142,13 +142,16 @@ fail:
|
||||
* inode->i_mutex: don't care
|
||||
*/
|
||||
struct posix_acl *
|
||||
ext4_get_acl(struct inode *inode, int type)
|
||||
ext4_get_acl(struct inode *inode, int type, bool rcu)
|
||||
{
|
||||
int name_index;
|
||||
char *value = NULL;
|
||||
struct posix_acl *acl;
|
||||
int retval;
|
||||
|
||||
if (rcu)
|
||||
return ERR_PTR(-ECHILD);
|
||||
|
||||
switch (type) {
|
||||
case ACL_TYPE_ACCESS:
|
||||
name_index = EXT4_XATTR_INDEX_POSIX_ACL_ACCESS;
|
||||
|
||||
@@ -55,7 +55,7 @@ static inline int ext4_acl_count(size_t size)
|
||||
#ifdef CONFIG_EXT4_FS_POSIX_ACL
|
||||
|
||||
/* acl.c */
|
||||
struct posix_acl *ext4_get_acl(struct inode *inode, int type);
|
||||
struct posix_acl *ext4_get_acl(struct inode *inode, int type, bool rcu);
|
||||
int ext4_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
|
||||
struct posix_acl *acl, int type);
|
||||
extern int ext4_init_acl(handle_t *, struct inode *, struct inode *);
|
||||
|
||||
@@ -196,8 +196,11 @@ static struct posix_acl *__f2fs_get_acl(struct inode *inode, int type,
|
||||
return acl;
|
||||
}
|
||||
|
||||
struct posix_acl *f2fs_get_acl(struct inode *inode, int type)
|
||||
struct posix_acl *f2fs_get_acl(struct inode *inode, int type, bool rcu)
|
||||
{
|
||||
if (rcu)
|
||||
return ERR_PTR(-ECHILD);
|
||||
|
||||
return __f2fs_get_acl(inode, type, NULL);
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ struct f2fs_acl_header {
|
||||
|
||||
#ifdef CONFIG_F2FS_FS_POSIX_ACL
|
||||
|
||||
extern struct posix_acl *f2fs_get_acl(struct inode *, int);
|
||||
extern struct posix_acl *f2fs_get_acl(struct inode *, int, bool);
|
||||
extern int f2fs_set_acl(struct user_namespace *, struct inode *,
|
||||
struct posix_acl *, int);
|
||||
extern int f2fs_init_acl(struct inode *, struct inode *, struct page *,
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#include <linux/posix_acl.h>
|
||||
#include <linux/posix_acl_xattr.h>
|
||||
|
||||
struct posix_acl *fuse_get_acl(struct inode *inode, int type)
|
||||
struct posix_acl *fuse_get_acl(struct inode *inode, int type, bool rcu)
|
||||
{
|
||||
struct fuse_conn *fc = get_fuse_conn(inode);
|
||||
int size;
|
||||
@@ -19,6 +19,9 @@ struct posix_acl *fuse_get_acl(struct inode *inode, int type)
|
||||
void *value = NULL;
|
||||
struct posix_acl *acl;
|
||||
|
||||
if (rcu)
|
||||
return ERR_PTR(-ECHILD);
|
||||
|
||||
if (fuse_is_bad(inode))
|
||||
return ERR_PTR(-EIO);
|
||||
|
||||
|
||||
@@ -1233,7 +1233,7 @@ extern const struct xattr_handler *fuse_acl_xattr_handlers[];
|
||||
extern const struct xattr_handler *fuse_no_acl_xattr_handlers[];
|
||||
|
||||
struct posix_acl;
|
||||
struct posix_acl *fuse_get_acl(struct inode *inode, int type);
|
||||
struct posix_acl *fuse_get_acl(struct inode *inode, int type, bool rcu);
|
||||
int fuse_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
|
||||
struct posix_acl *acl, int type);
|
||||
|
||||
|
||||
@@ -57,13 +57,16 @@ static struct posix_acl *__gfs2_get_acl(struct inode *inode, int type)
|
||||
return acl;
|
||||
}
|
||||
|
||||
struct posix_acl *gfs2_get_acl(struct inode *inode, int type)
|
||||
struct posix_acl *gfs2_get_acl(struct inode *inode, int type, bool rcu)
|
||||
{
|
||||
struct gfs2_inode *ip = GFS2_I(inode);
|
||||
struct gfs2_holder gh;
|
||||
bool need_unlock = false;
|
||||
struct posix_acl *acl;
|
||||
|
||||
if (rcu)
|
||||
return ERR_PTR(-ECHILD);
|
||||
|
||||
if (!gfs2_glock_is_locked_by_me(ip->i_gl)) {
|
||||
int ret = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED,
|
||||
LM_FLAG_ANY, &gh);
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
#define GFS2_ACL_MAX_ENTRIES(sdp) ((300 << (sdp)->sd_sb.sb_bsize_shift) >> 12)
|
||||
|
||||
extern struct posix_acl *gfs2_get_acl(struct inode *inode, int type);
|
||||
extern struct posix_acl *gfs2_get_acl(struct inode *inode, int type, bool rcu);
|
||||
extern int __gfs2_set_acl(struct inode *inode, struct posix_acl *acl, int type);
|
||||
extern int gfs2_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
|
||||
struct posix_acl *acl, int type);
|
||||
|
||||
@@ -173,12 +173,15 @@ static void *jffs2_acl_to_medium(const struct posix_acl *acl, size_t *size)
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
struct posix_acl *jffs2_get_acl(struct inode *inode, int type)
|
||||
struct posix_acl *jffs2_get_acl(struct inode *inode, int type, bool rcu)
|
||||
{
|
||||
struct posix_acl *acl;
|
||||
char *value = NULL;
|
||||
int rc, xprefix;
|
||||
|
||||
if (rcu)
|
||||
return ERR_PTR(-ECHILD);
|
||||
|
||||
switch (type) {
|
||||
case ACL_TYPE_ACCESS:
|
||||
xprefix = JFFS2_XPREFIX_ACL_ACCESS;
|
||||
|
||||
@@ -27,7 +27,7 @@ struct jffs2_acl_header {
|
||||
|
||||
#ifdef CONFIG_JFFS2_FS_POSIX_ACL
|
||||
|
||||
struct posix_acl *jffs2_get_acl(struct inode *inode, int type);
|
||||
struct posix_acl *jffs2_get_acl(struct inode *inode, int type, bool rcu);
|
||||
int jffs2_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
|
||||
struct posix_acl *acl, int type);
|
||||
extern int jffs2_init_acl_pre(struct inode *, struct inode *, umode_t *);
|
||||
|
||||
@@ -14,13 +14,16 @@
|
||||
#include "jfs_xattr.h"
|
||||
#include "jfs_acl.h"
|
||||
|
||||
struct posix_acl *jfs_get_acl(struct inode *inode, int type)
|
||||
struct posix_acl *jfs_get_acl(struct inode *inode, int type, bool rcu)
|
||||
{
|
||||
struct posix_acl *acl;
|
||||
char *ea_name;
|
||||
int size;
|
||||
char *value = NULL;
|
||||
|
||||
if (rcu)
|
||||
return ERR_PTR(-ECHILD);
|
||||
|
||||
switch(type) {
|
||||
case ACL_TYPE_ACCESS:
|
||||
ea_name = XATTR_NAME_POSIX_ACL_ACCESS;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
#ifdef CONFIG_JFS_POSIX_ACL
|
||||
|
||||
struct posix_acl *jfs_get_acl(struct inode *inode, int type);
|
||||
struct posix_acl *jfs_get_acl(struct inode *inode, int type, bool rcu);
|
||||
int jfs_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
|
||||
struct posix_acl *acl, int type);
|
||||
int jfs_init_acl(tid_t, struct inode *, struct inode *);
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
* nfs3acl.c
|
||||
*/
|
||||
#ifdef CONFIG_NFS_V3_ACL
|
||||
extern struct posix_acl *nfs3_get_acl(struct inode *inode, int type);
|
||||
extern struct posix_acl *nfs3_get_acl(struct inode *inode, int type, bool rcu);
|
||||
extern int nfs3_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
|
||||
struct posix_acl *acl, int type);
|
||||
extern int nfs3_proc_setacls(struct inode *inode, struct posix_acl *acl,
|
||||
|
||||
@@ -44,7 +44,7 @@ static void nfs3_abort_get_acl(struct posix_acl **p)
|
||||
cmpxchg(p, sentinel, ACL_NOT_CACHED);
|
||||
}
|
||||
|
||||
struct posix_acl *nfs3_get_acl(struct inode *inode, int type)
|
||||
struct posix_acl *nfs3_get_acl(struct inode *inode, int type, bool rcu)
|
||||
{
|
||||
struct nfs_server *server = NFS_SERVER(inode);
|
||||
struct page *pages[NFSACL_MAXPAGES] = { };
|
||||
@@ -62,6 +62,9 @@ struct posix_acl *nfs3_get_acl(struct inode *inode, int type)
|
||||
};
|
||||
int status, count;
|
||||
|
||||
if (rcu)
|
||||
return ERR_PTR(-ECHILD);
|
||||
|
||||
if (!nfs_server_capable(inode, NFS_CAP_ACLS))
|
||||
return ERR_PTR(-EOPNOTSUPP);
|
||||
|
||||
|
||||
@@ -289,7 +289,7 @@ unlock:
|
||||
return status;
|
||||
}
|
||||
|
||||
struct posix_acl *ocfs2_iop_get_acl(struct inode *inode, int type)
|
||||
struct posix_acl *ocfs2_iop_get_acl(struct inode *inode, int type, bool rcu)
|
||||
{
|
||||
struct ocfs2_super *osb;
|
||||
struct buffer_head *di_bh = NULL;
|
||||
@@ -297,6 +297,9 @@ struct posix_acl *ocfs2_iop_get_acl(struct inode *inode, int type)
|
||||
int had_lock;
|
||||
struct ocfs2_lock_holder oh;
|
||||
|
||||
if (rcu)
|
||||
return ERR_PTR(-ECHILD);
|
||||
|
||||
osb = OCFS2_SB(inode->i_sb);
|
||||
if (!(osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL))
|
||||
return NULL;
|
||||
|
||||
@@ -16,7 +16,7 @@ struct ocfs2_acl_entry {
|
||||
__le32 e_id;
|
||||
};
|
||||
|
||||
struct posix_acl *ocfs2_iop_get_acl(struct inode *inode, int type);
|
||||
struct posix_acl *ocfs2_iop_get_acl(struct inode *inode, int type, bool rcu);
|
||||
int ocfs2_iop_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
|
||||
struct posix_acl *acl, int type);
|
||||
extern int ocfs2_acl_chmod(struct inode *, struct buffer_head *);
|
||||
|
||||
@@ -10,12 +10,15 @@
|
||||
#include "orangefs-bufmap.h"
|
||||
#include <linux/posix_acl_xattr.h>
|
||||
|
||||
struct posix_acl *orangefs_get_acl(struct inode *inode, int type)
|
||||
struct posix_acl *orangefs_get_acl(struct inode *inode, int type, bool rcu)
|
||||
{
|
||||
struct posix_acl *acl;
|
||||
int ret;
|
||||
char *key = NULL, *value = NULL;
|
||||
|
||||
if (rcu)
|
||||
return ERR_PTR(-ECHILD);
|
||||
|
||||
switch (type) {
|
||||
case ACL_TYPE_ACCESS:
|
||||
key = XATTR_NAME_POSIX_ACL_ACCESS;
|
||||
|
||||
@@ -882,12 +882,7 @@ int orangefs_getattr(struct user_namespace *mnt_userns, const struct path *path,
|
||||
if (!(request_mask & STATX_SIZE))
|
||||
stat->result_mask &= ~STATX_SIZE;
|
||||
|
||||
stat->attributes_mask = STATX_ATTR_IMMUTABLE |
|
||||
STATX_ATTR_APPEND;
|
||||
if (inode->i_flags & S_IMMUTABLE)
|
||||
stat->attributes |= STATX_ATTR_IMMUTABLE;
|
||||
if (inode->i_flags & S_APPEND)
|
||||
stat->attributes |= STATX_ATTR_APPEND;
|
||||
generic_fill_statx_attr(inode, stat);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ enum orangefs_vfs_op_states {
|
||||
extern int orangefs_init_acl(struct inode *inode, struct inode *dir);
|
||||
extern const struct xattr_handler *orangefs_xattr_handlers[];
|
||||
|
||||
extern struct posix_acl *orangefs_get_acl(struct inode *inode, int type);
|
||||
extern struct posix_acl *orangefs_get_acl(struct inode *inode, int type, bool rcu);
|
||||
extern int orangefs_set_acl(struct user_namespace *mnt_userns,
|
||||
struct inode *inode, struct posix_acl *acl,
|
||||
int type);
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <linux/fs.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/fileattr.h>
|
||||
#include <linux/splice.h>
|
||||
#include <linux/xattr.h>
|
||||
#include <linux/security.h>
|
||||
@@ -62,7 +63,7 @@ int ovl_copy_xattr(struct super_block *sb, struct dentry *old,
|
||||
return list_size;
|
||||
}
|
||||
|
||||
buf = kzalloc(list_size, GFP_KERNEL);
|
||||
buf = kvzalloc(list_size, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -105,11 +106,12 @@ retry:
|
||||
if (size > value_size) {
|
||||
void *new;
|
||||
|
||||
new = krealloc(value, size, GFP_KERNEL);
|
||||
new = kvmalloc(size, GFP_KERNEL);
|
||||
if (!new) {
|
||||
error = -ENOMEM;
|
||||
break;
|
||||
}
|
||||
kvfree(value);
|
||||
value = new;
|
||||
value_size = size;
|
||||
goto retry;
|
||||
@@ -124,12 +126,50 @@ retry:
|
||||
error = 0;
|
||||
}
|
||||
}
|
||||
kfree(value);
|
||||
kvfree(value);
|
||||
out:
|
||||
kfree(buf);
|
||||
kvfree(buf);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int ovl_copy_fileattr(struct inode *inode, struct path *old,
|
||||
struct path *new)
|
||||
{
|
||||
struct fileattr oldfa = { .flags_valid = true };
|
||||
struct fileattr newfa = { .flags_valid = true };
|
||||
int err;
|
||||
|
||||
err = ovl_real_fileattr_get(old, &oldfa);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = ovl_real_fileattr_get(new, &newfa);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* We cannot set immutable and append-only flags on upper inode,
|
||||
* because we would not be able to link upper inode to upper dir
|
||||
* not set overlay private xattr on upper inode.
|
||||
* Store these flags in overlay.protattr xattr instead.
|
||||
*/
|
||||
if (oldfa.flags & OVL_PROT_FS_FLAGS_MASK) {
|
||||
err = ovl_set_protattr(inode, new->dentry, &oldfa);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
BUILD_BUG_ON(OVL_COPY_FS_FLAGS_MASK & ~FS_COMMON_FL);
|
||||
newfa.flags &= ~OVL_COPY_FS_FLAGS_MASK;
|
||||
newfa.flags |= (oldfa.flags & OVL_COPY_FS_FLAGS_MASK);
|
||||
|
||||
BUILD_BUG_ON(OVL_COPY_FSX_FLAGS_MASK & ~FS_XFLAG_COMMON);
|
||||
newfa.fsx_xflags &= ~OVL_COPY_FSX_FLAGS_MASK;
|
||||
newfa.fsx_xflags |= (oldfa.fsx_xflags & OVL_COPY_FSX_FLAGS_MASK);
|
||||
|
||||
return ovl_real_fileattr_set(new, &newfa);
|
||||
}
|
||||
|
||||
static int ovl_copy_up_data(struct ovl_fs *ofs, struct path *old,
|
||||
struct path *new, loff_t len)
|
||||
{
|
||||
@@ -331,8 +371,8 @@ out_err:
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
int ovl_set_origin(struct ovl_fs *ofs, struct dentry *dentry,
|
||||
struct dentry *lower, struct dentry *upper)
|
||||
int ovl_set_origin(struct ovl_fs *ofs, struct dentry *lower,
|
||||
struct dentry *upper)
|
||||
{
|
||||
const struct ovl_fh *fh = NULL;
|
||||
int err;
|
||||
@@ -351,7 +391,7 @@ int ovl_set_origin(struct ovl_fs *ofs, struct dentry *dentry,
|
||||
/*
|
||||
* Do not fail when upper doesn't support xattrs.
|
||||
*/
|
||||
err = ovl_check_setxattr(dentry, upper, OVL_XATTR_ORIGIN, fh->buf,
|
||||
err = ovl_check_setxattr(ofs, upper, OVL_XATTR_ORIGIN, fh->buf,
|
||||
fh ? fh->fb.len : 0, 0);
|
||||
kfree(fh);
|
||||
|
||||
@@ -493,20 +533,21 @@ static int ovl_link_up(struct ovl_copy_up_ctx *c)
|
||||
static int ovl_copy_up_inode(struct ovl_copy_up_ctx *c, struct dentry *temp)
|
||||
{
|
||||
struct ovl_fs *ofs = OVL_FS(c->dentry->d_sb);
|
||||
struct inode *inode = d_inode(c->dentry);
|
||||
struct path upperpath, datapath;
|
||||
int err;
|
||||
|
||||
ovl_path_upper(c->dentry, &upperpath);
|
||||
if (WARN_ON(upperpath.dentry != NULL))
|
||||
return -EIO;
|
||||
|
||||
upperpath.dentry = temp;
|
||||
|
||||
/*
|
||||
* Copy up data first and then xattrs. Writing data after
|
||||
* xattrs will remove security.capability xattr automatically.
|
||||
*/
|
||||
if (S_ISREG(c->stat.mode) && !c->metacopy) {
|
||||
struct path upperpath, datapath;
|
||||
|
||||
ovl_path_upper(c->dentry, &upperpath);
|
||||
if (WARN_ON(upperpath.dentry != NULL))
|
||||
return -EIO;
|
||||
upperpath.dentry = temp;
|
||||
|
||||
ovl_path_lowerdata(c->dentry, &datapath);
|
||||
err = ovl_copy_up_data(ofs, &datapath, &upperpath,
|
||||
c->stat.size);
|
||||
@@ -518,6 +559,16 @@ static int ovl_copy_up_inode(struct ovl_copy_up_ctx *c, struct dentry *temp)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (inode->i_flags & OVL_COPY_I_FLAGS_MASK) {
|
||||
/*
|
||||
* Copy the fileattr inode flags that are the source of already
|
||||
* copied i_flags
|
||||
*/
|
||||
err = ovl_copy_fileattr(inode, &c->lowerpath, &upperpath);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Store identifier of lower inode in upper inode xattr to
|
||||
* allow lookup of the copy up origin inode.
|
||||
@@ -526,13 +577,13 @@ static int ovl_copy_up_inode(struct ovl_copy_up_ctx *c, struct dentry *temp)
|
||||
* hard link.
|
||||
*/
|
||||
if (c->origin) {
|
||||
err = ovl_set_origin(ofs, c->dentry, c->lowerpath.dentry, temp);
|
||||
err = ovl_set_origin(ofs, c->lowerpath.dentry, temp);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (c->metacopy) {
|
||||
err = ovl_check_setxattr(c->dentry, temp, OVL_XATTR_METACOPY,
|
||||
err = ovl_check_setxattr(ofs, temp, OVL_XATTR_METACOPY,
|
||||
NULL, 0, -EOPNOTSUPP);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -233,9 +233,10 @@ struct dentry *ovl_create_temp(struct dentry *workdir, struct ovl_cattr *attr)
|
||||
static int ovl_set_opaque_xerr(struct dentry *dentry, struct dentry *upper,
|
||||
int xerr)
|
||||
{
|
||||
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
|
||||
int err;
|
||||
|
||||
err = ovl_check_setxattr(dentry, upper, OVL_XATTR_OPAQUE, "y", 1, xerr);
|
||||
err = ovl_check_setxattr(ofs, upper, OVL_XATTR_OPAQUE, "y", 1, xerr);
|
||||
if (!err)
|
||||
ovl_dentry_set_opaque(dentry);
|
||||
|
||||
@@ -320,6 +321,7 @@ static bool ovl_type_origin(struct dentry *dentry)
|
||||
static int ovl_create_upper(struct dentry *dentry, struct inode *inode,
|
||||
struct ovl_cattr *attr)
|
||||
{
|
||||
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
|
||||
struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent);
|
||||
struct inode *udir = upperdir->d_inode;
|
||||
struct dentry *newdentry;
|
||||
@@ -338,7 +340,8 @@ static int ovl_create_upper(struct dentry *dentry, struct inode *inode,
|
||||
if (IS_ERR(newdentry))
|
||||
goto out_unlock;
|
||||
|
||||
if (ovl_type_merge(dentry->d_parent) && d_is_dir(newdentry)) {
|
||||
if (ovl_type_merge(dentry->d_parent) && d_is_dir(newdentry) &&
|
||||
!ovl_allow_offline_changes(ofs)) {
|
||||
/* Setting opaque here is just an optimization, allow to fail */
|
||||
ovl_set_opaque(dentry, newdentry);
|
||||
}
|
||||
@@ -542,8 +545,10 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode,
|
||||
goto out_cleanup;
|
||||
}
|
||||
err = ovl_instantiate(dentry, inode, newdentry, hardlink);
|
||||
if (err)
|
||||
goto out_cleanup;
|
||||
if (err) {
|
||||
ovl_cleanup(udir, newdentry);
|
||||
dput(newdentry);
|
||||
}
|
||||
out_dput:
|
||||
dput(upper);
|
||||
out_unlock:
|
||||
@@ -1043,6 +1048,7 @@ static bool ovl_need_absolute_redirect(struct dentry *dentry, bool samedir)
|
||||
static int ovl_set_redirect(struct dentry *dentry, bool samedir)
|
||||
{
|
||||
int err;
|
||||
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
|
||||
const char *redirect = ovl_dentry_get_redirect(dentry);
|
||||
bool absolute_redirect = ovl_need_absolute_redirect(dentry, samedir);
|
||||
|
||||
@@ -1053,7 +1059,7 @@ static int ovl_set_redirect(struct dentry *dentry, bool samedir)
|
||||
if (IS_ERR(redirect))
|
||||
return PTR_ERR(redirect);
|
||||
|
||||
err = ovl_check_setxattr(dentry, ovl_dentry_upper(dentry),
|
||||
err = ovl_check_setxattr(ofs, ovl_dentry_upper(dentry),
|
||||
OVL_XATTR_REDIRECT,
|
||||
redirect, strlen(redirect), -EXDEV);
|
||||
if (!err) {
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <linux/fiemap.h>
|
||||
#include <linux/fileattr.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/namei.h>
|
||||
#include "overlayfs.h"
|
||||
|
||||
|
||||
@@ -33,12 +34,6 @@ int ovl_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
|
||||
goto out;
|
||||
|
||||
if (attr->ia_valid & ATTR_SIZE) {
|
||||
struct inode *realinode = d_inode(ovl_dentry_real(dentry));
|
||||
|
||||
err = -ETXTBSY;
|
||||
if (atomic_read(&realinode->i_writecount) < 0)
|
||||
goto out_drop_write;
|
||||
|
||||
/* Truncate should trigger data copy up as well */
|
||||
full_copy_up = true;
|
||||
}
|
||||
@@ -162,7 +157,8 @@ int ovl_getattr(struct user_namespace *mnt_userns, const struct path *path,
|
||||
enum ovl_path_type type;
|
||||
struct path realpath;
|
||||
const struct cred *old_cred;
|
||||
bool is_dir = S_ISDIR(dentry->d_inode->i_mode);
|
||||
struct inode *inode = d_inode(dentry);
|
||||
bool is_dir = S_ISDIR(inode->i_mode);
|
||||
int fsid = 0;
|
||||
int err;
|
||||
bool metacopy_blocks = false;
|
||||
@@ -175,6 +171,9 @@ int ovl_getattr(struct user_namespace *mnt_userns, const struct path *path,
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
/* Report the effective immutable/append-only STATX flags */
|
||||
generic_fill_statx_attr(inode, stat);
|
||||
|
||||
/*
|
||||
* For non-dir or same fs, we use st_ino of the copy up origin.
|
||||
* This guaranties constant st_dev/st_ino across copy up.
|
||||
@@ -448,7 +447,7 @@ ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size)
|
||||
return res;
|
||||
}
|
||||
|
||||
struct posix_acl *ovl_get_acl(struct inode *inode, int type)
|
||||
struct posix_acl *ovl_get_acl(struct inode *inode, int type, bool rcu)
|
||||
{
|
||||
struct inode *realinode = ovl_inode_real(inode);
|
||||
const struct cred *old_cred;
|
||||
@@ -457,6 +456,9 @@ struct posix_acl *ovl_get_acl(struct inode *inode, int type)
|
||||
if (!IS_ENABLED(CONFIG_FS_POSIX_ACL) || !IS_POSIXACL(realinode))
|
||||
return NULL;
|
||||
|
||||
if (rcu)
|
||||
return get_cached_acl_rcu(realinode, type);
|
||||
|
||||
old_cred = ovl_override_creds(inode->i_sb);
|
||||
acl = get_acl(realinode, type);
|
||||
revert_creds(old_cred);
|
||||
@@ -503,16 +505,14 @@ static int ovl_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
|
||||
* Introducing security_inode_fileattr_get/set() hooks would solve this issue
|
||||
* properly.
|
||||
*/
|
||||
static int ovl_security_fileattr(struct dentry *dentry, struct fileattr *fa,
|
||||
static int ovl_security_fileattr(struct path *realpath, struct fileattr *fa,
|
||||
bool set)
|
||||
{
|
||||
struct path realpath;
|
||||
struct file *file;
|
||||
unsigned int cmd;
|
||||
int err;
|
||||
|
||||
ovl_path_real(dentry, &realpath);
|
||||
file = dentry_open(&realpath, O_RDONLY, current_cred());
|
||||
file = dentry_open(realpath, O_RDONLY, current_cred());
|
||||
if (IS_ERR(file))
|
||||
return PTR_ERR(file);
|
||||
|
||||
@@ -527,12 +527,24 @@ static int ovl_security_fileattr(struct dentry *dentry, struct fileattr *fa,
|
||||
return err;
|
||||
}
|
||||
|
||||
int ovl_real_fileattr_set(struct path *realpath, struct fileattr *fa)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = ovl_security_fileattr(realpath, fa, true);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return vfs_fileattr_set(&init_user_ns, realpath->dentry, fa);
|
||||
}
|
||||
|
||||
int ovl_fileattr_set(struct user_namespace *mnt_userns,
|
||||
struct dentry *dentry, struct fileattr *fa)
|
||||
{
|
||||
struct inode *inode = d_inode(dentry);
|
||||
struct dentry *upperdentry;
|
||||
struct path upperpath;
|
||||
const struct cred *old_cred;
|
||||
unsigned int flags;
|
||||
int err;
|
||||
|
||||
err = ovl_want_write(dentry);
|
||||
@@ -541,31 +553,78 @@ int ovl_fileattr_set(struct user_namespace *mnt_userns,
|
||||
|
||||
err = ovl_copy_up(dentry);
|
||||
if (!err) {
|
||||
upperdentry = ovl_dentry_upper(dentry);
|
||||
ovl_path_real(dentry, &upperpath);
|
||||
|
||||
old_cred = ovl_override_creds(inode->i_sb);
|
||||
err = ovl_security_fileattr(dentry, fa, true);
|
||||
/*
|
||||
* Store immutable/append-only flags in xattr and clear them
|
||||
* in upper fileattr (in case they were set by older kernel)
|
||||
* so children of "ovl-immutable" directories lower aliases of
|
||||
* "ovl-immutable" hardlinks could be copied up.
|
||||
* Clear xattr when flags are cleared.
|
||||
*/
|
||||
err = ovl_set_protattr(inode, upperpath.dentry, fa);
|
||||
if (!err)
|
||||
err = vfs_fileattr_set(&init_user_ns, upperdentry, fa);
|
||||
err = ovl_real_fileattr_set(&upperpath, fa);
|
||||
revert_creds(old_cred);
|
||||
ovl_copyflags(ovl_inode_real(inode), inode);
|
||||
|
||||
/*
|
||||
* Merge real inode flags with inode flags read from
|
||||
* overlay.protattr xattr
|
||||
*/
|
||||
flags = ovl_inode_real(inode)->i_flags & OVL_COPY_I_FLAGS_MASK;
|
||||
|
||||
BUILD_BUG_ON(OVL_PROT_I_FLAGS_MASK & ~OVL_COPY_I_FLAGS_MASK);
|
||||
flags |= inode->i_flags & OVL_PROT_I_FLAGS_MASK;
|
||||
inode_set_flags(inode, flags, OVL_COPY_I_FLAGS_MASK);
|
||||
|
||||
/* Update ctime */
|
||||
ovl_copyattr(ovl_inode_real(inode), inode);
|
||||
}
|
||||
ovl_drop_write(dentry);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Convert inode protection flags to fileattr flags */
|
||||
static void ovl_fileattr_prot_flags(struct inode *inode, struct fileattr *fa)
|
||||
{
|
||||
BUILD_BUG_ON(OVL_PROT_FS_FLAGS_MASK & ~FS_COMMON_FL);
|
||||
BUILD_BUG_ON(OVL_PROT_FSX_FLAGS_MASK & ~FS_XFLAG_COMMON);
|
||||
|
||||
if (inode->i_flags & S_APPEND) {
|
||||
fa->flags |= FS_APPEND_FL;
|
||||
fa->fsx_xflags |= FS_XFLAG_APPEND;
|
||||
}
|
||||
if (inode->i_flags & S_IMMUTABLE) {
|
||||
fa->flags |= FS_IMMUTABLE_FL;
|
||||
fa->fsx_xflags |= FS_XFLAG_IMMUTABLE;
|
||||
}
|
||||
}
|
||||
|
||||
int ovl_real_fileattr_get(struct path *realpath, struct fileattr *fa)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = ovl_security_fileattr(realpath, fa, false);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return vfs_fileattr_get(realpath->dentry, fa);
|
||||
}
|
||||
|
||||
int ovl_fileattr_get(struct dentry *dentry, struct fileattr *fa)
|
||||
{
|
||||
struct inode *inode = d_inode(dentry);
|
||||
struct dentry *realdentry = ovl_dentry_real(dentry);
|
||||
struct path realpath;
|
||||
const struct cred *old_cred;
|
||||
int err;
|
||||
|
||||
ovl_path_real(dentry, &realpath);
|
||||
|
||||
old_cred = ovl_override_creds(inode->i_sb);
|
||||
err = ovl_security_fileattr(dentry, fa, false);
|
||||
if (!err)
|
||||
err = vfs_fileattr_get(realdentry, fa);
|
||||
err = ovl_real_fileattr_get(&realpath, fa);
|
||||
ovl_fileattr_prot_flags(inode, fa);
|
||||
revert_creds(old_cred);
|
||||
|
||||
return err;
|
||||
@@ -1118,6 +1177,10 @@ struct inode *ovl_get_inode(struct super_block *sb,
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for immutable/append-only inode flags in xattr */
|
||||
if (upperdentry)
|
||||
ovl_check_protattr(inode, upperdentry);
|
||||
|
||||
if (inode->i_state & I_NEW)
|
||||
unlock_new_inode(inode);
|
||||
out:
|
||||
|
||||
@@ -392,7 +392,7 @@ invalid:
|
||||
upperdentry, d_inode(upperdentry)->i_mode & S_IFMT,
|
||||
d_inode(origin)->i_mode & S_IFMT);
|
||||
dput(origin);
|
||||
return -EIO;
|
||||
return -ESTALE;
|
||||
}
|
||||
|
||||
static int ovl_check_origin(struct ovl_fs *ofs, struct dentry *upperdentry,
|
||||
@@ -811,7 +811,7 @@ static int ovl_fix_origin(struct ovl_fs *ofs, struct dentry *dentry,
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = ovl_set_origin(ofs, dentry, lower, upper);
|
||||
err = ovl_set_origin(ofs, lower, upper);
|
||||
if (!err)
|
||||
err = ovl_set_impure(dentry->d_parent, upper->d_parent);
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ enum ovl_xattr {
|
||||
OVL_XATTR_NLINK,
|
||||
OVL_XATTR_UPPER,
|
||||
OVL_XATTR_METACOPY,
|
||||
OVL_XATTR_PROTATTR,
|
||||
};
|
||||
|
||||
enum ovl_inode_flag {
|
||||
@@ -262,6 +263,18 @@ static inline bool ovl_open_flags_need_copy_up(int flags)
|
||||
return ((OPEN_FMODE(flags) & FMODE_WRITE) || (flags & O_TRUNC));
|
||||
}
|
||||
|
||||
static inline bool ovl_allow_offline_changes(struct ovl_fs *ofs)
|
||||
{
|
||||
/*
|
||||
* To avoid regressions in existing setups with overlay lower offline
|
||||
* changes, we allow lower changes only if none of the new features
|
||||
* are used.
|
||||
*/
|
||||
return (!ofs->config.index && !ofs->config.metacopy &&
|
||||
!ofs->config.redirect_dir && ofs->config.xino != OVL_XINO_ON);
|
||||
}
|
||||
|
||||
|
||||
/* util.c */
|
||||
int ovl_want_write(struct dentry *dentry);
|
||||
void ovl_drop_write(struct dentry *dentry);
|
||||
@@ -320,7 +333,7 @@ bool ovl_already_copied_up(struct dentry *dentry, int flags);
|
||||
bool ovl_check_origin_xattr(struct ovl_fs *ofs, struct dentry *dentry);
|
||||
bool ovl_check_dir_xattr(struct super_block *sb, struct dentry *dentry,
|
||||
enum ovl_xattr ox);
|
||||
int ovl_check_setxattr(struct dentry *dentry, struct dentry *upperdentry,
|
||||
int ovl_check_setxattr(struct ovl_fs *ofs, struct dentry *upperdentry,
|
||||
enum ovl_xattr ox, const void *value, size_t size,
|
||||
int xerr);
|
||||
int ovl_set_impure(struct dentry *dentry, struct dentry *upperdentry);
|
||||
@@ -485,7 +498,7 @@ int ovl_xattr_set(struct dentry *dentry, struct inode *inode, const char *name,
|
||||
int ovl_xattr_get(struct dentry *dentry, struct inode *inode, const char *name,
|
||||
void *value, size_t size);
|
||||
ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size);
|
||||
struct posix_acl *ovl_get_acl(struct inode *inode, int type);
|
||||
struct posix_acl *ovl_get_acl(struct inode *inode, int type, bool rcu);
|
||||
int ovl_update_time(struct inode *inode, struct timespec64 *ts, int flags);
|
||||
bool ovl_is_private_xattr(struct super_block *sb, const char *name);
|
||||
|
||||
@@ -518,9 +531,28 @@ static inline void ovl_copyattr(struct inode *from, struct inode *to)
|
||||
i_size_write(to, i_size_read(from));
|
||||
}
|
||||
|
||||
/* vfs inode flags copied from real to ovl inode */
|
||||
#define OVL_COPY_I_FLAGS_MASK (S_SYNC | S_NOATIME | S_APPEND | S_IMMUTABLE)
|
||||
/* vfs inode flags read from overlay.protattr xattr to ovl inode */
|
||||
#define OVL_PROT_I_FLAGS_MASK (S_APPEND | S_IMMUTABLE)
|
||||
|
||||
/*
|
||||
* fileattr flags copied from lower to upper inode on copy up.
|
||||
* We cannot copy up immutable/append-only flags, because that would prevent
|
||||
* linking temp inode to upper dir, so we store them in xattr instead.
|
||||
*/
|
||||
#define OVL_COPY_FS_FLAGS_MASK (FS_SYNC_FL | FS_NOATIME_FL)
|
||||
#define OVL_COPY_FSX_FLAGS_MASK (FS_XFLAG_SYNC | FS_XFLAG_NOATIME)
|
||||
#define OVL_PROT_FS_FLAGS_MASK (FS_APPEND_FL | FS_IMMUTABLE_FL)
|
||||
#define OVL_PROT_FSX_FLAGS_MASK (FS_XFLAG_APPEND | FS_XFLAG_IMMUTABLE)
|
||||
|
||||
void ovl_check_protattr(struct inode *inode, struct dentry *upper);
|
||||
int ovl_set_protattr(struct inode *inode, struct dentry *upper,
|
||||
struct fileattr *fa);
|
||||
|
||||
static inline void ovl_copyflags(struct inode *from, struct inode *to)
|
||||
{
|
||||
unsigned int mask = S_SYNC | S_IMMUTABLE | S_APPEND | S_NOATIME;
|
||||
unsigned int mask = OVL_COPY_I_FLAGS_MASK;
|
||||
|
||||
inode_set_flags(to, from->i_flags & mask, mask);
|
||||
}
|
||||
@@ -548,6 +580,8 @@ struct dentry *ovl_create_temp(struct dentry *workdir, struct ovl_cattr *attr);
|
||||
extern const struct file_operations ovl_file_operations;
|
||||
int __init ovl_aio_request_cache_init(void);
|
||||
void ovl_aio_request_cache_destroy(void);
|
||||
int ovl_real_fileattr_get(struct path *realpath, struct fileattr *fa);
|
||||
int ovl_real_fileattr_set(struct path *realpath, struct fileattr *fa);
|
||||
int ovl_fileattr_get(struct dentry *dentry, struct fileattr *fa);
|
||||
int ovl_fileattr_set(struct user_namespace *mnt_userns,
|
||||
struct dentry *dentry, struct fileattr *fa);
|
||||
@@ -561,8 +595,8 @@ int ovl_copy_xattr(struct super_block *sb, struct dentry *old,
|
||||
int ovl_set_attr(struct dentry *upper, struct kstat *stat);
|
||||
struct ovl_fh *ovl_encode_real_fh(struct ovl_fs *ofs, struct dentry *real,
|
||||
bool is_upper);
|
||||
int ovl_set_origin(struct ovl_fs *ofs, struct dentry *dentry,
|
||||
struct dentry *lower, struct dentry *upper);
|
||||
int ovl_set_origin(struct ovl_fs *ofs, struct dentry *lower,
|
||||
struct dentry *upper);
|
||||
|
||||
/* export.c */
|
||||
extern const struct export_operations ovl_export_operations;
|
||||
|
||||
@@ -1599,9 +1599,7 @@ static bool ovl_lower_uuid_ok(struct ovl_fs *ofs, const uuid_t *uuid)
|
||||
* user opted-in to one of the new features that require following the
|
||||
* lower inode of non-dir upper.
|
||||
*/
|
||||
if (!ofs->config.index && !ofs->config.metacopy &&
|
||||
ofs->config.xino != OVL_XINO_ON &&
|
||||
uuid_is_null(uuid))
|
||||
if (ovl_allow_offline_changes(ofs) && uuid_is_null(uuid))
|
||||
return false;
|
||||
|
||||
for (i = 0; i < ofs->numfs; i++) {
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <linux/cred.h>
|
||||
#include <linux/xattr.h>
|
||||
#include <linux/exportfs.h>
|
||||
#include <linux/fileattr.h>
|
||||
#include <linux/uuid.h>
|
||||
#include <linux/namei.h>
|
||||
#include <linux/ratelimit.h>
|
||||
@@ -585,6 +586,7 @@ bool ovl_check_dir_xattr(struct super_block *sb, struct dentry *dentry,
|
||||
#define OVL_XATTR_NLINK_POSTFIX "nlink"
|
||||
#define OVL_XATTR_UPPER_POSTFIX "upper"
|
||||
#define OVL_XATTR_METACOPY_POSTFIX "metacopy"
|
||||
#define OVL_XATTR_PROTATTR_POSTFIX "protattr"
|
||||
|
||||
#define OVL_XATTR_TAB_ENTRY(x) \
|
||||
[x] = { [false] = OVL_XATTR_TRUSTED_PREFIX x ## _POSTFIX, \
|
||||
@@ -598,14 +600,14 @@ const char *const ovl_xattr_table[][2] = {
|
||||
OVL_XATTR_TAB_ENTRY(OVL_XATTR_NLINK),
|
||||
OVL_XATTR_TAB_ENTRY(OVL_XATTR_UPPER),
|
||||
OVL_XATTR_TAB_ENTRY(OVL_XATTR_METACOPY),
|
||||
OVL_XATTR_TAB_ENTRY(OVL_XATTR_PROTATTR),
|
||||
};
|
||||
|
||||
int ovl_check_setxattr(struct dentry *dentry, struct dentry *upperdentry,
|
||||
int ovl_check_setxattr(struct ovl_fs *ofs, struct dentry *upperdentry,
|
||||
enum ovl_xattr ox, const void *value, size_t size,
|
||||
int xerr)
|
||||
{
|
||||
int err;
|
||||
struct ovl_fs *ofs = dentry->d_sb->s_fs_info;
|
||||
|
||||
if (ofs->noxattr)
|
||||
return xerr;
|
||||
@@ -623,6 +625,7 @@ int ovl_check_setxattr(struct dentry *dentry, struct dentry *upperdentry,
|
||||
|
||||
int ovl_set_impure(struct dentry *dentry, struct dentry *upperdentry)
|
||||
{
|
||||
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
|
||||
int err;
|
||||
|
||||
if (ovl_test_flag(OVL_IMPURE, d_inode(dentry)))
|
||||
@@ -632,14 +635,95 @@ int ovl_set_impure(struct dentry *dentry, struct dentry *upperdentry)
|
||||
* Do not fail when upper doesn't support xattrs.
|
||||
* Upper inodes won't have origin nor redirect xattr anyway.
|
||||
*/
|
||||
err = ovl_check_setxattr(dentry, upperdentry, OVL_XATTR_IMPURE,
|
||||
"y", 1, 0);
|
||||
err = ovl_check_setxattr(ofs, upperdentry, OVL_XATTR_IMPURE, "y", 1, 0);
|
||||
if (!err)
|
||||
ovl_set_flag(OVL_IMPURE, d_inode(dentry));
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
#define OVL_PROTATTR_MAX 32 /* Reserved for future flags */
|
||||
|
||||
void ovl_check_protattr(struct inode *inode, struct dentry *upper)
|
||||
{
|
||||
struct ovl_fs *ofs = OVL_FS(inode->i_sb);
|
||||
u32 iflags = inode->i_flags & OVL_PROT_I_FLAGS_MASK;
|
||||
char buf[OVL_PROTATTR_MAX+1];
|
||||
int res, n;
|
||||
|
||||
res = ovl_do_getxattr(ofs, upper, OVL_XATTR_PROTATTR, buf,
|
||||
OVL_PROTATTR_MAX);
|
||||
if (res < 0)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Initialize inode flags from overlay.protattr xattr and upper inode
|
||||
* flags. If upper inode has those fileattr flags set (i.e. from old
|
||||
* kernel), we do not clear them on ovl_get_inode(), but we will clear
|
||||
* them on next fileattr_set().
|
||||
*/
|
||||
for (n = 0; n < res; n++) {
|
||||
if (buf[n] == 'a')
|
||||
iflags |= S_APPEND;
|
||||
else if (buf[n] == 'i')
|
||||
iflags |= S_IMMUTABLE;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if (!res || n < res) {
|
||||
pr_warn_ratelimited("incompatible overlay.protattr format (%pd2, len=%d)\n",
|
||||
upper, res);
|
||||
} else {
|
||||
inode_set_flags(inode, iflags, OVL_PROT_I_FLAGS_MASK);
|
||||
}
|
||||
}
|
||||
|
||||
int ovl_set_protattr(struct inode *inode, struct dentry *upper,
|
||||
struct fileattr *fa)
|
||||
{
|
||||
struct ovl_fs *ofs = OVL_FS(inode->i_sb);
|
||||
char buf[OVL_PROTATTR_MAX];
|
||||
int len = 0, err = 0;
|
||||
u32 iflags = 0;
|
||||
|
||||
BUILD_BUG_ON(HWEIGHT32(OVL_PROT_FS_FLAGS_MASK) > OVL_PROTATTR_MAX);
|
||||
|
||||
if (fa->flags & FS_APPEND_FL) {
|
||||
buf[len++] = 'a';
|
||||
iflags |= S_APPEND;
|
||||
}
|
||||
if (fa->flags & FS_IMMUTABLE_FL) {
|
||||
buf[len++] = 'i';
|
||||
iflags |= S_IMMUTABLE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Do not allow to set protection flags when upper doesn't support
|
||||
* xattrs, because we do not set those fileattr flags on upper inode.
|
||||
* Remove xattr if it exist and all protection flags are cleared.
|
||||
*/
|
||||
if (len) {
|
||||
err = ovl_check_setxattr(ofs, upper, OVL_XATTR_PROTATTR,
|
||||
buf, len, -EPERM);
|
||||
} else if (inode->i_flags & OVL_PROT_I_FLAGS_MASK) {
|
||||
err = ovl_do_removexattr(ofs, upper, OVL_XATTR_PROTATTR);
|
||||
if (err == -EOPNOTSUPP || err == -ENODATA)
|
||||
err = 0;
|
||||
}
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
inode_set_flags(inode, iflags, OVL_PROT_I_FLAGS_MASK);
|
||||
|
||||
/* Mask out the fileattr flags that should not be set in upper inode */
|
||||
fa->flags &= ~OVL_PROT_FS_FLAGS_MASK;
|
||||
fa->fsx_xflags &= ~OVL_PROT_FSX_FLAGS_MASK;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Caller must hold a reference to inode to prevent it from being freed while
|
||||
* it is marked inuse.
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <linux/xattr.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/user_namespace.h>
|
||||
#include <linux/namei.h>
|
||||
|
||||
static struct posix_acl **acl_by_type(struct inode *inode, int type)
|
||||
{
|
||||
@@ -56,7 +57,17 @@ EXPORT_SYMBOL(get_cached_acl);
|
||||
|
||||
struct posix_acl *get_cached_acl_rcu(struct inode *inode, int type)
|
||||
{
|
||||
return rcu_dereference(*acl_by_type(inode, type));
|
||||
struct posix_acl *acl = rcu_dereference(*acl_by_type(inode, type));
|
||||
|
||||
if (acl == ACL_DONT_CACHE) {
|
||||
struct posix_acl *ret;
|
||||
|
||||
ret = inode->i_op->get_acl(inode, type, LOOKUP_RCU);
|
||||
if (!IS_ERR(ret))
|
||||
acl = ret;
|
||||
}
|
||||
|
||||
return acl;
|
||||
}
|
||||
EXPORT_SYMBOL(get_cached_acl_rcu);
|
||||
|
||||
@@ -138,7 +149,7 @@ struct posix_acl *get_acl(struct inode *inode, int type)
|
||||
set_cached_acl(inode, type, NULL);
|
||||
return NULL;
|
||||
}
|
||||
acl = inode->i_op->get_acl(inode, type);
|
||||
acl = inode->i_op->get_acl(inode, type, false);
|
||||
|
||||
if (IS_ERR(acl)) {
|
||||
/*
|
||||
|
||||
@@ -48,7 +48,7 @@ static inline int reiserfs_acl_count(size_t size)
|
||||
}
|
||||
|
||||
#ifdef CONFIG_REISERFS_FS_POSIX_ACL
|
||||
struct posix_acl *reiserfs_get_acl(struct inode *inode, int type);
|
||||
struct posix_acl *reiserfs_get_acl(struct inode *inode, int type, bool rcu);
|
||||
int reiserfs_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
|
||||
struct posix_acl *acl, int type);
|
||||
int reiserfs_acl_chmod(struct inode *inode);
|
||||
|
||||
@@ -190,13 +190,16 @@ fail:
|
||||
* inode->i_mutex: down
|
||||
* BKL held [before 2.5.x]
|
||||
*/
|
||||
struct posix_acl *reiserfs_get_acl(struct inode *inode, int type)
|
||||
struct posix_acl *reiserfs_get_acl(struct inode *inode, int type, bool rcu)
|
||||
{
|
||||
char *name, *value;
|
||||
struct posix_acl *acl;
|
||||
int size;
|
||||
int retval;
|
||||
|
||||
if (rcu)
|
||||
return ERR_PTR(-ECHILD);
|
||||
|
||||
switch (type) {
|
||||
case ACL_TYPE_ACCESS:
|
||||
name = XATTR_NAME_POSIX_ACL_ACCESS;
|
||||
|
||||
18
fs/stat.c
18
fs/stat.c
@@ -59,6 +59,24 @@ void generic_fillattr(struct user_namespace *mnt_userns, struct inode *inode,
|
||||
}
|
||||
EXPORT_SYMBOL(generic_fillattr);
|
||||
|
||||
/**
|
||||
* generic_fill_statx_attr - Fill in the statx attributes from the inode flags
|
||||
* @inode: Inode to use as the source
|
||||
* @stat: Where to fill in the attribute flags
|
||||
*
|
||||
* Fill in the STATX_ATTR_* flags in the kstat structure for properties of the
|
||||
* inode that are published on i_flags and enforced by the VFS.
|
||||
*/
|
||||
void generic_fill_statx_attr(struct inode *inode, struct kstat *stat)
|
||||
{
|
||||
if (inode->i_flags & S_IMMUTABLE)
|
||||
stat->attributes |= STATX_ATTR_IMMUTABLE;
|
||||
if (inode->i_flags & S_APPEND)
|
||||
stat->attributes |= STATX_ATTR_APPEND;
|
||||
stat->attributes_mask |= KSTAT_ATTR_VFS_FLAGS;
|
||||
}
|
||||
EXPORT_SYMBOL(generic_fill_statx_attr);
|
||||
|
||||
/**
|
||||
* vfs_getattr_nosec - getattr without security checks
|
||||
* @path: file to get attributes from
|
||||
|
||||
@@ -125,7 +125,7 @@ xfs_acl_to_disk(struct xfs_acl *aclp, const struct posix_acl *acl)
|
||||
}
|
||||
|
||||
struct posix_acl *
|
||||
xfs_get_acl(struct inode *inode, int type)
|
||||
xfs_get_acl(struct inode *inode, int type, bool rcu)
|
||||
{
|
||||
struct xfs_inode *ip = XFS_I(inode);
|
||||
struct xfs_mount *mp = ip->i_mount;
|
||||
@@ -137,6 +137,9 @@ xfs_get_acl(struct inode *inode, int type)
|
||||
};
|
||||
int error;
|
||||
|
||||
if (rcu)
|
||||
return ERR_PTR(-ECHILD);
|
||||
|
||||
trace_xfs_get_acl(ip);
|
||||
|
||||
switch (type) {
|
||||
|
||||
@@ -10,13 +10,13 @@ struct inode;
|
||||
struct posix_acl;
|
||||
|
||||
#ifdef CONFIG_XFS_POSIX_ACL
|
||||
extern struct posix_acl *xfs_get_acl(struct inode *inode, int type);
|
||||
extern struct posix_acl *xfs_get_acl(struct inode *inode, int type, bool rcu);
|
||||
extern int xfs_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
|
||||
struct posix_acl *acl, int type);
|
||||
extern int __xfs_set_acl(struct inode *inode, struct posix_acl *acl, int type);
|
||||
void xfs_forget_acl(struct inode *inode, const char *name);
|
||||
#else
|
||||
static inline struct posix_acl *xfs_get_acl(struct inode *inode, int type)
|
||||
static inline struct posix_acl *xfs_get_acl(struct inode *inode, int type, bool rcu)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -588,6 +588,11 @@ static inline void mapping_allow_writable(struct address_space *mapping)
|
||||
|
||||
struct posix_acl;
|
||||
#define ACL_NOT_CACHED ((void *)(-1))
|
||||
/*
|
||||
* ACL_DONT_CACHE is for stacked filesystems, that rely on underlying fs to
|
||||
* cache the ACL. This also means that ->get_acl() can be called in RCU mode
|
||||
* with the LOOKUP_RCU flag.
|
||||
*/
|
||||
#define ACL_DONT_CACHE ((void *)(-3))
|
||||
|
||||
static inline struct posix_acl *
|
||||
@@ -2109,7 +2114,7 @@ struct inode_operations {
|
||||
struct dentry * (*lookup) (struct inode *,struct dentry *, unsigned int);
|
||||
const char * (*get_link) (struct dentry *, struct inode *, struct delayed_call *);
|
||||
int (*permission) (struct user_namespace *, struct inode *, int);
|
||||
struct posix_acl * (*get_acl)(struct inode *, int);
|
||||
struct posix_acl * (*get_acl)(struct inode *, int, bool);
|
||||
|
||||
int (*readlink) (struct dentry *, char __user *,int);
|
||||
|
||||
@@ -3314,6 +3319,7 @@ extern int page_symlink(struct inode *inode, const char *symname, int len);
|
||||
extern const struct inode_operations page_symlink_inode_operations;
|
||||
extern void kfree_link(void *);
|
||||
void generic_fillattr(struct user_namespace *, struct inode *, struct kstat *);
|
||||
void generic_fill_statx_attr(struct inode *inode, struct kstat *stat);
|
||||
extern int vfs_getattr_nosec(const struct path *, struct kstat *, u32, unsigned int);
|
||||
extern int vfs_getattr(const struct path *, struct kstat *, u32, unsigned int);
|
||||
void __inode_add_bytes(struct inode *inode, loff_t bytes);
|
||||
|
||||
@@ -72,6 +72,8 @@ extern struct posix_acl *get_posix_acl(struct inode *, int);
|
||||
extern int set_posix_acl(struct user_namespace *, struct inode *, int,
|
||||
struct posix_acl *);
|
||||
|
||||
struct posix_acl *get_cached_acl_rcu(struct inode *inode, int type);
|
||||
|
||||
#ifdef CONFIG_FS_POSIX_ACL
|
||||
int posix_acl_chmod(struct user_namespace *, struct inode *, umode_t);
|
||||
extern int posix_acl_create(struct inode *, umode_t *, struct posix_acl **,
|
||||
@@ -84,7 +86,6 @@ extern int simple_set_acl(struct user_namespace *, struct inode *,
|
||||
extern int simple_acl_create(struct inode *, struct inode *);
|
||||
|
||||
struct posix_acl *get_cached_acl(struct inode *inode, int type);
|
||||
struct posix_acl *get_cached_acl_rcu(struct inode *inode, int type);
|
||||
void set_cached_acl(struct inode *inode, int type, struct posix_acl *acl);
|
||||
void forget_cached_acl(struct inode *inode, int type);
|
||||
void forget_all_cached_acls(struct inode *inode);
|
||||
|
||||
@@ -34,6 +34,10 @@ struct kstat {
|
||||
STATX_ATTR_ENCRYPTED | \
|
||||
STATX_ATTR_VERITY \
|
||||
)/* Attrs corresponding to FS_*_FL flags */
|
||||
#define KSTAT_ATTR_VFS_FLAGS \
|
||||
(STATX_ATTR_IMMUTABLE | \
|
||||
STATX_ATTR_APPEND \
|
||||
) /* Attrs corresponding to S_* flags that are enforced by the VFS */
|
||||
u64 ino;
|
||||
dev_t dev;
|
||||
dev_t rdev;
|
||||
|
||||
Reference in New Issue
Block a user