mirror of
https://github.com/Dasharo/linux.git
synced 2026-03-06 15:25:10 -08:00
Merge tag 'ceph-for-6.6-rc1' of https://github.com/ceph/ceph-client
Pull ceph updates from Ilya Dryomov: "Mixed with some fixes and cleanups, this brings in reasonably complete fscrypt support to CephFS! The list of things which don't work with encryption should be fairly short, mostly around the edges: fallocate (not supported well in CephFS to begin with), copy_file_range (requires re-encryption), non-default striping patterns. This was a multi-year effort principally by Jeff Layton with assistance from Xiubo Li, Luís Henriques and others, including several dependant changes in the MDS, netfs helper library and fscrypt framework itself" * tag 'ceph-for-6.6-rc1' of https://github.com/ceph/ceph-client: (53 commits) ceph: make num_fwd and num_retry to __u32 ceph: make members in struct ceph_mds_request_args_ext a union rbd: use list_for_each_entry() helper libceph: do not include crypto/algapi.h ceph: switch ceph_lookup/atomic_open() to use new fscrypt helper ceph: fix updating i_truncate_pagecache_size for fscrypt ceph: wait for OSD requests' callbacks to finish when unmounting ceph: drop messages from MDS when unmounting ceph: update documentation regarding snapshot naming limitations ceph: prevent snapshot creation in encrypted locked directories ceph: add support for encrypted snapshot names ceph: invalidate pages when doing direct/sync writes ceph: plumb in decryption during reads ceph: add encryption support to writepage and writepages ceph: add read/modify/write to ceph_sync_write ceph: align data in pages in ceph_sync_write ceph: don't use special DIO path for encrypted inodes ceph: add truncate size handling support for fscrypt ceph: add object version support for sync read libceph: allow ceph_osdc_new_request to accept a multi-op read ...
This commit is contained in:
@@ -57,6 +57,16 @@ a snapshot on any subdirectory (and its nested contents) in the
|
||||
system. Snapshot creation and deletion are as simple as 'mkdir
|
||||
.snap/foo' and 'rmdir .snap/foo'.
|
||||
|
||||
Snapshot names have two limitations:
|
||||
|
||||
* They can not start with an underscore ('_'), as these names are reserved
|
||||
for internal usage by the MDS.
|
||||
* They can not exceed 240 characters in size. This is because the MDS makes
|
||||
use of long snapshot names internally, which follow the format:
|
||||
`_<SNAPSHOT-NAME>_<INODE-NUMBER>`. Since filenames in general can't have
|
||||
more than 255 characters, and `<node-id>` takes 13 characters, the long
|
||||
snapshot names can take as much as 255 - 1 - 1 - 13 = 240.
|
||||
|
||||
Ceph also provides some recursive accounting on directories for nested
|
||||
files and bytes. That is, a 'getfattr -d foo' on any directory in the
|
||||
system will reveal the total number of nested regular files and
|
||||
|
||||
@@ -7199,7 +7199,6 @@ static void rbd_dev_remove_parent(struct rbd_device *rbd_dev)
|
||||
static ssize_t do_rbd_remove(const char *buf, size_t count)
|
||||
{
|
||||
struct rbd_device *rbd_dev = NULL;
|
||||
struct list_head *tmp;
|
||||
int dev_id;
|
||||
char opt_buf[6];
|
||||
bool force = false;
|
||||
@@ -7226,8 +7225,7 @@ static ssize_t do_rbd_remove(const char *buf, size_t count)
|
||||
|
||||
ret = -ENOENT;
|
||||
spin_lock(&rbd_dev_list_lock);
|
||||
list_for_each(tmp, &rbd_dev_list) {
|
||||
rbd_dev = list_entry(tmp, struct rbd_device, node);
|
||||
list_for_each_entry(rbd_dev, &rbd_dev_list, node) {
|
||||
if (rbd_dev->dev_id == dev_id) {
|
||||
ret = 0;
|
||||
break;
|
||||
|
||||
@@ -12,3 +12,4 @@ ceph-y := super.o inode.o dir.o file.o locks.o addr.o ioctl.o \
|
||||
|
||||
ceph-$(CONFIG_CEPH_FSCACHE) += cache.o
|
||||
ceph-$(CONFIG_CEPH_FS_POSIX_ACL) += acl.o
|
||||
ceph-$(CONFIG_FS_ENCRYPTION) += crypto.o
|
||||
|
||||
@@ -140,7 +140,7 @@ int ceph_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
|
||||
newattrs.ia_ctime = current_time(inode);
|
||||
newattrs.ia_mode = new_mode;
|
||||
newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
|
||||
ret = __ceph_setattr(inode, &newattrs);
|
||||
ret = __ceph_setattr(inode, &newattrs, NULL);
|
||||
if (ret)
|
||||
goto out_free;
|
||||
}
|
||||
@@ -151,7 +151,7 @@ int ceph_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
|
||||
newattrs.ia_ctime = old_ctime;
|
||||
newattrs.ia_mode = old_mode;
|
||||
newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
|
||||
__ceph_setattr(inode, &newattrs);
|
||||
__ceph_setattr(inode, &newattrs, NULL);
|
||||
}
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
198
fs/ceph/addr.c
198
fs/ceph/addr.c
@@ -18,6 +18,7 @@
|
||||
#include "mds_client.h"
|
||||
#include "cache.h"
|
||||
#include "metric.h"
|
||||
#include "crypto.h"
|
||||
#include <linux/ceph/osd_client.h>
|
||||
#include <linux/ceph/striper.h>
|
||||
|
||||
@@ -242,11 +243,13 @@ static bool ceph_netfs_clamp_length(struct netfs_io_subrequest *subreq)
|
||||
|
||||
static void finish_netfs_read(struct ceph_osd_request *req)
|
||||
{
|
||||
struct ceph_fs_client *fsc = ceph_inode_to_client(req->r_inode);
|
||||
struct inode *inode = req->r_inode;
|
||||
struct ceph_fs_client *fsc = ceph_inode_to_client(inode);
|
||||
struct ceph_osd_data *osd_data = osd_req_op_extent_osd_data(req, 0);
|
||||
struct netfs_io_subrequest *subreq = req->r_priv;
|
||||
int num_pages;
|
||||
struct ceph_osd_req_op *op = &req->r_ops[0];
|
||||
int err = req->r_result;
|
||||
bool sparse = (op->op == CEPH_OSD_OP_SPARSE_READ);
|
||||
|
||||
ceph_update_read_metrics(&fsc->mdsc->metric, req->r_start_latency,
|
||||
req->r_end_latency, osd_data->length, err);
|
||||
@@ -260,14 +263,29 @@ static void finish_netfs_read(struct ceph_osd_request *req)
|
||||
else if (err == -EBLOCKLISTED)
|
||||
fsc->blocklisted = true;
|
||||
|
||||
if (err >= 0 && err < subreq->len)
|
||||
__set_bit(NETFS_SREQ_CLEAR_TAIL, &subreq->flags);
|
||||
if (err >= 0) {
|
||||
if (sparse && err > 0)
|
||||
err = ceph_sparse_ext_map_end(op);
|
||||
if (err < subreq->len)
|
||||
__set_bit(NETFS_SREQ_CLEAR_TAIL, &subreq->flags);
|
||||
if (IS_ENCRYPTED(inode) && err > 0) {
|
||||
err = ceph_fscrypt_decrypt_extents(inode,
|
||||
osd_data->pages, subreq->start,
|
||||
op->extent.sparse_ext,
|
||||
op->extent.sparse_ext_cnt);
|
||||
if (err > subreq->len)
|
||||
err = subreq->len;
|
||||
}
|
||||
}
|
||||
|
||||
if (osd_data->type == CEPH_OSD_DATA_TYPE_PAGES) {
|
||||
ceph_put_page_vector(osd_data->pages,
|
||||
calc_pages_for(osd_data->alignment,
|
||||
osd_data->length), false);
|
||||
}
|
||||
netfs_subreq_terminated(subreq, err, false);
|
||||
|
||||
num_pages = calc_pages_for(osd_data->alignment, osd_data->length);
|
||||
ceph_put_page_vector(osd_data->pages, num_pages, false);
|
||||
iput(req->r_inode);
|
||||
ceph_dec_osd_stopping_blocker(fsc->mdsc);
|
||||
}
|
||||
|
||||
static bool ceph_netfs_issue_op_inline(struct netfs_io_subrequest *subreq)
|
||||
@@ -334,10 +352,10 @@ static void ceph_netfs_issue_read(struct netfs_io_subrequest *subreq)
|
||||
struct ceph_osd_request *req = NULL;
|
||||
struct ceph_vino vino = ceph_vino(inode);
|
||||
struct iov_iter iter;
|
||||
struct page **pages;
|
||||
size_t page_off;
|
||||
int err = 0;
|
||||
u64 len = subreq->len;
|
||||
bool sparse = IS_ENCRYPTED(inode) || ceph_test_mount_opt(fsc, SPARSEREAD);
|
||||
u64 off = subreq->start;
|
||||
|
||||
if (ceph_inode_is_shutdown(inode)) {
|
||||
err = -EIO;
|
||||
@@ -347,8 +365,10 @@ static void ceph_netfs_issue_read(struct netfs_io_subrequest *subreq)
|
||||
if (ceph_has_inline_data(ci) && ceph_netfs_issue_op_inline(subreq))
|
||||
return;
|
||||
|
||||
req = ceph_osdc_new_request(&fsc->client->osdc, &ci->i_layout, vino, subreq->start, &len,
|
||||
0, 1, CEPH_OSD_OP_READ,
|
||||
ceph_fscrypt_adjust_off_and_len(inode, &off, &len);
|
||||
|
||||
req = ceph_osdc_new_request(&fsc->client->osdc, &ci->i_layout, vino,
|
||||
off, &len, 0, 1, sparse ? CEPH_OSD_OP_SPARSE_READ : CEPH_OSD_OP_READ,
|
||||
CEPH_OSD_FLAG_READ | fsc->client->osdc.client->options->read_from_replica,
|
||||
NULL, ci->i_truncate_seq, ci->i_truncate_size, false);
|
||||
if (IS_ERR(req)) {
|
||||
@@ -357,20 +377,48 @@ static void ceph_netfs_issue_read(struct netfs_io_subrequest *subreq)
|
||||
goto out;
|
||||
}
|
||||
|
||||
dout("%s: pos=%llu orig_len=%zu len=%llu\n", __func__, subreq->start, subreq->len, len);
|
||||
iov_iter_xarray(&iter, ITER_DEST, &rreq->mapping->i_pages, subreq->start, len);
|
||||
err = iov_iter_get_pages_alloc2(&iter, &pages, len, &page_off);
|
||||
if (err < 0) {
|
||||
dout("%s: iov_ter_get_pages_alloc returned %d\n", __func__, err);
|
||||
goto out;
|
||||
if (sparse) {
|
||||
err = ceph_alloc_sparse_ext_map(&req->r_ops[0]);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* should always give us a page-aligned read */
|
||||
WARN_ON_ONCE(page_off);
|
||||
len = err;
|
||||
err = 0;
|
||||
dout("%s: pos=%llu orig_len=%zu len=%llu\n", __func__, subreq->start, subreq->len, len);
|
||||
|
||||
osd_req_op_extent_osd_data_pages(req, 0, pages, len, 0, false, false);
|
||||
iov_iter_xarray(&iter, ITER_DEST, &rreq->mapping->i_pages, subreq->start, len);
|
||||
|
||||
/*
|
||||
* FIXME: For now, use CEPH_OSD_DATA_TYPE_PAGES instead of _ITER for
|
||||
* encrypted inodes. We'd need infrastructure that handles an iov_iter
|
||||
* instead of page arrays, and we don't have that as of yet. Once the
|
||||
* dust settles on the write helpers and encrypt/decrypt routines for
|
||||
* netfs, we should be able to rework this.
|
||||
*/
|
||||
if (IS_ENCRYPTED(inode)) {
|
||||
struct page **pages;
|
||||
size_t page_off;
|
||||
|
||||
err = iov_iter_get_pages_alloc2(&iter, &pages, len, &page_off);
|
||||
if (err < 0) {
|
||||
dout("%s: iov_ter_get_pages_alloc returned %d\n",
|
||||
__func__, err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* should always give us a page-aligned read */
|
||||
WARN_ON_ONCE(page_off);
|
||||
len = err;
|
||||
err = 0;
|
||||
|
||||
osd_req_op_extent_osd_data_pages(req, 0, pages, len, 0, false,
|
||||
false);
|
||||
} else {
|
||||
osd_req_op_extent_osd_iter(req, 0, &iter);
|
||||
}
|
||||
if (!ceph_inc_osd_stopping_blocker(fsc->mdsc)) {
|
||||
err = -EIO;
|
||||
goto out;
|
||||
}
|
||||
req->r_callback = finish_netfs_read;
|
||||
req->r_priv = subreq;
|
||||
req->r_inode = inode;
|
||||
@@ -571,10 +619,12 @@ static u64 get_writepages_data_length(struct inode *inode,
|
||||
struct page *page, u64 start)
|
||||
{
|
||||
struct ceph_inode_info *ci = ceph_inode(inode);
|
||||
struct ceph_snap_context *snapc = page_snap_context(page);
|
||||
struct ceph_snap_context *snapc;
|
||||
struct ceph_cap_snap *capsnap = NULL;
|
||||
u64 end = i_size_read(inode);
|
||||
u64 ret;
|
||||
|
||||
snapc = page_snap_context(ceph_fscrypt_pagecache_page(page));
|
||||
if (snapc != ci->i_head_snapc) {
|
||||
bool found = false;
|
||||
spin_lock(&ci->i_ceph_lock);
|
||||
@@ -589,9 +639,12 @@ static u64 get_writepages_data_length(struct inode *inode,
|
||||
spin_unlock(&ci->i_ceph_lock);
|
||||
WARN_ON(!found);
|
||||
}
|
||||
if (end > page_offset(page) + thp_size(page))
|
||||
end = page_offset(page) + thp_size(page);
|
||||
return end > start ? end - start : 0;
|
||||
if (end > ceph_fscrypt_page_offset(page) + thp_size(page))
|
||||
end = ceph_fscrypt_page_offset(page) + thp_size(page);
|
||||
ret = end > start ? end - start : 0;
|
||||
if (ret && fscrypt_is_bounce_page(page))
|
||||
ret = round_up(ret, CEPH_FSCRYPT_BLOCK_SIZE);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -610,10 +663,12 @@ static int writepage_nounlock(struct page *page, struct writeback_control *wbc)
|
||||
loff_t page_off = page_offset(page);
|
||||
int err;
|
||||
loff_t len = thp_size(page);
|
||||
loff_t wlen;
|
||||
struct ceph_writeback_ctl ceph_wbc;
|
||||
struct ceph_osd_client *osdc = &fsc->client->osdc;
|
||||
struct ceph_osd_request *req;
|
||||
bool caching = ceph_is_cache_enabled(inode);
|
||||
struct page *bounce_page = NULL;
|
||||
|
||||
dout("writepage %p idx %lu\n", page, page->index);
|
||||
|
||||
@@ -649,31 +704,51 @@ static int writepage_nounlock(struct page *page, struct writeback_control *wbc)
|
||||
if (ceph_wbc.i_size < page_off + len)
|
||||
len = ceph_wbc.i_size - page_off;
|
||||
|
||||
wlen = IS_ENCRYPTED(inode) ? round_up(len, CEPH_FSCRYPT_BLOCK_SIZE) : len;
|
||||
dout("writepage %p page %p index %lu on %llu~%llu snapc %p seq %lld\n",
|
||||
inode, page, page->index, page_off, len, snapc, snapc->seq);
|
||||
inode, page, page->index, page_off, wlen, snapc, snapc->seq);
|
||||
|
||||
if (atomic_long_inc_return(&fsc->writeback_count) >
|
||||
CONGESTION_ON_THRESH(fsc->mount_options->congestion_kb))
|
||||
fsc->write_congested = true;
|
||||
|
||||
req = ceph_osdc_new_request(osdc, &ci->i_layout, ceph_vino(inode), page_off, &len, 0, 1,
|
||||
CEPH_OSD_OP_WRITE, CEPH_OSD_FLAG_WRITE, snapc,
|
||||
ceph_wbc.truncate_seq, ceph_wbc.truncate_size,
|
||||
true);
|
||||
req = ceph_osdc_new_request(osdc, &ci->i_layout, ceph_vino(inode),
|
||||
page_off, &wlen, 0, 1, CEPH_OSD_OP_WRITE,
|
||||
CEPH_OSD_FLAG_WRITE, snapc,
|
||||
ceph_wbc.truncate_seq,
|
||||
ceph_wbc.truncate_size, true);
|
||||
if (IS_ERR(req)) {
|
||||
redirty_page_for_writepage(wbc, page);
|
||||
return PTR_ERR(req);
|
||||
}
|
||||
|
||||
if (wlen < len)
|
||||
len = wlen;
|
||||
|
||||
set_page_writeback(page);
|
||||
if (caching)
|
||||
ceph_set_page_fscache(page);
|
||||
ceph_fscache_write_to_cache(inode, page_off, len, caching);
|
||||
|
||||
if (IS_ENCRYPTED(inode)) {
|
||||
bounce_page = fscrypt_encrypt_pagecache_blocks(page,
|
||||
CEPH_FSCRYPT_BLOCK_SIZE, 0,
|
||||
GFP_NOFS);
|
||||
if (IS_ERR(bounce_page)) {
|
||||
redirty_page_for_writepage(wbc, page);
|
||||
end_page_writeback(page);
|
||||
ceph_osdc_put_request(req);
|
||||
return PTR_ERR(bounce_page);
|
||||
}
|
||||
}
|
||||
|
||||
/* it may be a short write due to an object boundary */
|
||||
WARN_ON_ONCE(len > thp_size(page));
|
||||
osd_req_op_extent_osd_data_pages(req, 0, &page, len, 0, false, false);
|
||||
dout("writepage %llu~%llu (%llu bytes)\n", page_off, len, len);
|
||||
osd_req_op_extent_osd_data_pages(req, 0,
|
||||
bounce_page ? &bounce_page : &page, wlen, 0,
|
||||
false, false);
|
||||
dout("writepage %llu~%llu (%llu bytes, %sencrypted)\n",
|
||||
page_off, len, wlen, IS_ENCRYPTED(inode) ? "" : "not ");
|
||||
|
||||
req->r_mtime = inode->i_mtime;
|
||||
ceph_osdc_start_request(osdc, req);
|
||||
@@ -681,7 +756,7 @@ static int writepage_nounlock(struct page *page, struct writeback_control *wbc)
|
||||
|
||||
ceph_update_write_metrics(&fsc->mdsc->metric, req->r_start_latency,
|
||||
req->r_end_latency, len, err);
|
||||
|
||||
fscrypt_free_bounce_page(bounce_page);
|
||||
ceph_osdc_put_request(req);
|
||||
if (err == 0)
|
||||
err = len;
|
||||
@@ -800,6 +875,11 @@ static void writepages_finish(struct ceph_osd_request *req)
|
||||
total_pages += num_pages;
|
||||
for (j = 0; j < num_pages; j++) {
|
||||
page = osd_data->pages[j];
|
||||
if (fscrypt_is_bounce_page(page)) {
|
||||
page = fscrypt_pagecache_page(page);
|
||||
fscrypt_free_bounce_page(osd_data->pages[j]);
|
||||
osd_data->pages[j] = page;
|
||||
}
|
||||
BUG_ON(!page);
|
||||
WARN_ON(!PageUptodate(page));
|
||||
|
||||
@@ -835,6 +915,7 @@ static void writepages_finish(struct ceph_osd_request *req)
|
||||
else
|
||||
kfree(osd_data->pages);
|
||||
ceph_osdc_put_request(req);
|
||||
ceph_dec_osd_stopping_blocker(fsc->mdsc);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1070,9 +1151,28 @@ get_more_pages:
|
||||
fsc->mount_options->congestion_kb))
|
||||
fsc->write_congested = true;
|
||||
|
||||
pages[locked_pages++] = page;
|
||||
fbatch.folios[i] = NULL;
|
||||
if (IS_ENCRYPTED(inode)) {
|
||||
pages[locked_pages] =
|
||||
fscrypt_encrypt_pagecache_blocks(page,
|
||||
PAGE_SIZE, 0,
|
||||
locked_pages ? GFP_NOWAIT : GFP_NOFS);
|
||||
if (IS_ERR(pages[locked_pages])) {
|
||||
if (PTR_ERR(pages[locked_pages]) == -EINVAL)
|
||||
pr_err("%s: inode->i_blkbits=%hhu\n",
|
||||
__func__, inode->i_blkbits);
|
||||
/* better not fail on first page! */
|
||||
BUG_ON(locked_pages == 0);
|
||||
pages[locked_pages] = NULL;
|
||||
redirty_page_for_writepage(wbc, page);
|
||||
unlock_page(page);
|
||||
break;
|
||||
}
|
||||
++locked_pages;
|
||||
} else {
|
||||
pages[locked_pages++] = page;
|
||||
}
|
||||
|
||||
fbatch.folios[i] = NULL;
|
||||
len += thp_size(page);
|
||||
}
|
||||
|
||||
@@ -1100,7 +1200,7 @@ get_more_pages:
|
||||
}
|
||||
|
||||
new_request:
|
||||
offset = page_offset(pages[0]);
|
||||
offset = ceph_fscrypt_page_offset(pages[0]);
|
||||
len = wsize;
|
||||
|
||||
req = ceph_osdc_new_request(&fsc->client->osdc,
|
||||
@@ -1121,9 +1221,13 @@ new_request:
|
||||
ceph_wbc.truncate_size, true);
|
||||
BUG_ON(IS_ERR(req));
|
||||
}
|
||||
BUG_ON(len < page_offset(pages[locked_pages - 1]) +
|
||||
thp_size(page) - offset);
|
||||
BUG_ON(len < ceph_fscrypt_page_offset(pages[locked_pages - 1]) +
|
||||
thp_size(pages[locked_pages - 1]) - offset);
|
||||
|
||||
if (!ceph_inc_osd_stopping_blocker(fsc->mdsc)) {
|
||||
rc = -EIO;
|
||||
goto release_folios;
|
||||
}
|
||||
req->r_callback = writepages_finish;
|
||||
req->r_inode = inode;
|
||||
|
||||
@@ -1132,7 +1236,9 @@ new_request:
|
||||
data_pages = pages;
|
||||
op_idx = 0;
|
||||
for (i = 0; i < locked_pages; i++) {
|
||||
u64 cur_offset = page_offset(pages[i]);
|
||||
struct page *page = ceph_fscrypt_pagecache_page(pages[i]);
|
||||
|
||||
u64 cur_offset = page_offset(page);
|
||||
/*
|
||||
* Discontinuity in page range? Ceph can handle that by just passing
|
||||
* multiple extents in the write op.
|
||||
@@ -1161,9 +1267,9 @@ new_request:
|
||||
op_idx++;
|
||||
}
|
||||
|
||||
set_page_writeback(pages[i]);
|
||||
set_page_writeback(page);
|
||||
if (caching)
|
||||
ceph_set_page_fscache(pages[i]);
|
||||
ceph_set_page_fscache(page);
|
||||
len += thp_size(page);
|
||||
}
|
||||
ceph_fscache_write_to_cache(inode, offset, len, caching);
|
||||
@@ -1179,8 +1285,16 @@ new_request:
|
||||
offset);
|
||||
len = max(len, min_len);
|
||||
}
|
||||
if (IS_ENCRYPTED(inode))
|
||||
len = round_up(len, CEPH_FSCRYPT_BLOCK_SIZE);
|
||||
|
||||
dout("writepages got pages at %llu~%llu\n", offset, len);
|
||||
|
||||
if (IS_ENCRYPTED(inode) &&
|
||||
((offset | len) & ~CEPH_FSCRYPT_BLOCK_MASK))
|
||||
pr_warn("%s: bad encrypted write offset=%lld len=%llu\n",
|
||||
__func__, offset, len);
|
||||
|
||||
osd_req_op_extent_osd_data_pages(req, op_idx, data_pages, len,
|
||||
0, from_pool, false);
|
||||
osd_req_op_extent_update(req, op_idx, len);
|
||||
|
||||
235
fs/ceph/caps.c
235
fs/ceph/caps.c
@@ -14,6 +14,7 @@
|
||||
#include "super.h"
|
||||
#include "mds_client.h"
|
||||
#include "cache.h"
|
||||
#include "crypto.h"
|
||||
#include <linux/ceph/decode.h>
|
||||
#include <linux/ceph/messenger.h>
|
||||
|
||||
@@ -1216,15 +1217,11 @@ struct cap_msg_args {
|
||||
umode_t mode;
|
||||
bool inline_data;
|
||||
bool wake;
|
||||
bool encrypted;
|
||||
u32 fscrypt_auth_len;
|
||||
u8 fscrypt_auth[sizeof(struct ceph_fscrypt_auth)]; // for context
|
||||
};
|
||||
|
||||
/*
|
||||
* cap struct size + flock buffer size + inline version + inline data size +
|
||||
* osd_epoch_barrier + oldest_flush_tid
|
||||
*/
|
||||
#define CAP_MSG_SIZE (sizeof(struct ceph_mds_caps) + \
|
||||
4 + 8 + 4 + 4 + 8 + 4 + 4 + 4 + 8 + 8 + 4)
|
||||
|
||||
/* Marshal up the cap msg to the MDS */
|
||||
static void encode_cap_msg(struct ceph_msg *msg, struct cap_msg_args *arg)
|
||||
{
|
||||
@@ -1240,7 +1237,7 @@ static void encode_cap_msg(struct ceph_msg *msg, struct cap_msg_args *arg)
|
||||
arg->size, arg->max_size, arg->xattr_version,
|
||||
arg->xattr_buf ? (int)arg->xattr_buf->vec.iov_len : 0);
|
||||
|
||||
msg->hdr.version = cpu_to_le16(10);
|
||||
msg->hdr.version = cpu_to_le16(12);
|
||||
msg->hdr.tid = cpu_to_le64(arg->flush_tid);
|
||||
|
||||
fc = msg->front.iov_base;
|
||||
@@ -1257,7 +1254,13 @@ static void encode_cap_msg(struct ceph_msg *msg, struct cap_msg_args *arg)
|
||||
fc->ino = cpu_to_le64(arg->ino);
|
||||
fc->snap_follows = cpu_to_le64(arg->follows);
|
||||
|
||||
fc->size = cpu_to_le64(arg->size);
|
||||
#if IS_ENABLED(CONFIG_FS_ENCRYPTION)
|
||||
if (arg->encrypted)
|
||||
fc->size = cpu_to_le64(round_up(arg->size,
|
||||
CEPH_FSCRYPT_BLOCK_SIZE));
|
||||
else
|
||||
#endif
|
||||
fc->size = cpu_to_le64(arg->size);
|
||||
fc->max_size = cpu_to_le64(arg->max_size);
|
||||
ceph_encode_timespec64(&fc->mtime, &arg->mtime);
|
||||
ceph_encode_timespec64(&fc->atime, &arg->atime);
|
||||
@@ -1311,6 +1314,27 @@ static void encode_cap_msg(struct ceph_msg *msg, struct cap_msg_args *arg)
|
||||
|
||||
/* Advisory flags (version 10) */
|
||||
ceph_encode_32(&p, arg->flags);
|
||||
|
||||
/* dirstats (version 11) - these are r/o on the client */
|
||||
ceph_encode_64(&p, 0);
|
||||
ceph_encode_64(&p, 0);
|
||||
|
||||
#if IS_ENABLED(CONFIG_FS_ENCRYPTION)
|
||||
/*
|
||||
* fscrypt_auth and fscrypt_file (version 12)
|
||||
*
|
||||
* fscrypt_auth holds the crypto context (if any). fscrypt_file
|
||||
* tracks the real i_size as an __le64 field (and we use a rounded-up
|
||||
* i_size in the traditional size field).
|
||||
*/
|
||||
ceph_encode_32(&p, arg->fscrypt_auth_len);
|
||||
ceph_encode_copy(&p, arg->fscrypt_auth, arg->fscrypt_auth_len);
|
||||
ceph_encode_32(&p, sizeof(__le64));
|
||||
ceph_encode_64(&p, arg->size);
|
||||
#else /* CONFIG_FS_ENCRYPTION */
|
||||
ceph_encode_32(&p, 0);
|
||||
ceph_encode_32(&p, 0);
|
||||
#endif /* CONFIG_FS_ENCRYPTION */
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1378,7 +1402,6 @@ static void __prep_cap(struct cap_msg_args *arg, struct ceph_cap *cap,
|
||||
arg->follows = flushing ? ci->i_head_snapc->seq : 0;
|
||||
arg->flush_tid = flush_tid;
|
||||
arg->oldest_flush_tid = oldest_flush_tid;
|
||||
|
||||
arg->size = i_size_read(inode);
|
||||
ci->i_reported_size = arg->size;
|
||||
arg->max_size = ci->i_wanted_max_size;
|
||||
@@ -1432,8 +1455,39 @@ static void __prep_cap(struct cap_msg_args *arg, struct ceph_cap *cap,
|
||||
}
|
||||
}
|
||||
arg->flags = flags;
|
||||
arg->encrypted = IS_ENCRYPTED(inode);
|
||||
#if IS_ENABLED(CONFIG_FS_ENCRYPTION)
|
||||
if (ci->fscrypt_auth_len &&
|
||||
WARN_ON_ONCE(ci->fscrypt_auth_len > sizeof(struct ceph_fscrypt_auth))) {
|
||||
/* Don't set this if it's too big */
|
||||
arg->fscrypt_auth_len = 0;
|
||||
} else {
|
||||
arg->fscrypt_auth_len = ci->fscrypt_auth_len;
|
||||
memcpy(arg->fscrypt_auth, ci->fscrypt_auth,
|
||||
min_t(size_t, ci->fscrypt_auth_len,
|
||||
sizeof(arg->fscrypt_auth)));
|
||||
}
|
||||
#endif /* CONFIG_FS_ENCRYPTION */
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_FS_ENCRYPTION)
|
||||
#define CAP_MSG_FIXED_FIELDS (sizeof(struct ceph_mds_caps) + \
|
||||
4 + 8 + 4 + 4 + 8 + 4 + 4 + 4 + 8 + 8 + 4 + 8 + 8 + 4 + 4 + 8)
|
||||
|
||||
static inline int cap_msg_size(struct cap_msg_args *arg)
|
||||
{
|
||||
return CAP_MSG_FIXED_FIELDS + arg->fscrypt_auth_len;
|
||||
}
|
||||
#else
|
||||
#define CAP_MSG_FIXED_FIELDS (sizeof(struct ceph_mds_caps) + \
|
||||
4 + 8 + 4 + 4 + 8 + 4 + 4 + 4 + 8 + 8 + 4 + 8 + 8 + 4 + 4)
|
||||
|
||||
static inline int cap_msg_size(struct cap_msg_args *arg)
|
||||
{
|
||||
return CAP_MSG_FIXED_FIELDS;
|
||||
}
|
||||
#endif /* CONFIG_FS_ENCRYPTION */
|
||||
|
||||
/*
|
||||
* Send a cap msg on the given inode.
|
||||
*
|
||||
@@ -1444,7 +1498,8 @@ static void __send_cap(struct cap_msg_args *arg, struct ceph_inode_info *ci)
|
||||
struct ceph_msg *msg;
|
||||
struct inode *inode = &ci->netfs.inode;
|
||||
|
||||
msg = ceph_msg_new(CEPH_MSG_CLIENT_CAPS, CAP_MSG_SIZE, GFP_NOFS, false);
|
||||
msg = ceph_msg_new(CEPH_MSG_CLIENT_CAPS, cap_msg_size(arg), GFP_NOFS,
|
||||
false);
|
||||
if (!msg) {
|
||||
pr_err("error allocating cap msg: ino (%llx.%llx) flushing %s tid %llu, requeuing cap.\n",
|
||||
ceph_vinop(inode), ceph_cap_string(arg->dirty),
|
||||
@@ -1470,10 +1525,6 @@ static inline int __send_flush_snap(struct inode *inode,
|
||||
struct cap_msg_args arg;
|
||||
struct ceph_msg *msg;
|
||||
|
||||
msg = ceph_msg_new(CEPH_MSG_CLIENT_CAPS, CAP_MSG_SIZE, GFP_NOFS, false);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
arg.session = session;
|
||||
arg.ino = ceph_vino(inode).ino;
|
||||
arg.cid = 0;
|
||||
@@ -1510,6 +1561,15 @@ static inline int __send_flush_snap(struct inode *inode,
|
||||
arg.inline_data = capsnap->inline_data;
|
||||
arg.flags = 0;
|
||||
arg.wake = false;
|
||||
arg.encrypted = IS_ENCRYPTED(inode);
|
||||
|
||||
/* No fscrypt_auth changes from a capsnap.*/
|
||||
arg.fscrypt_auth_len = 0;
|
||||
|
||||
msg = ceph_msg_new(CEPH_MSG_CLIENT_CAPS, cap_msg_size(&arg),
|
||||
GFP_NOFS, false);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
encode_cap_msg(msg, &arg);
|
||||
ceph_con_send(&arg.session->s_con, msg);
|
||||
@@ -2900,10 +2960,9 @@ int ceph_try_get_caps(struct inode *inode, int need, int want,
|
||||
* due to a small max_size, make sure we check_max_size (and possibly
|
||||
* ask the mds) so we don't get hung up indefinitely.
|
||||
*/
|
||||
int ceph_get_caps(struct file *filp, int need, int want, loff_t endoff, int *got)
|
||||
int __ceph_get_caps(struct inode *inode, struct ceph_file_info *fi, int need,
|
||||
int want, loff_t endoff, int *got)
|
||||
{
|
||||
struct ceph_file_info *fi = filp->private_data;
|
||||
struct inode *inode = file_inode(filp);
|
||||
struct ceph_inode_info *ci = ceph_inode(inode);
|
||||
struct ceph_fs_client *fsc = ceph_inode_to_client(inode);
|
||||
int ret, _got, flags;
|
||||
@@ -2912,7 +2971,7 @@ int ceph_get_caps(struct file *filp, int need, int want, loff_t endoff, int *got
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if ((fi->fmode & CEPH_FILE_MODE_WR) &&
|
||||
if (fi && (fi->fmode & CEPH_FILE_MODE_WR) &&
|
||||
fi->filp_gen != READ_ONCE(fsc->filp_gen))
|
||||
return -EBADF;
|
||||
|
||||
@@ -2965,7 +3024,7 @@ int ceph_get_caps(struct file *filp, int need, int want, loff_t endoff, int *got
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((fi->fmode & CEPH_FILE_MODE_WR) &&
|
||||
if (fi && (fi->fmode & CEPH_FILE_MODE_WR) &&
|
||||
fi->filp_gen != READ_ONCE(fsc->filp_gen)) {
|
||||
if (ret >= 0 && _got)
|
||||
ceph_put_cap_refs(ci, _got);
|
||||
@@ -3028,6 +3087,15 @@ int ceph_get_caps(struct file *filp, int need, int want, loff_t endoff, int *got
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ceph_get_caps(struct file *filp, int need, int want, loff_t endoff,
|
||||
int *got)
|
||||
{
|
||||
struct ceph_file_info *fi = filp->private_data;
|
||||
struct inode *inode = file_inode(filp);
|
||||
|
||||
return __ceph_get_caps(inode, fi, need, want, endoff, got);
|
||||
}
|
||||
|
||||
/*
|
||||
* Take cap refs. Caller must already know we hold at least one ref
|
||||
* on the caps in question or we don't know this is safe.
|
||||
@@ -3323,6 +3391,9 @@ struct cap_extra_info {
|
||||
/* currently issued */
|
||||
int issued;
|
||||
struct timespec64 btime;
|
||||
u8 *fscrypt_auth;
|
||||
u32 fscrypt_auth_len;
|
||||
u64 fscrypt_file_size;
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -3355,6 +3426,14 @@ static void handle_cap_grant(struct inode *inode,
|
||||
bool deleted_inode = false;
|
||||
bool fill_inline = false;
|
||||
|
||||
/*
|
||||
* If there is at least one crypto block then we'll trust
|
||||
* fscrypt_file_size. If the real length of the file is 0, then
|
||||
* ignore it (it has probably been truncated down to 0 by the MDS).
|
||||
*/
|
||||
if (IS_ENCRYPTED(inode) && size)
|
||||
size = extra_info->fscrypt_file_size;
|
||||
|
||||
dout("handle_cap_grant inode %p cap %p mds%d seq %d %s\n",
|
||||
inode, cap, session->s_mds, seq, ceph_cap_string(newcaps));
|
||||
dout(" size %llu max_size %llu, i_size %llu\n", size, max_size,
|
||||
@@ -3421,6 +3500,14 @@ static void handle_cap_grant(struct inode *inode,
|
||||
dout("%p mode 0%o uid.gid %d.%d\n", inode, inode->i_mode,
|
||||
from_kuid(&init_user_ns, inode->i_uid),
|
||||
from_kgid(&init_user_ns, inode->i_gid));
|
||||
#if IS_ENABLED(CONFIG_FS_ENCRYPTION)
|
||||
if (ci->fscrypt_auth_len != extra_info->fscrypt_auth_len ||
|
||||
memcmp(ci->fscrypt_auth, extra_info->fscrypt_auth,
|
||||
ci->fscrypt_auth_len))
|
||||
pr_warn_ratelimited("%s: cap grant attempt to change fscrypt_auth on non-I_NEW inode (old len %d new len %d)\n",
|
||||
__func__, ci->fscrypt_auth_len,
|
||||
extra_info->fscrypt_auth_len);
|
||||
#endif
|
||||
}
|
||||
|
||||
if ((newcaps & CEPH_CAP_LINK_SHARED) &&
|
||||
@@ -3837,7 +3924,8 @@ static void handle_cap_flushsnap_ack(struct inode *inode, u64 flush_tid,
|
||||
*/
|
||||
static bool handle_cap_trunc(struct inode *inode,
|
||||
struct ceph_mds_caps *trunc,
|
||||
struct ceph_mds_session *session)
|
||||
struct ceph_mds_session *session,
|
||||
struct cap_extra_info *extra_info)
|
||||
{
|
||||
struct ceph_inode_info *ci = ceph_inode(inode);
|
||||
int mds = session->s_mds;
|
||||
@@ -3854,8 +3942,16 @@ static bool handle_cap_trunc(struct inode *inode,
|
||||
|
||||
issued |= implemented | dirty;
|
||||
|
||||
dout("handle_cap_trunc inode %p mds%d seq %d to %lld seq %d\n",
|
||||
inode, mds, seq, truncate_size, truncate_seq);
|
||||
/*
|
||||
* If there is at least one crypto block then we'll trust
|
||||
* fscrypt_file_size. If the real length of the file is 0, then
|
||||
* ignore it (it has probably been truncated down to 0 by the MDS).
|
||||
*/
|
||||
if (IS_ENCRYPTED(inode) && size)
|
||||
size = extra_info->fscrypt_file_size;
|
||||
|
||||
dout("%s inode %p mds%d seq %d to %lld truncate seq %d\n",
|
||||
__func__, inode, mds, seq, truncate_size, truncate_seq);
|
||||
queue_trunc = ceph_fill_file_size(inode, issued,
|
||||
truncate_seq, truncate_size, size);
|
||||
return queue_trunc;
|
||||
@@ -4075,6 +4171,52 @@ retry:
|
||||
*target_cap = cap;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_FS_ENCRYPTION
|
||||
static int parse_fscrypt_fields(void **p, void *end,
|
||||
struct cap_extra_info *extra)
|
||||
{
|
||||
u32 len;
|
||||
|
||||
ceph_decode_32_safe(p, end, extra->fscrypt_auth_len, bad);
|
||||
if (extra->fscrypt_auth_len) {
|
||||
ceph_decode_need(p, end, extra->fscrypt_auth_len, bad);
|
||||
extra->fscrypt_auth = kmalloc(extra->fscrypt_auth_len,
|
||||
GFP_KERNEL);
|
||||
if (!extra->fscrypt_auth)
|
||||
return -ENOMEM;
|
||||
ceph_decode_copy_safe(p, end, extra->fscrypt_auth,
|
||||
extra->fscrypt_auth_len, bad);
|
||||
}
|
||||
|
||||
ceph_decode_32_safe(p, end, len, bad);
|
||||
if (len >= sizeof(u64)) {
|
||||
ceph_decode_64_safe(p, end, extra->fscrypt_file_size, bad);
|
||||
len -= sizeof(u64);
|
||||
}
|
||||
ceph_decode_skip_n(p, end, len, bad);
|
||||
return 0;
|
||||
bad:
|
||||
return -EIO;
|
||||
}
|
||||
#else
|
||||
static int parse_fscrypt_fields(void **p, void *end,
|
||||
struct cap_extra_info *extra)
|
||||
{
|
||||
u32 len;
|
||||
|
||||
/* Don't care about these fields unless we're encryption-capable */
|
||||
ceph_decode_32_safe(p, end, len, bad);
|
||||
if (len)
|
||||
ceph_decode_skip_n(p, end, len, bad);
|
||||
ceph_decode_32_safe(p, end, len, bad);
|
||||
if (len)
|
||||
ceph_decode_skip_n(p, end, len, bad);
|
||||
return 0;
|
||||
bad:
|
||||
return -EIO;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Handle a caps message from the MDS.
|
||||
*
|
||||
@@ -4105,6 +4247,9 @@ void ceph_handle_caps(struct ceph_mds_session *session,
|
||||
|
||||
dout("handle_caps from mds%d\n", session->s_mds);
|
||||
|
||||
if (!ceph_inc_mds_stopping_blocker(mdsc, session))
|
||||
return;
|
||||
|
||||
/* decode */
|
||||
end = msg->front.iov_base + msg->front.iov_len;
|
||||
if (msg->front.iov_len < sizeof(*h))
|
||||
@@ -4195,13 +4340,17 @@ void ceph_handle_caps(struct ceph_mds_session *session,
|
||||
ceph_decode_64_safe(&p, end, extra_info.nsubdirs, bad);
|
||||
}
|
||||
|
||||
if (msg_version >= 12) {
|
||||
if (parse_fscrypt_fields(&p, end, &extra_info))
|
||||
goto bad;
|
||||
}
|
||||
|
||||
/* lookup ino */
|
||||
inode = ceph_find_inode(mdsc->fsc->sb, vino);
|
||||
dout(" op %s ino %llx.%llx inode %p\n", ceph_cap_op_name(op), vino.ino,
|
||||
vino.snap, inode);
|
||||
|
||||
mutex_lock(&session->s_mutex);
|
||||
inc_session_sequence(session);
|
||||
dout(" mds%d seq %lld cap seq %u\n", session->s_mds, session->s_seq,
|
||||
(unsigned)seq);
|
||||
|
||||
@@ -4292,7 +4441,8 @@ void ceph_handle_caps(struct ceph_mds_session *session,
|
||||
break;
|
||||
|
||||
case CEPH_CAP_OP_TRUNC:
|
||||
queue_trunc = handle_cap_trunc(inode, h, session);
|
||||
queue_trunc = handle_cap_trunc(inode, h, session,
|
||||
&extra_info);
|
||||
spin_unlock(&ci->i_ceph_lock);
|
||||
if (queue_trunc)
|
||||
ceph_queue_vmtruncate(inode);
|
||||
@@ -4309,12 +4459,15 @@ done:
|
||||
done_unlocked:
|
||||
iput(inode);
|
||||
out:
|
||||
ceph_dec_mds_stopping_blocker(mdsc);
|
||||
|
||||
ceph_put_string(extra_info.pool_ns);
|
||||
|
||||
/* Defer closing the sessions after s_mutex lock being released */
|
||||
if (close_sessions)
|
||||
ceph_mdsc_close_sessions(mdsc);
|
||||
|
||||
kfree(extra_info.fscrypt_auth);
|
||||
return;
|
||||
|
||||
flush_cap_releases:
|
||||
@@ -4611,6 +4764,18 @@ int ceph_encode_inode_release(void **p, struct inode *inode,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ceph_encode_dentry_release - encode a dentry release into an outgoing request
|
||||
* @p: outgoing request buffer
|
||||
* @dentry: dentry to release
|
||||
* @dir: dir to release it from
|
||||
* @mds: mds that we're speaking to
|
||||
* @drop: caps being dropped
|
||||
* @unless: unless we have these caps
|
||||
*
|
||||
* Encode a dentry release into an outgoing request buffer. Returns 1 if the
|
||||
* thing was released, or a negative error code otherwise.
|
||||
*/
|
||||
int ceph_encode_dentry_release(void **p, struct dentry *dentry,
|
||||
struct inode *dir,
|
||||
int mds, int drop, int unless)
|
||||
@@ -4643,13 +4808,25 @@ int ceph_encode_dentry_release(void **p, struct dentry *dentry,
|
||||
if (ret && di->lease_session && di->lease_session->s_mds == mds) {
|
||||
dout("encode_dentry_release %p mds%d seq %d\n",
|
||||
dentry, mds, (int)di->lease_seq);
|
||||
rel->dname_len = cpu_to_le32(dentry->d_name.len);
|
||||
memcpy(*p, dentry->d_name.name, dentry->d_name.len);
|
||||
*p += dentry->d_name.len;
|
||||
rel->dname_seq = cpu_to_le32(di->lease_seq);
|
||||
__ceph_mdsc_drop_dentry_lease(dentry);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
if (IS_ENCRYPTED(dir) && fscrypt_has_encryption_key(dir)) {
|
||||
int ret2 = ceph_encode_encrypted_fname(dir, dentry, *p);
|
||||
|
||||
if (ret2 < 0)
|
||||
return ret2;
|
||||
|
||||
rel->dname_len = cpu_to_le32(ret2);
|
||||
*p += ret2;
|
||||
} else {
|
||||
rel->dname_len = cpu_to_le32(dentry->d_name.len);
|
||||
memcpy(*p, dentry->d_name.name, dentry->d_name.len);
|
||||
*p += dentry->d_name.len;
|
||||
}
|
||||
} else {
|
||||
spin_unlock(&dentry->d_lock);
|
||||
}
|
||||
spin_unlock(&dentry->d_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
673
fs/ceph/crypto.c
Normal file
673
fs/ceph/crypto.c
Normal file
File diff suppressed because it is too large
Load Diff
288
fs/ceph/crypto.h
Normal file
288
fs/ceph/crypto.h
Normal file
@@ -0,0 +1,288 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Ceph fscrypt functionality
|
||||
*/
|
||||
|
||||
#ifndef _CEPH_CRYPTO_H
|
||||
#define _CEPH_CRYPTO_H
|
||||
|
||||
#include <crypto/sha2.h>
|
||||
#include <linux/fscrypt.h>
|
||||
|
||||
#define CEPH_FSCRYPT_BLOCK_SHIFT 12
|
||||
#define CEPH_FSCRYPT_BLOCK_SIZE (_AC(1, UL) << CEPH_FSCRYPT_BLOCK_SHIFT)
|
||||
#define CEPH_FSCRYPT_BLOCK_MASK (~(CEPH_FSCRYPT_BLOCK_SIZE-1))
|
||||
|
||||
struct ceph_fs_client;
|
||||
struct ceph_acl_sec_ctx;
|
||||
struct ceph_mds_request;
|
||||
|
||||
struct ceph_fname {
|
||||
struct inode *dir;
|
||||
char *name; // b64 encoded, possibly hashed
|
||||
unsigned char *ctext; // binary crypttext (if any)
|
||||
u32 name_len; // length of name buffer
|
||||
u32 ctext_len; // length of crypttext
|
||||
bool no_copy;
|
||||
};
|
||||
|
||||
/*
|
||||
* Header for the crypted file when truncating the size, this
|
||||
* will be sent to MDS, and the MDS will update the encrypted
|
||||
* last block and then truncate the size.
|
||||
*/
|
||||
struct ceph_fscrypt_truncate_size_header {
|
||||
__u8 ver;
|
||||
__u8 compat;
|
||||
|
||||
/*
|
||||
* It will be sizeof(assert_ver + file_offset + block_size)
|
||||
* if the last block is empty when it's located in a file
|
||||
* hole. Or the data_len will plus CEPH_FSCRYPT_BLOCK_SIZE.
|
||||
*/
|
||||
__le32 data_len;
|
||||
|
||||
__le64 change_attr;
|
||||
__le64 file_offset;
|
||||
__le32 block_size;
|
||||
} __packed;
|
||||
|
||||
struct ceph_fscrypt_auth {
|
||||
__le32 cfa_version;
|
||||
__le32 cfa_blob_len;
|
||||
u8 cfa_blob[FSCRYPT_SET_CONTEXT_MAX_SIZE];
|
||||
} __packed;
|
||||
|
||||
#define CEPH_FSCRYPT_AUTH_VERSION 1
|
||||
static inline u32 ceph_fscrypt_auth_len(struct ceph_fscrypt_auth *fa)
|
||||
{
|
||||
u32 ctxsize = le32_to_cpu(fa->cfa_blob_len);
|
||||
|
||||
return offsetof(struct ceph_fscrypt_auth, cfa_blob) + ctxsize;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_FS_ENCRYPTION
|
||||
/*
|
||||
* We want to encrypt filenames when creating them, but the encrypted
|
||||
* versions of those names may have illegal characters in them. To mitigate
|
||||
* that, we base64 encode them, but that gives us a result that can exceed
|
||||
* NAME_MAX.
|
||||
*
|
||||
* Follow a similar scheme to fscrypt itself, and cap the filename to a
|
||||
* smaller size. If the ciphertext name is longer than the value below, then
|
||||
* sha256 hash the remaining bytes.
|
||||
*
|
||||
* For the fscrypt_nokey_name struct the dirhash[2] member is useless in ceph
|
||||
* so the corresponding struct will be:
|
||||
*
|
||||
* struct fscrypt_ceph_nokey_name {
|
||||
* u8 bytes[157];
|
||||
* u8 sha256[SHA256_DIGEST_SIZE];
|
||||
* }; // 180 bytes => 240 bytes base64-encoded, which is <= NAME_MAX (255)
|
||||
*
|
||||
* (240 bytes is the maximum size allowed for snapshot names to take into
|
||||
* account the format: '_<SNAPSHOT-NAME>_<INODE-NUMBER>'.)
|
||||
*
|
||||
* Note that for long names that end up having their tail portion hashed, we
|
||||
* must also store the full encrypted name (in the dentry's alternate_name
|
||||
* field).
|
||||
*/
|
||||
#define CEPH_NOHASH_NAME_MAX (180 - SHA256_DIGEST_SIZE)
|
||||
|
||||
#define CEPH_BASE64_CHARS(nbytes) DIV_ROUND_UP((nbytes) * 4, 3)
|
||||
|
||||
int ceph_base64_encode(const u8 *src, int srclen, char *dst);
|
||||
int ceph_base64_decode(const char *src, int srclen, u8 *dst);
|
||||
|
||||
void ceph_fscrypt_set_ops(struct super_block *sb);
|
||||
|
||||
void ceph_fscrypt_free_dummy_policy(struct ceph_fs_client *fsc);
|
||||
|
||||
int ceph_fscrypt_prepare_context(struct inode *dir, struct inode *inode,
|
||||
struct ceph_acl_sec_ctx *as);
|
||||
void ceph_fscrypt_as_ctx_to_req(struct ceph_mds_request *req,
|
||||
struct ceph_acl_sec_ctx *as);
|
||||
int ceph_encode_encrypted_dname(struct inode *parent, struct qstr *d_name,
|
||||
char *buf);
|
||||
int ceph_encode_encrypted_fname(struct inode *parent, struct dentry *dentry,
|
||||
char *buf);
|
||||
|
||||
static inline int ceph_fname_alloc_buffer(struct inode *parent,
|
||||
struct fscrypt_str *fname)
|
||||
{
|
||||
if (!IS_ENCRYPTED(parent))
|
||||
return 0;
|
||||
return fscrypt_fname_alloc_buffer(NAME_MAX, fname);
|
||||
}
|
||||
|
||||
static inline void ceph_fname_free_buffer(struct inode *parent,
|
||||
struct fscrypt_str *fname)
|
||||
{
|
||||
if (IS_ENCRYPTED(parent))
|
||||
fscrypt_fname_free_buffer(fname);
|
||||
}
|
||||
|
||||
int ceph_fname_to_usr(const struct ceph_fname *fname, struct fscrypt_str *tname,
|
||||
struct fscrypt_str *oname, bool *is_nokey);
|
||||
int ceph_fscrypt_prepare_readdir(struct inode *dir);
|
||||
|
||||
static inline unsigned int ceph_fscrypt_blocks(u64 off, u64 len)
|
||||
{
|
||||
/* crypto blocks cannot span more than one page */
|
||||
BUILD_BUG_ON(CEPH_FSCRYPT_BLOCK_SHIFT > PAGE_SHIFT);
|
||||
|
||||
return ((off+len+CEPH_FSCRYPT_BLOCK_SIZE-1) >> CEPH_FSCRYPT_BLOCK_SHIFT) -
|
||||
(off >> CEPH_FSCRYPT_BLOCK_SHIFT);
|
||||
}
|
||||
|
||||
/*
|
||||
* If we have an encrypted inode then we must adjust the offset and
|
||||
* range of the on-the-wire read to cover an entire encryption block.
|
||||
* The copy will be done using the original offset and length, after
|
||||
* we've decrypted the result.
|
||||
*/
|
||||
static inline void ceph_fscrypt_adjust_off_and_len(struct inode *inode,
|
||||
u64 *off, u64 *len)
|
||||
{
|
||||
if (IS_ENCRYPTED(inode)) {
|
||||
*len = ceph_fscrypt_blocks(*off, *len) * CEPH_FSCRYPT_BLOCK_SIZE;
|
||||
*off &= CEPH_FSCRYPT_BLOCK_MASK;
|
||||
}
|
||||
}
|
||||
|
||||
int ceph_fscrypt_decrypt_block_inplace(const struct inode *inode,
|
||||
struct page *page, unsigned int len,
|
||||
unsigned int offs, u64 lblk_num);
|
||||
int ceph_fscrypt_encrypt_block_inplace(const struct inode *inode,
|
||||
struct page *page, unsigned int len,
|
||||
unsigned int offs, u64 lblk_num,
|
||||
gfp_t gfp_flags);
|
||||
int ceph_fscrypt_decrypt_pages(struct inode *inode, struct page **page,
|
||||
u64 off, int len);
|
||||
int ceph_fscrypt_decrypt_extents(struct inode *inode, struct page **page,
|
||||
u64 off, struct ceph_sparse_extent *map,
|
||||
u32 ext_cnt);
|
||||
int ceph_fscrypt_encrypt_pages(struct inode *inode, struct page **page, u64 off,
|
||||
int len, gfp_t gfp);
|
||||
|
||||
static inline struct page *ceph_fscrypt_pagecache_page(struct page *page)
|
||||
{
|
||||
return fscrypt_is_bounce_page(page) ? fscrypt_pagecache_page(page) : page;
|
||||
}
|
||||
|
||||
#else /* CONFIG_FS_ENCRYPTION */
|
||||
|
||||
static inline void ceph_fscrypt_set_ops(struct super_block *sb)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void ceph_fscrypt_free_dummy_policy(struct ceph_fs_client *fsc)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int ceph_fscrypt_prepare_context(struct inode *dir,
|
||||
struct inode *inode,
|
||||
struct ceph_acl_sec_ctx *as)
|
||||
{
|
||||
if (IS_ENCRYPTED(dir))
|
||||
return -EOPNOTSUPP;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void ceph_fscrypt_as_ctx_to_req(struct ceph_mds_request *req,
|
||||
struct ceph_acl_sec_ctx *as_ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int ceph_encode_encrypted_dname(struct inode *parent,
|
||||
struct qstr *d_name, char *buf)
|
||||
{
|
||||
memcpy(buf, d_name->name, d_name->len);
|
||||
return d_name->len;
|
||||
}
|
||||
|
||||
static inline int ceph_encode_encrypted_fname(struct inode *parent,
|
||||
struct dentry *dentry, char *buf)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int ceph_fname_alloc_buffer(struct inode *parent,
|
||||
struct fscrypt_str *fname)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void ceph_fname_free_buffer(struct inode *parent,
|
||||
struct fscrypt_str *fname)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int ceph_fname_to_usr(const struct ceph_fname *fname,
|
||||
struct fscrypt_str *tname,
|
||||
struct fscrypt_str *oname, bool *is_nokey)
|
||||
{
|
||||
oname->name = fname->name;
|
||||
oname->len = fname->name_len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int ceph_fscrypt_prepare_readdir(struct inode *dir)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void ceph_fscrypt_adjust_off_and_len(struct inode *inode,
|
||||
u64 *off, u64 *len)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int ceph_fscrypt_decrypt_block_inplace(const struct inode *inode,
|
||||
struct page *page, unsigned int len,
|
||||
unsigned int offs, u64 lblk_num)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int ceph_fscrypt_encrypt_block_inplace(const struct inode *inode,
|
||||
struct page *page, unsigned int len,
|
||||
unsigned int offs, u64 lblk_num,
|
||||
gfp_t gfp_flags)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int ceph_fscrypt_decrypt_pages(struct inode *inode,
|
||||
struct page **page, u64 off,
|
||||
int len)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int ceph_fscrypt_decrypt_extents(struct inode *inode,
|
||||
struct page **page, u64 off,
|
||||
struct ceph_sparse_extent *map,
|
||||
u32 ext_cnt)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int ceph_fscrypt_encrypt_pages(struct inode *inode,
|
||||
struct page **page, u64 off,
|
||||
int len, gfp_t gfp)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline struct page *ceph_fscrypt_pagecache_page(struct page *page)
|
||||
{
|
||||
return page;
|
||||
}
|
||||
#endif /* CONFIG_FS_ENCRYPTION */
|
||||
|
||||
static inline loff_t ceph_fscrypt_page_offset(struct page *page)
|
||||
{
|
||||
return page_offset(ceph_fscrypt_pagecache_page(page));
|
||||
}
|
||||
|
||||
#endif /* _CEPH_CRYPTO_H */
|
||||
194
fs/ceph/dir.c
194
fs/ceph/dir.c
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "super.h"
|
||||
#include "mds_client.h"
|
||||
#include "crypto.h"
|
||||
|
||||
/*
|
||||
* Directory operations: readdir, lookup, create, link, unlink,
|
||||
@@ -241,7 +242,9 @@ static int __dcache_readdir(struct file *file, struct dir_context *ctx,
|
||||
di = ceph_dentry(dentry);
|
||||
if (d_unhashed(dentry) ||
|
||||
d_really_is_negative(dentry) ||
|
||||
di->lease_shared_gen != shared_gen) {
|
||||
di->lease_shared_gen != shared_gen ||
|
||||
((dentry->d_flags & DCACHE_NOKEY_NAME) &&
|
||||
fscrypt_has_encryption_key(dir))) {
|
||||
spin_unlock(&dentry->d_lock);
|
||||
dput(dentry);
|
||||
err = -EAGAIN;
|
||||
@@ -340,6 +343,10 @@ static int ceph_readdir(struct file *file, struct dir_context *ctx)
|
||||
ctx->pos = 2;
|
||||
}
|
||||
|
||||
err = ceph_fscrypt_prepare_readdir(inode);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
spin_lock(&ci->i_ceph_lock);
|
||||
/* request Fx cap. if have Fx, we don't need to release Fs cap
|
||||
* for later create/unlink. */
|
||||
@@ -389,6 +396,7 @@ more:
|
||||
req = ceph_mdsc_create_request(mdsc, op, USE_AUTH_MDS);
|
||||
if (IS_ERR(req))
|
||||
return PTR_ERR(req);
|
||||
|
||||
err = ceph_alloc_readdir_reply_buffer(req, inode);
|
||||
if (err) {
|
||||
ceph_mdsc_put_request(req);
|
||||
@@ -402,11 +410,21 @@ more:
|
||||
req->r_inode_drop = CEPH_CAP_FILE_EXCL;
|
||||
}
|
||||
if (dfi->last_name) {
|
||||
req->r_path2 = kstrdup(dfi->last_name, GFP_KERNEL);
|
||||
struct qstr d_name = { .name = dfi->last_name,
|
||||
.len = strlen(dfi->last_name) };
|
||||
|
||||
req->r_path2 = kzalloc(NAME_MAX + 1, GFP_KERNEL);
|
||||
if (!req->r_path2) {
|
||||
ceph_mdsc_put_request(req);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
err = ceph_encode_encrypted_dname(inode, &d_name,
|
||||
req->r_path2);
|
||||
if (err < 0) {
|
||||
ceph_mdsc_put_request(req);
|
||||
return err;
|
||||
}
|
||||
} else if (is_hash_order(ctx->pos)) {
|
||||
req->r_args.readdir.offset_hash =
|
||||
cpu_to_le32(fpos_hash(ctx->pos));
|
||||
@@ -511,15 +529,20 @@ more:
|
||||
for (; i < rinfo->dir_nr; i++) {
|
||||
struct ceph_mds_reply_dir_entry *rde = rinfo->dir_entries + i;
|
||||
|
||||
BUG_ON(rde->offset < ctx->pos);
|
||||
if (rde->offset < ctx->pos) {
|
||||
pr_warn("%s: rde->offset 0x%llx ctx->pos 0x%llx\n",
|
||||
__func__, rde->offset, ctx->pos);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (WARN_ON_ONCE(!rde->inode.in))
|
||||
return -EIO;
|
||||
|
||||
ctx->pos = rde->offset;
|
||||
dout("readdir (%d/%d) -> %llx '%.*s' %p\n",
|
||||
i, rinfo->dir_nr, ctx->pos,
|
||||
rde->name_len, rde->name, &rde->inode.in);
|
||||
|
||||
BUG_ON(!rde->inode.in);
|
||||
|
||||
if (!dir_emit(ctx, rde->name, rde->name_len,
|
||||
ceph_present_ino(inode->i_sb, le64_to_cpu(rde->inode.in->ino)),
|
||||
le32_to_cpu(rde->inode.in->mode) >> 12)) {
|
||||
@@ -532,6 +555,8 @@ more:
|
||||
dout("filldir stopping us...\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Reset the lengths to their original allocated vals */
|
||||
ctx->pos++;
|
||||
}
|
||||
|
||||
@@ -586,7 +611,6 @@ more:
|
||||
dfi->dir_ordered_count);
|
||||
spin_unlock(&ci->i_ceph_lock);
|
||||
}
|
||||
|
||||
dout("readdir %p file %p done.\n", inode, file);
|
||||
return 0;
|
||||
}
|
||||
@@ -760,6 +784,18 @@ static struct dentry *ceph_lookup(struct inode *dir, struct dentry *dentry,
|
||||
if (dentry->d_name.len > NAME_MAX)
|
||||
return ERR_PTR(-ENAMETOOLONG);
|
||||
|
||||
if (IS_ENCRYPTED(dir)) {
|
||||
bool had_key = fscrypt_has_encryption_key(dir);
|
||||
|
||||
err = fscrypt_prepare_lookup_partial(dir, dentry);
|
||||
if (err < 0)
|
||||
return ERR_PTR(err);
|
||||
|
||||
/* mark directory as incomplete if it has been unlocked */
|
||||
if (!had_key && fscrypt_has_encryption_key(dir))
|
||||
ceph_dir_clear_complete(dir);
|
||||
}
|
||||
|
||||
/* can we conclude ENOENT locally? */
|
||||
if (d_really_is_negative(dentry)) {
|
||||
struct ceph_inode_info *ci = ceph_inode(dir);
|
||||
@@ -865,13 +901,6 @@ static int ceph_mknod(struct mnt_idmap *idmap, struct inode *dir,
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = ceph_pre_init_acls(dir, &mode, &as_ctx);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
err = ceph_security_init_secctx(dentry, mode, &as_ctx);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
dout("mknod in dir %p dentry %p mode 0%ho rdev %d\n",
|
||||
dir, dentry, mode, rdev);
|
||||
req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_MKNOD, USE_AUTH_MDS);
|
||||
@@ -879,6 +908,17 @@ static int ceph_mknod(struct mnt_idmap *idmap, struct inode *dir,
|
||||
err = PTR_ERR(req);
|
||||
goto out;
|
||||
}
|
||||
|
||||
req->r_new_inode = ceph_new_inode(dir, dentry, &mode, &as_ctx);
|
||||
if (IS_ERR(req->r_new_inode)) {
|
||||
err = PTR_ERR(req->r_new_inode);
|
||||
req->r_new_inode = NULL;
|
||||
goto out_req;
|
||||
}
|
||||
|
||||
if (S_ISREG(mode) && IS_ENCRYPTED(dir))
|
||||
set_bit(CEPH_MDS_R_FSCRYPT_FILE, &req->r_req_flags);
|
||||
|
||||
req->r_dentry = dget(dentry);
|
||||
req->r_num_caps = 2;
|
||||
req->r_parent = dir;
|
||||
@@ -889,13 +929,13 @@ static int ceph_mknod(struct mnt_idmap *idmap, struct inode *dir,
|
||||
req->r_dentry_drop = CEPH_CAP_FILE_SHARED | CEPH_CAP_AUTH_EXCL |
|
||||
CEPH_CAP_XATTR_EXCL;
|
||||
req->r_dentry_unless = CEPH_CAP_FILE_EXCL;
|
||||
if (as_ctx.pagelist) {
|
||||
req->r_pagelist = as_ctx.pagelist;
|
||||
as_ctx.pagelist = NULL;
|
||||
}
|
||||
|
||||
ceph_as_ctx_to_req(req, &as_ctx);
|
||||
|
||||
err = ceph_mdsc_do_request(mdsc, dir, req);
|
||||
if (!err && !req->r_reply_info.head->is_dentry)
|
||||
err = ceph_handle_notrace_create(dir, dentry);
|
||||
out_req:
|
||||
ceph_mdsc_put_request(req);
|
||||
out:
|
||||
if (!err)
|
||||
@@ -912,12 +952,50 @@ static int ceph_create(struct mnt_idmap *idmap, struct inode *dir,
|
||||
return ceph_mknod(idmap, dir, dentry, mode, 0);
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_FS_ENCRYPTION)
|
||||
static int prep_encrypted_symlink_target(struct ceph_mds_request *req,
|
||||
const char *dest)
|
||||
{
|
||||
int err;
|
||||
int len = strlen(dest);
|
||||
struct fscrypt_str osd_link = FSTR_INIT(NULL, 0);
|
||||
|
||||
err = fscrypt_prepare_symlink(req->r_parent, dest, len, PATH_MAX,
|
||||
&osd_link);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = fscrypt_encrypt_symlink(req->r_new_inode, dest, len, &osd_link);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
req->r_path2 = kmalloc(CEPH_BASE64_CHARS(osd_link.len) + 1, GFP_KERNEL);
|
||||
if (!req->r_path2) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
len = ceph_base64_encode(osd_link.name, osd_link.len, req->r_path2);
|
||||
req->r_path2[len] = '\0';
|
||||
out:
|
||||
fscrypt_fname_free_buffer(&osd_link);
|
||||
return err;
|
||||
}
|
||||
#else
|
||||
static int prep_encrypted_symlink_target(struct ceph_mds_request *req,
|
||||
const char *dest)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int ceph_symlink(struct mnt_idmap *idmap, struct inode *dir,
|
||||
struct dentry *dentry, const char *dest)
|
||||
{
|
||||
struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(dir->i_sb);
|
||||
struct ceph_mds_request *req;
|
||||
struct ceph_acl_sec_ctx as_ctx = {};
|
||||
umode_t mode = S_IFLNK | 0777;
|
||||
int err;
|
||||
|
||||
if (ceph_snap(dir) != CEPH_NOSNAP)
|
||||
@@ -932,38 +1010,48 @@ static int ceph_symlink(struct mnt_idmap *idmap, struct inode *dir,
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = ceph_security_init_secctx(dentry, S_IFLNK | 0777, &as_ctx);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
dout("symlink in dir %p dentry %p to '%s'\n", dir, dentry, dest);
|
||||
req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_SYMLINK, USE_AUTH_MDS);
|
||||
if (IS_ERR(req)) {
|
||||
err = PTR_ERR(req);
|
||||
goto out;
|
||||
}
|
||||
req->r_path2 = kstrdup(dest, GFP_KERNEL);
|
||||
if (!req->r_path2) {
|
||||
err = -ENOMEM;
|
||||
ceph_mdsc_put_request(req);
|
||||
goto out;
|
||||
|
||||
req->r_new_inode = ceph_new_inode(dir, dentry, &mode, &as_ctx);
|
||||
if (IS_ERR(req->r_new_inode)) {
|
||||
err = PTR_ERR(req->r_new_inode);
|
||||
req->r_new_inode = NULL;
|
||||
goto out_req;
|
||||
}
|
||||
|
||||
req->r_parent = dir;
|
||||
ihold(dir);
|
||||
|
||||
if (IS_ENCRYPTED(req->r_new_inode)) {
|
||||
err = prep_encrypted_symlink_target(req, dest);
|
||||
if (err)
|
||||
goto out_req;
|
||||
} else {
|
||||
req->r_path2 = kstrdup(dest, GFP_KERNEL);
|
||||
if (!req->r_path2) {
|
||||
err = -ENOMEM;
|
||||
goto out_req;
|
||||
}
|
||||
}
|
||||
|
||||
set_bit(CEPH_MDS_R_PARENT_LOCKED, &req->r_req_flags);
|
||||
req->r_dentry = dget(dentry);
|
||||
req->r_num_caps = 2;
|
||||
req->r_dentry_drop = CEPH_CAP_FILE_SHARED | CEPH_CAP_AUTH_EXCL |
|
||||
CEPH_CAP_XATTR_EXCL;
|
||||
req->r_dentry_unless = CEPH_CAP_FILE_EXCL;
|
||||
if (as_ctx.pagelist) {
|
||||
req->r_pagelist = as_ctx.pagelist;
|
||||
as_ctx.pagelist = NULL;
|
||||
}
|
||||
|
||||
ceph_as_ctx_to_req(req, &as_ctx);
|
||||
|
||||
err = ceph_mdsc_do_request(mdsc, dir, req);
|
||||
if (!err && !req->r_reply_info.head->is_dentry)
|
||||
err = ceph_handle_notrace_create(dir, dentry);
|
||||
out_req:
|
||||
ceph_mdsc_put_request(req);
|
||||
out:
|
||||
if (err)
|
||||
@@ -1003,14 +1091,12 @@ static int ceph_mkdir(struct mnt_idmap *idmap, struct inode *dir,
|
||||
err = -EDQUOT;
|
||||
goto out;
|
||||
}
|
||||
if ((op == CEPH_MDS_OP_MKSNAP) && IS_ENCRYPTED(dir) &&
|
||||
!fscrypt_has_encryption_key(dir)) {
|
||||
err = -ENOKEY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
mode |= S_IFDIR;
|
||||
err = ceph_pre_init_acls(dir, &mode, &as_ctx);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
err = ceph_security_init_secctx(dentry, mode, &as_ctx);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
req = ceph_mdsc_create_request(mdsc, op, USE_AUTH_MDS);
|
||||
if (IS_ERR(req)) {
|
||||
@@ -1018,6 +1104,14 @@ static int ceph_mkdir(struct mnt_idmap *idmap, struct inode *dir,
|
||||
goto out;
|
||||
}
|
||||
|
||||
mode |= S_IFDIR;
|
||||
req->r_new_inode = ceph_new_inode(dir, dentry, &mode, &as_ctx);
|
||||
if (IS_ERR(req->r_new_inode)) {
|
||||
err = PTR_ERR(req->r_new_inode);
|
||||
req->r_new_inode = NULL;
|
||||
goto out_req;
|
||||
}
|
||||
|
||||
req->r_dentry = dget(dentry);
|
||||
req->r_num_caps = 2;
|
||||
req->r_parent = dir;
|
||||
@@ -1027,15 +1121,15 @@ static int ceph_mkdir(struct mnt_idmap *idmap, struct inode *dir,
|
||||
req->r_dentry_drop = CEPH_CAP_FILE_SHARED | CEPH_CAP_AUTH_EXCL |
|
||||
CEPH_CAP_XATTR_EXCL;
|
||||
req->r_dentry_unless = CEPH_CAP_FILE_EXCL;
|
||||
if (as_ctx.pagelist) {
|
||||
req->r_pagelist = as_ctx.pagelist;
|
||||
as_ctx.pagelist = NULL;
|
||||
}
|
||||
|
||||
ceph_as_ctx_to_req(req, &as_ctx);
|
||||
|
||||
err = ceph_mdsc_do_request(mdsc, dir, req);
|
||||
if (!err &&
|
||||
!req->r_reply_info.head->is_target &&
|
||||
!req->r_reply_info.head->is_dentry)
|
||||
err = ceph_handle_notrace_create(dir, dentry);
|
||||
out_req:
|
||||
ceph_mdsc_put_request(req);
|
||||
out:
|
||||
if (!err)
|
||||
@@ -1063,6 +1157,10 @@ static int ceph_link(struct dentry *old_dentry, struct inode *dir,
|
||||
if (ceph_snap(dir) != CEPH_NOSNAP)
|
||||
return -EROFS;
|
||||
|
||||
err = fscrypt_prepare_link(old_dentry, dir, dentry);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
dout("link in dir %p %llx.%llx old_dentry %p:'%pd' dentry %p:'%pd'\n",
|
||||
dir, ceph_vinop(dir), old_dentry, old_dentry, dentry, dentry);
|
||||
req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_LINK, USE_AUTH_MDS);
|
||||
@@ -1310,6 +1408,11 @@ static int ceph_rename(struct mnt_idmap *idmap, struct inode *old_dir,
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = fscrypt_prepare_rename(old_dir, old_dentry, new_dir, new_dentry,
|
||||
flags);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
dout("rename dir %p dentry %p to dir %p dentry %p\n",
|
||||
old_dir, old_dentry, new_dir, new_dentry);
|
||||
req = ceph_mdsc_create_request(mdsc, op, USE_AUTH_MDS);
|
||||
@@ -1765,6 +1868,10 @@ static int ceph_d_revalidate(struct dentry *dentry, unsigned int flags)
|
||||
struct inode *dir, *inode;
|
||||
struct ceph_mds_client *mdsc;
|
||||
|
||||
valid = fscrypt_d_revalidate(dentry, flags);
|
||||
if (valid <= 0)
|
||||
return valid;
|
||||
|
||||
if (flags & LOOKUP_RCU) {
|
||||
parent = READ_ONCE(dentry->d_parent);
|
||||
dir = d_inode_rcu(parent);
|
||||
@@ -1777,8 +1884,9 @@ static int ceph_d_revalidate(struct dentry *dentry, unsigned int flags)
|
||||
inode = d_inode(dentry);
|
||||
}
|
||||
|
||||
dout("d_revalidate %p '%pd' inode %p offset 0x%llx\n", dentry,
|
||||
dentry, inode, ceph_dentry(dentry)->offset);
|
||||
dout("d_revalidate %p '%pd' inode %p offset 0x%llx nokey %d\n", dentry,
|
||||
dentry, inode, ceph_dentry(dentry)->offset,
|
||||
!!(dentry->d_flags & DCACHE_NOKEY_NAME));
|
||||
|
||||
mdsc = ceph_sb_to_client(dir->i_sb)->mdsc;
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "super.h"
|
||||
#include "mds_client.h"
|
||||
#include "crypto.h"
|
||||
|
||||
/*
|
||||
* Basic fh
|
||||
@@ -535,7 +536,9 @@ static int ceph_get_name(struct dentry *parent, char *name,
|
||||
{
|
||||
struct ceph_mds_client *mdsc;
|
||||
struct ceph_mds_request *req;
|
||||
struct inode *dir = d_inode(parent);
|
||||
struct inode *inode = d_inode(child);
|
||||
struct ceph_mds_reply_info_parsed *rinfo;
|
||||
int err;
|
||||
|
||||
if (ceph_snap(inode) != CEPH_NOSNAP)
|
||||
@@ -547,30 +550,47 @@ static int ceph_get_name(struct dentry *parent, char *name,
|
||||
if (IS_ERR(req))
|
||||
return PTR_ERR(req);
|
||||
|
||||
inode_lock(d_inode(parent));
|
||||
|
||||
inode_lock(dir);
|
||||
req->r_inode = inode;
|
||||
ihold(inode);
|
||||
req->r_ino2 = ceph_vino(d_inode(parent));
|
||||
req->r_parent = d_inode(parent);
|
||||
ihold(req->r_parent);
|
||||
req->r_parent = dir;
|
||||
ihold(dir);
|
||||
set_bit(CEPH_MDS_R_PARENT_LOCKED, &req->r_req_flags);
|
||||
req->r_num_caps = 2;
|
||||
err = ceph_mdsc_do_request(mdsc, NULL, req);
|
||||
inode_unlock(dir);
|
||||
|
||||
inode_unlock(d_inode(parent));
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
if (!err) {
|
||||
struct ceph_mds_reply_info_parsed *rinfo = &req->r_reply_info;
|
||||
rinfo = &req->r_reply_info;
|
||||
if (!IS_ENCRYPTED(dir)) {
|
||||
memcpy(name, rinfo->dname, rinfo->dname_len);
|
||||
name[rinfo->dname_len] = 0;
|
||||
dout("get_name %p ino %llx.%llx name %s\n",
|
||||
child, ceph_vinop(inode), name);
|
||||
} else {
|
||||
dout("get_name %p ino %llx.%llx err %d\n",
|
||||
child, ceph_vinop(inode), err);
|
||||
}
|
||||
struct fscrypt_str oname = FSTR_INIT(NULL, 0);
|
||||
struct ceph_fname fname = { .dir = dir,
|
||||
.name = rinfo->dname,
|
||||
.ctext = rinfo->altname,
|
||||
.name_len = rinfo->dname_len,
|
||||
.ctext_len = rinfo->altname_len };
|
||||
|
||||
err = ceph_fname_alloc_buffer(dir, &oname);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
err = ceph_fname_to_usr(&fname, NULL, &oname, NULL);
|
||||
if (!err) {
|
||||
memcpy(name, oname.name, oname.len);
|
||||
name[oname.len] = 0;
|
||||
}
|
||||
ceph_fname_free_buffer(dir, &oname);
|
||||
}
|
||||
out:
|
||||
dout("get_name %p ino %llx.%llx err %d %s%s\n",
|
||||
child, ceph_vinop(inode), err,
|
||||
err ? "" : "name ", err ? "" : name);
|
||||
ceph_mdsc_put_request(req);
|
||||
return err;
|
||||
}
|
||||
|
||||
602
fs/ceph/file.c
602
fs/ceph/file.c
File diff suppressed because it is too large
Load Diff
625
fs/ceph/inode.c
625
fs/ceph/inode.c
File diff suppressed because it is too large
Load Diff
127
fs/ceph/ioctl.c
127
fs/ceph/ioctl.c
@@ -6,6 +6,7 @@
|
||||
#include "mds_client.h"
|
||||
#include "ioctl.h"
|
||||
#include <linux/ceph/striper.h>
|
||||
#include <linux/fscrypt.h>
|
||||
|
||||
/*
|
||||
* ioctls
|
||||
@@ -268,9 +269,96 @@ static long ceph_ioctl_syncio(struct file *file)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vet_mds_for_fscrypt(struct file *file)
|
||||
{
|
||||
int i, ret = -EOPNOTSUPP;
|
||||
struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(file_inode(file)->i_sb);
|
||||
|
||||
mutex_lock(&mdsc->mutex);
|
||||
for (i = 0; i < mdsc->max_sessions; i++) {
|
||||
struct ceph_mds_session *s = mdsc->sessions[i];
|
||||
|
||||
if (!s)
|
||||
continue;
|
||||
if (test_bit(CEPHFS_FEATURE_ALTERNATE_NAME, &s->s_features))
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&mdsc->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static long ceph_set_encryption_policy(struct file *file, unsigned long arg)
|
||||
{
|
||||
int ret, got = 0;
|
||||
struct inode *inode = file_inode(file);
|
||||
struct ceph_inode_info *ci = ceph_inode(inode);
|
||||
|
||||
/* encrypted directories can't have striped layout */
|
||||
if (ci->i_layout.stripe_count > 1)
|
||||
return -EINVAL;
|
||||
|
||||
ret = vet_mds_for_fscrypt(file);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Ensure we hold these caps so that we _know_ that the rstats check
|
||||
* in the empty_dir check is reliable.
|
||||
*/
|
||||
ret = ceph_get_caps(file, CEPH_CAP_FILE_SHARED, 0, -1, &got);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = fscrypt_ioctl_set_policy(file, (const void __user *)arg);
|
||||
if (got)
|
||||
ceph_put_cap_refs(ci, got);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const char *ceph_ioctl_cmd_name(const unsigned int cmd)
|
||||
{
|
||||
switch (cmd) {
|
||||
case CEPH_IOC_GET_LAYOUT:
|
||||
return "get_layout";
|
||||
case CEPH_IOC_SET_LAYOUT:
|
||||
return "set_layout";
|
||||
case CEPH_IOC_SET_LAYOUT_POLICY:
|
||||
return "set_layout_policy";
|
||||
case CEPH_IOC_GET_DATALOC:
|
||||
return "get_dataloc";
|
||||
case CEPH_IOC_LAZYIO:
|
||||
return "lazyio";
|
||||
case CEPH_IOC_SYNCIO:
|
||||
return "syncio";
|
||||
case FS_IOC_SET_ENCRYPTION_POLICY:
|
||||
return "set_encryption_policy";
|
||||
case FS_IOC_GET_ENCRYPTION_POLICY:
|
||||
return "get_encryption_policy";
|
||||
case FS_IOC_GET_ENCRYPTION_POLICY_EX:
|
||||
return "get_encryption_policy_ex";
|
||||
case FS_IOC_ADD_ENCRYPTION_KEY:
|
||||
return "add_encryption_key";
|
||||
case FS_IOC_REMOVE_ENCRYPTION_KEY:
|
||||
return "remove_encryption_key";
|
||||
case FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS:
|
||||
return "remove_encryption_key_all_users";
|
||||
case FS_IOC_GET_ENCRYPTION_KEY_STATUS:
|
||||
return "get_encryption_key_status";
|
||||
case FS_IOC_GET_ENCRYPTION_NONCE:
|
||||
return "get_encryption_nonce";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
long ceph_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
dout("ioctl file %p cmd %u arg %lu\n", file, cmd, arg);
|
||||
int ret;
|
||||
|
||||
dout("ioctl file %p cmd %s arg %lu\n", file,
|
||||
ceph_ioctl_cmd_name(cmd), arg);
|
||||
switch (cmd) {
|
||||
case CEPH_IOC_GET_LAYOUT:
|
||||
return ceph_ioctl_get_layout(file, (void __user *)arg);
|
||||
@@ -289,6 +377,43 @@ long ceph_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
|
||||
case CEPH_IOC_SYNCIO:
|
||||
return ceph_ioctl_syncio(file);
|
||||
|
||||
case FS_IOC_SET_ENCRYPTION_POLICY:
|
||||
return ceph_set_encryption_policy(file, arg);
|
||||
|
||||
case FS_IOC_GET_ENCRYPTION_POLICY:
|
||||
ret = vet_mds_for_fscrypt(file);
|
||||
if (ret)
|
||||
return ret;
|
||||
return fscrypt_ioctl_get_policy(file, (void __user *)arg);
|
||||
|
||||
case FS_IOC_GET_ENCRYPTION_POLICY_EX:
|
||||
ret = vet_mds_for_fscrypt(file);
|
||||
if (ret)
|
||||
return ret;
|
||||
return fscrypt_ioctl_get_policy_ex(file, (void __user *)arg);
|
||||
|
||||
case FS_IOC_ADD_ENCRYPTION_KEY:
|
||||
ret = vet_mds_for_fscrypt(file);
|
||||
if (ret)
|
||||
return ret;
|
||||
return fscrypt_ioctl_add_key(file, (void __user *)arg);
|
||||
|
||||
case FS_IOC_REMOVE_ENCRYPTION_KEY:
|
||||
return fscrypt_ioctl_remove_key(file, (void __user *)arg);
|
||||
|
||||
case FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS:
|
||||
return fscrypt_ioctl_remove_key_all_users(file,
|
||||
(void __user *)arg);
|
||||
|
||||
case FS_IOC_GET_ENCRYPTION_KEY_STATUS:
|
||||
return fscrypt_ioctl_get_key_status(file, (void __user *)arg);
|
||||
|
||||
case FS_IOC_GET_ENCRYPTION_NONCE:
|
||||
ret = vet_mds_for_fscrypt(file);
|
||||
if (ret)
|
||||
return ret;
|
||||
return fscrypt_ioctl_get_nonce(file, (void __user *)arg);
|
||||
}
|
||||
|
||||
return -ENOTTY;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -32,8 +32,9 @@ enum ceph_feature_type {
|
||||
CEPHFS_FEATURE_ALTERNATE_NAME,
|
||||
CEPHFS_FEATURE_NOTIFY_SESSION_STATE,
|
||||
CEPHFS_FEATURE_OP_GETVXATTR,
|
||||
CEPHFS_FEATURE_32BITS_RETRY_FWD,
|
||||
|
||||
CEPHFS_FEATURE_MAX = CEPHFS_FEATURE_OP_GETVXATTR,
|
||||
CEPHFS_FEATURE_MAX = CEPHFS_FEATURE_32BITS_RETRY_FWD,
|
||||
};
|
||||
|
||||
#define CEPHFS_FEATURES_CLIENT_SUPPORTED { \
|
||||
@@ -44,8 +45,10 @@ enum ceph_feature_type {
|
||||
CEPHFS_FEATURE_MULTI_RECONNECT, \
|
||||
CEPHFS_FEATURE_DELEG_INO, \
|
||||
CEPHFS_FEATURE_METRIC_COLLECT, \
|
||||
CEPHFS_FEATURE_ALTERNATE_NAME, \
|
||||
CEPHFS_FEATURE_NOTIFY_SESSION_STATE, \
|
||||
CEPHFS_FEATURE_OP_GETVXATTR, \
|
||||
CEPHFS_FEATURE_32BITS_RETRY_FWD, \
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -86,13 +89,19 @@ struct ceph_mds_reply_info_in {
|
||||
s32 dir_pin;
|
||||
struct ceph_timespec btime;
|
||||
struct ceph_timespec snap_btime;
|
||||
u8 *fscrypt_auth;
|
||||
u8 *fscrypt_file;
|
||||
u32 fscrypt_auth_len;
|
||||
u32 fscrypt_file_len;
|
||||
u64 rsnaps;
|
||||
u64 change_attr;
|
||||
};
|
||||
|
||||
struct ceph_mds_reply_dir_entry {
|
||||
bool is_nokey;
|
||||
char *name;
|
||||
u32 name_len;
|
||||
u32 raw_hash;
|
||||
struct ceph_mds_reply_lease *lease;
|
||||
struct ceph_mds_reply_info_in inode;
|
||||
loff_t offset;
|
||||
@@ -116,7 +125,9 @@ struct ceph_mds_reply_info_parsed {
|
||||
struct ceph_mds_reply_info_in diri, targeti;
|
||||
struct ceph_mds_reply_dirfrag *dirfrag;
|
||||
char *dname;
|
||||
u8 *altname;
|
||||
u32 dname_len;
|
||||
u32 altname_len;
|
||||
struct ceph_mds_reply_lease *dlease;
|
||||
struct ceph_mds_reply_xattr xattr_info;
|
||||
|
||||
@@ -263,6 +274,7 @@ struct ceph_mds_request {
|
||||
|
||||
struct inode *r_parent; /* parent dir inode */
|
||||
struct inode *r_target_inode; /* resulting inode */
|
||||
struct inode *r_new_inode; /* new inode (for creates) */
|
||||
|
||||
#define CEPH_MDS_R_DIRECT_IS_HASH (1) /* r_direct_hash is valid */
|
||||
#define CEPH_MDS_R_ABORTED (2) /* call was aborted */
|
||||
@@ -272,11 +284,19 @@ struct ceph_mds_request {
|
||||
#define CEPH_MDS_R_DID_PREPOPULATE (6) /* prepopulated readdir */
|
||||
#define CEPH_MDS_R_PARENT_LOCKED (7) /* is r_parent->i_rwsem wlocked? */
|
||||
#define CEPH_MDS_R_ASYNC (8) /* async request */
|
||||
#define CEPH_MDS_R_FSCRYPT_FILE (9) /* must marshal fscrypt_file field */
|
||||
unsigned long r_req_flags;
|
||||
|
||||
struct mutex r_fill_mutex;
|
||||
|
||||
union ceph_mds_request_args r_args;
|
||||
|
||||
struct ceph_fscrypt_auth *r_fscrypt_auth;
|
||||
u64 r_fscrypt_file;
|
||||
|
||||
u8 *r_altname; /* fscrypt binary crypttext for long filenames */
|
||||
u32 r_altname_len; /* length of r_altname */
|
||||
|
||||
int r_fmode; /* file mode, if expecting cap */
|
||||
int r_request_release_offset;
|
||||
const struct cred *r_cred;
|
||||
@@ -381,8 +401,9 @@ struct cap_wait {
|
||||
};
|
||||
|
||||
enum {
|
||||
CEPH_MDSC_STOPPING_BEGIN = 1,
|
||||
CEPH_MDSC_STOPPING_FLUSHED = 2,
|
||||
CEPH_MDSC_STOPPING_BEGIN = 1,
|
||||
CEPH_MDSC_STOPPING_FLUSHING = 2,
|
||||
CEPH_MDSC_STOPPING_FLUSHED = 3,
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -401,7 +422,11 @@ struct ceph_mds_client {
|
||||
struct ceph_mds_session **sessions; /* NULL for mds if no session */
|
||||
atomic_t num_sessions;
|
||||
int max_sessions; /* len of sessions array */
|
||||
int stopping; /* true if shutting down */
|
||||
|
||||
spinlock_t stopping_lock; /* protect snap_empty */
|
||||
int stopping; /* the stage of shutting down */
|
||||
atomic_t stopping_blockers;
|
||||
struct completion stopping_waiter;
|
||||
|
||||
atomic64_t quotarealms_count; /* # realms with quota */
|
||||
/*
|
||||
@@ -557,7 +582,7 @@ static inline void ceph_mdsc_free_path(char *path, int len)
|
||||
}
|
||||
|
||||
extern char *ceph_mdsc_build_path(struct dentry *dentry, int *plen, u64 *base,
|
||||
int stop_on_nosnap);
|
||||
int for_wire);
|
||||
|
||||
extern void __ceph_mdsc_drop_dentry_lease(struct dentry *dentry);
|
||||
extern void ceph_mdsc_lease_send_msg(struct ceph_mds_session *session,
|
||||
|
||||
@@ -47,25 +47,23 @@ void ceph_handle_quota(struct ceph_mds_client *mdsc,
|
||||
struct inode *inode;
|
||||
struct ceph_inode_info *ci;
|
||||
|
||||
if (!ceph_inc_mds_stopping_blocker(mdsc, session))
|
||||
return;
|
||||
|
||||
if (msg->front.iov_len < sizeof(*h)) {
|
||||
pr_err("%s corrupt message mds%d len %d\n", __func__,
|
||||
session->s_mds, (int)msg->front.iov_len);
|
||||
ceph_msg_dump(msg);
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* increment msg sequence number */
|
||||
mutex_lock(&session->s_mutex);
|
||||
inc_session_sequence(session);
|
||||
mutex_unlock(&session->s_mutex);
|
||||
|
||||
/* lookup inode */
|
||||
vino.ino = le64_to_cpu(h->ino);
|
||||
vino.snap = CEPH_NOSNAP;
|
||||
inode = ceph_find_inode(sb, vino);
|
||||
if (!inode) {
|
||||
pr_warn("Failed to find inode %llu\n", vino.ino);
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
ci = ceph_inode(inode);
|
||||
|
||||
@@ -78,6 +76,8 @@ void ceph_handle_quota(struct ceph_mds_client *mdsc,
|
||||
spin_unlock(&ci->i_ceph_lock);
|
||||
|
||||
iput(inode);
|
||||
out:
|
||||
ceph_dec_mds_stopping_blocker(mdsc);
|
||||
}
|
||||
|
||||
static struct ceph_quotarealm_inode *
|
||||
|
||||
@@ -1015,6 +1015,9 @@ void ceph_handle_snap(struct ceph_mds_client *mdsc,
|
||||
int locked_rwsem = 0;
|
||||
bool close_sessions = false;
|
||||
|
||||
if (!ceph_inc_mds_stopping_blocker(mdsc, session))
|
||||
return;
|
||||
|
||||
/* decode */
|
||||
if (msg->front.iov_len < sizeof(*h))
|
||||
goto bad;
|
||||
@@ -1030,10 +1033,6 @@ void ceph_handle_snap(struct ceph_mds_client *mdsc,
|
||||
dout("%s from mds%d op %s split %llx tracelen %d\n", __func__,
|
||||
mds, ceph_snap_op_name(op), split, trace_len);
|
||||
|
||||
mutex_lock(&session->s_mutex);
|
||||
inc_session_sequence(session);
|
||||
mutex_unlock(&session->s_mutex);
|
||||
|
||||
down_write(&mdsc->snap_rwsem);
|
||||
locked_rwsem = 1;
|
||||
|
||||
@@ -1151,6 +1150,7 @@ skip_inode:
|
||||
up_write(&mdsc->snap_rwsem);
|
||||
|
||||
flush_snaps(mdsc);
|
||||
ceph_dec_mds_stopping_blocker(mdsc);
|
||||
return;
|
||||
|
||||
bad:
|
||||
@@ -1160,6 +1160,8 @@ out:
|
||||
if (locked_rwsem)
|
||||
up_write(&mdsc->snap_rwsem);
|
||||
|
||||
ceph_dec_mds_stopping_blocker(mdsc);
|
||||
|
||||
if (close_sessions)
|
||||
ceph_mdsc_close_sessions(mdsc);
|
||||
return;
|
||||
|
||||
191
fs/ceph/super.c
191
fs/ceph/super.c
@@ -20,6 +20,7 @@
|
||||
#include "super.h"
|
||||
#include "mds_client.h"
|
||||
#include "cache.h"
|
||||
#include "crypto.h"
|
||||
|
||||
#include <linux/ceph/ceph_features.h>
|
||||
#include <linux/ceph/decode.h>
|
||||
@@ -46,6 +47,7 @@ static void ceph_put_super(struct super_block *s)
|
||||
struct ceph_fs_client *fsc = ceph_sb_to_client(s);
|
||||
|
||||
dout("put_super\n");
|
||||
ceph_fscrypt_free_dummy_policy(fsc);
|
||||
ceph_mdsc_close_sessions(fsc->mdsc);
|
||||
}
|
||||
|
||||
@@ -151,6 +153,7 @@ enum {
|
||||
Opt_recover_session,
|
||||
Opt_source,
|
||||
Opt_mon_addr,
|
||||
Opt_test_dummy_encryption,
|
||||
/* string args above */
|
||||
Opt_dirstat,
|
||||
Opt_rbytes,
|
||||
@@ -165,6 +168,7 @@ enum {
|
||||
Opt_copyfrom,
|
||||
Opt_wsync,
|
||||
Opt_pagecache,
|
||||
Opt_sparseread,
|
||||
};
|
||||
|
||||
enum ceph_recover_session_mode {
|
||||
@@ -192,6 +196,7 @@ static const struct fs_parameter_spec ceph_mount_parameters[] = {
|
||||
fsparam_string ("fsc", Opt_fscache), // fsc=...
|
||||
fsparam_flag_no ("ino32", Opt_ino32),
|
||||
fsparam_string ("mds_namespace", Opt_mds_namespace),
|
||||
fsparam_string ("mon_addr", Opt_mon_addr),
|
||||
fsparam_flag_no ("poolperm", Opt_poolperm),
|
||||
fsparam_flag_no ("quotadf", Opt_quotadf),
|
||||
fsparam_u32 ("rasize", Opt_rasize),
|
||||
@@ -203,10 +208,12 @@ static const struct fs_parameter_spec ceph_mount_parameters[] = {
|
||||
fsparam_u32 ("rsize", Opt_rsize),
|
||||
fsparam_string ("snapdirname", Opt_snapdirname),
|
||||
fsparam_string ("source", Opt_source),
|
||||
fsparam_string ("mon_addr", Opt_mon_addr),
|
||||
fsparam_flag ("test_dummy_encryption", Opt_test_dummy_encryption),
|
||||
fsparam_string ("test_dummy_encryption", Opt_test_dummy_encryption),
|
||||
fsparam_u32 ("wsize", Opt_wsize),
|
||||
fsparam_flag_no ("wsync", Opt_wsync),
|
||||
fsparam_flag_no ("pagecache", Opt_pagecache),
|
||||
fsparam_flag_no ("sparseread", Opt_sparseread),
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -576,6 +583,29 @@ static int ceph_parse_mount_param(struct fs_context *fc,
|
||||
else
|
||||
fsopt->flags &= ~CEPH_MOUNT_OPT_NOPAGECACHE;
|
||||
break;
|
||||
case Opt_sparseread:
|
||||
if (result.negated)
|
||||
fsopt->flags &= ~CEPH_MOUNT_OPT_SPARSEREAD;
|
||||
else
|
||||
fsopt->flags |= CEPH_MOUNT_OPT_SPARSEREAD;
|
||||
break;
|
||||
case Opt_test_dummy_encryption:
|
||||
#ifdef CONFIG_FS_ENCRYPTION
|
||||
fscrypt_free_dummy_policy(&fsopt->dummy_enc_policy);
|
||||
ret = fscrypt_parse_test_dummy_encryption(param,
|
||||
&fsopt->dummy_enc_policy);
|
||||
if (ret == -EINVAL) {
|
||||
warnfc(fc, "Value of option \"%s\" is unrecognized",
|
||||
param->key);
|
||||
} else if (ret == -EEXIST) {
|
||||
warnfc(fc, "Conflicting test_dummy_encryption options");
|
||||
ret = -EINVAL;
|
||||
}
|
||||
#else
|
||||
warnfc(fc,
|
||||
"FS encryption not supported: test_dummy_encryption mount option ignored");
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
@@ -596,6 +626,7 @@ static void destroy_mount_options(struct ceph_mount_options *args)
|
||||
kfree(args->server_path);
|
||||
kfree(args->fscache_uniq);
|
||||
kfree(args->mon_addr);
|
||||
fscrypt_free_dummy_policy(&args->dummy_enc_policy);
|
||||
kfree(args);
|
||||
}
|
||||
|
||||
@@ -710,9 +741,12 @@ static int ceph_show_options(struct seq_file *m, struct dentry *root)
|
||||
|
||||
if (!(fsopt->flags & CEPH_MOUNT_OPT_ASYNC_DIROPS))
|
||||
seq_puts(m, ",wsync");
|
||||
|
||||
if (fsopt->flags & CEPH_MOUNT_OPT_NOPAGECACHE)
|
||||
seq_puts(m, ",nopagecache");
|
||||
if (fsopt->flags & CEPH_MOUNT_OPT_SPARSEREAD)
|
||||
seq_puts(m, ",sparseread");
|
||||
|
||||
fscrypt_show_test_dummy_encryption(m, ',', root->d_sb);
|
||||
|
||||
if (fsopt->wsize != CEPH_MAX_WRITE_SIZE)
|
||||
seq_printf(m, ",wsize=%u", fsopt->wsize);
|
||||
@@ -1052,6 +1086,50 @@ out:
|
||||
return root;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_FS_ENCRYPTION
|
||||
static int ceph_apply_test_dummy_encryption(struct super_block *sb,
|
||||
struct fs_context *fc,
|
||||
struct ceph_mount_options *fsopt)
|
||||
{
|
||||
struct ceph_fs_client *fsc = sb->s_fs_info;
|
||||
|
||||
if (!fscrypt_is_dummy_policy_set(&fsopt->dummy_enc_policy))
|
||||
return 0;
|
||||
|
||||
/* No changing encryption context on remount. */
|
||||
if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE &&
|
||||
!fscrypt_is_dummy_policy_set(&fsc->fsc_dummy_enc_policy)) {
|
||||
if (fscrypt_dummy_policies_equal(&fsopt->dummy_enc_policy,
|
||||
&fsc->fsc_dummy_enc_policy))
|
||||
return 0;
|
||||
errorfc(fc, "Can't set test_dummy_encryption on remount");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Also make sure fsopt doesn't contain a conflicting value. */
|
||||
if (fscrypt_is_dummy_policy_set(&fsc->fsc_dummy_enc_policy)) {
|
||||
if (fscrypt_dummy_policies_equal(&fsopt->dummy_enc_policy,
|
||||
&fsc->fsc_dummy_enc_policy))
|
||||
return 0;
|
||||
errorfc(fc, "Conflicting test_dummy_encryption options");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
fsc->fsc_dummy_enc_policy = fsopt->dummy_enc_policy;
|
||||
memset(&fsopt->dummy_enc_policy, 0, sizeof(fsopt->dummy_enc_policy));
|
||||
|
||||
warnfc(fc, "test_dummy_encryption mode enabled");
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static int ceph_apply_test_dummy_encryption(struct super_block *sb,
|
||||
struct fs_context *fc,
|
||||
struct ceph_mount_options *fsopt)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* mount: join the ceph cluster, and open root directory.
|
||||
*/
|
||||
@@ -1080,6 +1158,11 @@ static struct dentry *ceph_real_mount(struct ceph_fs_client *fsc,
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = ceph_apply_test_dummy_encryption(fsc->sb, fc,
|
||||
fsc->mount_options);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
dout("mount opening path '%s'\n", path);
|
||||
|
||||
ceph_fs_debugfs_init(fsc);
|
||||
@@ -1101,6 +1184,7 @@ static struct dentry *ceph_real_mount(struct ceph_fs_client *fsc,
|
||||
|
||||
out:
|
||||
mutex_unlock(&fsc->client->mount_mutex);
|
||||
ceph_fscrypt_free_dummy_policy(fsc);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
@@ -1126,6 +1210,8 @@ static int ceph_set_super(struct super_block *s, struct fs_context *fc)
|
||||
s->s_time_max = U32_MAX;
|
||||
s->s_flags |= SB_NODIRATIME | SB_NOATIME;
|
||||
|
||||
ceph_fscrypt_set_ops(s);
|
||||
|
||||
ret = set_anon_super_fc(s, fc);
|
||||
if (ret != 0)
|
||||
fsc->sb = NULL;
|
||||
@@ -1287,15 +1373,26 @@ static void ceph_free_fc(struct fs_context *fc)
|
||||
|
||||
static int ceph_reconfigure_fc(struct fs_context *fc)
|
||||
{
|
||||
int err;
|
||||
struct ceph_parse_opts_ctx *pctx = fc->fs_private;
|
||||
struct ceph_mount_options *fsopt = pctx->opts;
|
||||
struct ceph_fs_client *fsc = ceph_sb_to_client(fc->root->d_sb);
|
||||
struct super_block *sb = fc->root->d_sb;
|
||||
struct ceph_fs_client *fsc = ceph_sb_to_client(sb);
|
||||
|
||||
err = ceph_apply_test_dummy_encryption(sb, fc, fsopt);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (fsopt->flags & CEPH_MOUNT_OPT_ASYNC_DIROPS)
|
||||
ceph_set_mount_opt(fsc, ASYNC_DIROPS);
|
||||
else
|
||||
ceph_clear_mount_opt(fsc, ASYNC_DIROPS);
|
||||
|
||||
if (fsopt->flags & CEPH_MOUNT_OPT_SPARSEREAD)
|
||||
ceph_set_mount_opt(fsc, SPARSEREAD);
|
||||
else
|
||||
ceph_clear_mount_opt(fsc, SPARSEREAD);
|
||||
|
||||
if (strcmp_null(fsc->mount_options->mon_addr, fsopt->mon_addr)) {
|
||||
kfree(fsc->mount_options->mon_addr);
|
||||
fsc->mount_options->mon_addr = fsopt->mon_addr;
|
||||
@@ -1303,7 +1400,7 @@ static int ceph_reconfigure_fc(struct fs_context *fc)
|
||||
pr_notice("ceph: monitor addresses recorded, but not used for reconnection");
|
||||
}
|
||||
|
||||
sync_filesystem(fc->root->d_sb);
|
||||
sync_filesystem(sb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1365,25 +1462,101 @@ nomem:
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return true if it successfully increases the blocker counter,
|
||||
* or false if the mdsc is in stopping and flushed state.
|
||||
*/
|
||||
static bool __inc_stopping_blocker(struct ceph_mds_client *mdsc)
|
||||
{
|
||||
spin_lock(&mdsc->stopping_lock);
|
||||
if (mdsc->stopping >= CEPH_MDSC_STOPPING_FLUSHING) {
|
||||
spin_unlock(&mdsc->stopping_lock);
|
||||
return false;
|
||||
}
|
||||
atomic_inc(&mdsc->stopping_blockers);
|
||||
spin_unlock(&mdsc->stopping_lock);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __dec_stopping_blocker(struct ceph_mds_client *mdsc)
|
||||
{
|
||||
spin_lock(&mdsc->stopping_lock);
|
||||
if (!atomic_dec_return(&mdsc->stopping_blockers) &&
|
||||
mdsc->stopping >= CEPH_MDSC_STOPPING_FLUSHING)
|
||||
complete_all(&mdsc->stopping_waiter);
|
||||
spin_unlock(&mdsc->stopping_lock);
|
||||
}
|
||||
|
||||
/* For metadata IO requests */
|
||||
bool ceph_inc_mds_stopping_blocker(struct ceph_mds_client *mdsc,
|
||||
struct ceph_mds_session *session)
|
||||
{
|
||||
mutex_lock(&session->s_mutex);
|
||||
inc_session_sequence(session);
|
||||
mutex_unlock(&session->s_mutex);
|
||||
|
||||
return __inc_stopping_blocker(mdsc);
|
||||
}
|
||||
|
||||
void ceph_dec_mds_stopping_blocker(struct ceph_mds_client *mdsc)
|
||||
{
|
||||
__dec_stopping_blocker(mdsc);
|
||||
}
|
||||
|
||||
/* For data IO requests */
|
||||
bool ceph_inc_osd_stopping_blocker(struct ceph_mds_client *mdsc)
|
||||
{
|
||||
return __inc_stopping_blocker(mdsc);
|
||||
}
|
||||
|
||||
void ceph_dec_osd_stopping_blocker(struct ceph_mds_client *mdsc)
|
||||
{
|
||||
__dec_stopping_blocker(mdsc);
|
||||
}
|
||||
|
||||
static void ceph_kill_sb(struct super_block *s)
|
||||
{
|
||||
struct ceph_fs_client *fsc = ceph_sb_to_client(s);
|
||||
struct ceph_mds_client *mdsc = fsc->mdsc;
|
||||
bool wait;
|
||||
|
||||
dout("kill_sb %p\n", s);
|
||||
|
||||
ceph_mdsc_pre_umount(fsc->mdsc);
|
||||
ceph_mdsc_pre_umount(mdsc);
|
||||
flush_fs_workqueues(fsc);
|
||||
|
||||
/*
|
||||
* Though the kill_anon_super() will finally trigger the
|
||||
* sync_filesystem() anyway, we still need to do it here
|
||||
* and then bump the stage of shutdown to stop the work
|
||||
* queue as earlier as possible.
|
||||
* sync_filesystem() anyway, we still need to do it here and
|
||||
* then bump the stage of shutdown. This will allow us to
|
||||
* drop any further message, which will increase the inodes'
|
||||
* i_count reference counters but makes no sense any more,
|
||||
* from MDSs.
|
||||
*
|
||||
* Without this when evicting the inodes it may fail in the
|
||||
* kill_anon_super(), which will trigger a warning when
|
||||
* destroying the fscrypt keyring and then possibly trigger
|
||||
* a further crash in ceph module when the iput() tries to
|
||||
* evict the inodes later.
|
||||
*/
|
||||
sync_filesystem(s);
|
||||
|
||||
fsc->mdsc->stopping = CEPH_MDSC_STOPPING_FLUSHED;
|
||||
spin_lock(&mdsc->stopping_lock);
|
||||
mdsc->stopping = CEPH_MDSC_STOPPING_FLUSHING;
|
||||
wait = !!atomic_read(&mdsc->stopping_blockers);
|
||||
spin_unlock(&mdsc->stopping_lock);
|
||||
|
||||
if (wait && atomic_read(&mdsc->stopping_blockers)) {
|
||||
long timeleft = wait_for_completion_killable_timeout(
|
||||
&mdsc->stopping_waiter,
|
||||
fsc->client->options->mount_timeout);
|
||||
if (!timeleft) /* timed out */
|
||||
pr_warn("umount timed out, %ld\n", timeleft);
|
||||
else if (timeleft < 0) /* killed */
|
||||
pr_warn("umount was killed, %ld\n", timeleft);
|
||||
}
|
||||
|
||||
mdsc->stopping = CEPH_MDSC_STOPPING_FLUSHED;
|
||||
kill_anon_super(s);
|
||||
|
||||
fsc->client->extra_mon_dispatch = NULL;
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <linux/hashtable.h>
|
||||
|
||||
#include <linux/ceph/libceph.h>
|
||||
#include "crypto.h"
|
||||
|
||||
/* large granularity for statfs utilization stats to facilitate
|
||||
* large volume sizes on 32-bit machines. */
|
||||
@@ -42,6 +43,7 @@
|
||||
#define CEPH_MOUNT_OPT_NOCOPYFROM (1<<14) /* don't use RADOS 'copy-from' op */
|
||||
#define CEPH_MOUNT_OPT_ASYNC_DIROPS (1<<15) /* allow async directory ops */
|
||||
#define CEPH_MOUNT_OPT_NOPAGECACHE (1<<16) /* bypass pagecache altogether */
|
||||
#define CEPH_MOUNT_OPT_SPARSEREAD (1<<17) /* always do sparse reads */
|
||||
|
||||
#define CEPH_MOUNT_OPT_DEFAULT \
|
||||
(CEPH_MOUNT_OPT_DCACHE | \
|
||||
@@ -98,6 +100,7 @@ struct ceph_mount_options {
|
||||
char *server_path; /* default NULL (means "/") */
|
||||
char *fscache_uniq; /* default NULL */
|
||||
char *mon_addr;
|
||||
struct fscrypt_dummy_policy dummy_enc_policy;
|
||||
};
|
||||
|
||||
/* mount state */
|
||||
@@ -154,9 +157,11 @@ struct ceph_fs_client {
|
||||
#ifdef CONFIG_CEPH_FSCACHE
|
||||
struct fscache_volume *fscache;
|
||||
#endif
|
||||
#ifdef CONFIG_FS_ENCRYPTION
|
||||
struct fscrypt_dummy_policy fsc_dummy_enc_policy;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* File i/o capability. This tracks shared state with the metadata
|
||||
* server that allows us to cache or writeback attributes or to read
|
||||
@@ -419,6 +424,11 @@ struct ceph_inode_info {
|
||||
u32 i_truncate_seq; /* last truncate to smaller size */
|
||||
u64 i_truncate_size; /* and the size we last truncated down to */
|
||||
int i_truncate_pending; /* still need to call vmtruncate */
|
||||
/*
|
||||
* For none fscrypt case it equals to i_truncate_size or it will
|
||||
* equals to fscrypt_file_size
|
||||
*/
|
||||
u64 i_truncate_pagecache_size;
|
||||
|
||||
u64 i_max_size; /* max file size authorized by mds */
|
||||
u64 i_reported_size; /* (max_)size reported to or requested of mds */
|
||||
@@ -449,6 +459,13 @@ struct ceph_inode_info {
|
||||
|
||||
struct work_struct i_work;
|
||||
unsigned long i_work_mask;
|
||||
|
||||
#ifdef CONFIG_FS_ENCRYPTION
|
||||
u32 fscrypt_auth_len;
|
||||
u32 fscrypt_file_len;
|
||||
u8 *fscrypt_auth;
|
||||
u8 *fscrypt_file;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct ceph_netfs_request_data {
|
||||
@@ -998,6 +1015,7 @@ static inline bool __ceph_have_pending_cap_snap(struct ceph_inode_info *ci)
|
||||
/* inode.c */
|
||||
struct ceph_mds_reply_info_in;
|
||||
struct ceph_mds_reply_dirfrag;
|
||||
struct ceph_acl_sec_ctx;
|
||||
|
||||
extern const struct inode_operations ceph_file_iops;
|
||||
|
||||
@@ -1005,8 +1023,14 @@ extern struct inode *ceph_alloc_inode(struct super_block *sb);
|
||||
extern void ceph_evict_inode(struct inode *inode);
|
||||
extern void ceph_free_inode(struct inode *inode);
|
||||
|
||||
struct inode *ceph_new_inode(struct inode *dir, struct dentry *dentry,
|
||||
umode_t *mode, struct ceph_acl_sec_ctx *as_ctx);
|
||||
void ceph_as_ctx_to_req(struct ceph_mds_request *req,
|
||||
struct ceph_acl_sec_ctx *as_ctx);
|
||||
|
||||
extern struct inode *ceph_get_inode(struct super_block *sb,
|
||||
struct ceph_vino vino);
|
||||
struct ceph_vino vino,
|
||||
struct inode *newino);
|
||||
extern struct inode *ceph_get_snapdir(struct inode *parent);
|
||||
extern int ceph_fill_file_size(struct inode *inode, int issued,
|
||||
u32 truncate_seq, u64 truncate_size, u64 size);
|
||||
@@ -1065,7 +1089,13 @@ static inline int ceph_do_getattr(struct inode *inode, int mask, bool force)
|
||||
}
|
||||
extern int ceph_permission(struct mnt_idmap *idmap,
|
||||
struct inode *inode, int mask);
|
||||
extern int __ceph_setattr(struct inode *inode, struct iattr *attr);
|
||||
|
||||
struct ceph_iattr {
|
||||
struct ceph_fscrypt_auth *fscrypt_auth;
|
||||
};
|
||||
|
||||
extern int __ceph_setattr(struct inode *inode, struct iattr *attr,
|
||||
struct ceph_iattr *cia);
|
||||
extern int ceph_setattr(struct mnt_idmap *idmap,
|
||||
struct dentry *dentry, struct iattr *attr);
|
||||
extern int ceph_getattr(struct mnt_idmap *idmap,
|
||||
@@ -1099,6 +1129,9 @@ struct ceph_acl_sec_ctx {
|
||||
#ifdef CONFIG_CEPH_FS_SECURITY_LABEL
|
||||
void *sec_ctx;
|
||||
u32 sec_ctxlen;
|
||||
#endif
|
||||
#ifdef CONFIG_FS_ENCRYPTION
|
||||
struct ceph_fscrypt_auth *fscrypt_auth;
|
||||
#endif
|
||||
struct ceph_pagelist *pagelist;
|
||||
};
|
||||
@@ -1237,6 +1270,8 @@ extern int ceph_encode_dentry_release(void **p, struct dentry *dn,
|
||||
struct inode *dir,
|
||||
int mds, int drop, int unless);
|
||||
|
||||
extern int __ceph_get_caps(struct inode *inode, struct ceph_file_info *fi,
|
||||
int need, int want, loff_t endoff, int *got);
|
||||
extern int ceph_get_caps(struct file *filp, int need, int want,
|
||||
loff_t endoff, int *got);
|
||||
extern int ceph_try_get_caps(struct inode *inode,
|
||||
@@ -1272,6 +1307,9 @@ extern int ceph_renew_caps(struct inode *inode, int fmode);
|
||||
extern int ceph_open(struct inode *inode, struct file *file);
|
||||
extern int ceph_atomic_open(struct inode *dir, struct dentry *dentry,
|
||||
struct file *file, unsigned flags, umode_t mode);
|
||||
extern ssize_t __ceph_sync_read(struct inode *inode, loff_t *ki_pos,
|
||||
struct iov_iter *to, int *retry_op,
|
||||
u64 *last_objver);
|
||||
extern int ceph_release(struct inode *inode, struct file *filp);
|
||||
extern void ceph_fill_inline_data(struct inode *inode, struct page *locked_page,
|
||||
char *data, size_t len);
|
||||
@@ -1375,4 +1413,9 @@ extern bool ceph_quota_update_statfs(struct ceph_fs_client *fsc,
|
||||
struct kstatfs *buf);
|
||||
extern void ceph_cleanup_quotarealms_inodes(struct ceph_mds_client *mdsc);
|
||||
|
||||
bool ceph_inc_mds_stopping_blocker(struct ceph_mds_client *mdsc,
|
||||
struct ceph_mds_session *session);
|
||||
void ceph_dec_mds_stopping_blocker(struct ceph_mds_client *mdsc);
|
||||
bool ceph_inc_osd_stopping_blocker(struct ceph_mds_client *mdsc);
|
||||
void ceph_dec_osd_stopping_blocker(struct ceph_mds_client *mdsc);
|
||||
#endif /* _FS_CEPH_SUPER_H */
|
||||
|
||||
@@ -352,6 +352,24 @@ static ssize_t ceph_vxattrcb_auth_mds(struct ceph_inode_info *ci,
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_FS_ENCRYPTION)
|
||||
static bool ceph_vxattrcb_fscrypt_auth_exists(struct ceph_inode_info *ci)
|
||||
{
|
||||
return ci->fscrypt_auth_len;
|
||||
}
|
||||
|
||||
static ssize_t ceph_vxattrcb_fscrypt_auth(struct ceph_inode_info *ci,
|
||||
char *val, size_t size)
|
||||
{
|
||||
if (size) {
|
||||
if (size < ci->fscrypt_auth_len)
|
||||
return -ERANGE;
|
||||
memcpy(val, ci->fscrypt_auth, ci->fscrypt_auth_len);
|
||||
}
|
||||
return ci->fscrypt_auth_len;
|
||||
}
|
||||
#endif /* CONFIG_FS_ENCRYPTION */
|
||||
|
||||
#define CEPH_XATTR_NAME(_type, _name) XATTR_CEPH_PREFIX #_type "." #_name
|
||||
#define CEPH_XATTR_NAME2(_type, _name, _name2) \
|
||||
XATTR_CEPH_PREFIX #_type "." #_name "." #_name2
|
||||
@@ -500,6 +518,15 @@ static struct ceph_vxattr ceph_common_vxattrs[] = {
|
||||
.exists_cb = NULL,
|
||||
.flags = VXATTR_FLAG_READONLY,
|
||||
},
|
||||
#if IS_ENABLED(CONFIG_FS_ENCRYPTION)
|
||||
{
|
||||
.name = "ceph.fscrypt.auth",
|
||||
.name_size = sizeof("ceph.fscrypt.auth"),
|
||||
.getxattr_cb = ceph_vxattrcb_fscrypt_auth,
|
||||
.exists_cb = ceph_vxattrcb_fscrypt_auth_exists,
|
||||
.flags = VXATTR_FLAG_READONLY,
|
||||
},
|
||||
#endif /* CONFIG_FS_ENCRYPTION */
|
||||
{ .name = NULL, 0 } /* Required table terminator */
|
||||
};
|
||||
|
||||
@@ -1407,6 +1434,9 @@ void ceph_release_acl_sec_ctx(struct ceph_acl_sec_ctx *as_ctx)
|
||||
#endif
|
||||
#ifdef CONFIG_CEPH_FS_SECURITY_LABEL
|
||||
security_release_secctx(as_ctx->sec_ctx, as_ctx->sec_ctxlen);
|
||||
#endif
|
||||
#ifdef CONFIG_FS_ENCRYPTION
|
||||
kfree(as_ctx->fscrypt_auth);
|
||||
#endif
|
||||
if (as_ctx->pagelist)
|
||||
ceph_pagelist_release(as_ctx->pagelist);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user