From patchwork Fri Sep 21 16:37:56 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Howells X-Patchwork-Id: 10610699 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 00BE6913 for ; Fri, 21 Sep 2018 16:38:04 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id DA58C2B036 for ; Fri, 21 Sep 2018 16:38:03 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id CA60C2D32A; Fri, 21 Sep 2018 16:38:03 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 397DF287A8 for ; Fri, 21 Sep 2018 16:38:01 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2390753AbeIUW1k (ORCPT ); Fri, 21 Sep 2018 18:27:40 -0400 Received: from mx1.redhat.com ([209.132.183.28]:32822 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2389683AbeIUW1k (ORCPT ); Fri, 21 Sep 2018 18:27:40 -0400 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.23]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id CB20F804E4; Fri, 21 Sep 2018 16:37:58 +0000 (UTC) Received: from warthog.procyon.org.uk (ovpn-123-84.rdu2.redhat.com [10.10.123.84]) by smtp.corp.redhat.com (Postfix) with ESMTP id C5AF5171E4; Fri, 21 Sep 2018 16:37:56 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH 1/5] vfs: syscall: Add fsinfo() to query filesystem information [ver #12] From: David Howells To: viro@zeniv.linux.org.uk Cc: linux-api@vger.kernel.org, torvalds@linux-foundation.org, dhowells@redhat.com, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, mszeredi@redhat.com Date: Fri, 21 Sep 2018 17:37:56 +0100 Message-ID: <153754787604.19213.17179631694257430139.stgit@warthog.procyon.org.uk> In-Reply-To: <153754786825.19213.16907802981157370740.stgit@warthog.procyon.org.uk> References: <153754786825.19213.16907802981157370740.stgit@warthog.procyon.org.uk> User-Agent: StGit/unknown-version MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.23 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.27]); Fri, 21 Sep 2018 16:37:58 +0000 (UTC) Sender: linux-fsdevel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Add a system call to allow filesystem information to be queried. A request value can be given to indicate the desired attribute. Support is provided for enumerating multi-value attributes. =============== NEW SYSTEM CALL =============== The new system call looks like: int ret = fsinfo(int dfd, const char *filename, const struct fsinfo_params *params, void *buffer, size_t buf_size); The params parameter optionally points to a block of parameters: struct fsinfo_params { __u32 at_flags; __u32 request; __u32 Nth; __u32 Mth; __u32 __reserved[6]; }; If params is NULL, it is assumed params->request should be fsinfo_attr_statfs, params->Nth should be 0, params->Mth should be 0 and params->at_flags should be 0. If params is given, all of params->__reserved[] must be 0. dfd, filename and params->at_flags indicate the file to query. There is no equivalent of lstat() as that can be emulated with fsinfo() by setting AT_SYMLINK_NOFOLLOW in params->at_flags. There is also no equivalent of fstat() as that can be emulated by passing a NULL filename to fsinfo() with the fd of interest in dfd. AT_NO_AUTOMOUNT can also be used to an allow automount point to be queried without triggering it. params->request indicates the attribute/attributes to be queried. This can be one of: FSINFO_ATTR_STATFS - statfs-style info FSINFO_ATTR_FSINFO - Information about fsinfo() FSINFO_ATTR_IDS - Filesystem IDs FSINFO_ATTR_LIMITS - Filesystem limits FSINFO_ATTR_SUPPORTS - What's supported in statx(), IOC flags FSINFO_ATTR_CAPABILITIES - Filesystem capabilities FSINFO_ATTR_TIMESTAMP_INFO - Inode timestamp info FSINFO_ATTR_VOLUME_ID - Volume ID (string) FSINFO_ATTR_VOLUME_UUID - Volume UUID FSINFO_ATTR_VOLUME_NAME - Volume name (string) FSINFO_ATTR_CELL_NAME - Cell name (string) FSINFO_ATTR_DOMAIN_NAME - Domain name (string) FSINFO_ATTR_SERVER_NAME - Name of the Nth server (string) FSINFO_ATTR_SERVER_ADDRESS - Mth address of the Nth server FSINFO_ATTR_PARAMETER - Nth mount parameter (string) FSINFO_ATTR_SOURCE - Nth mount source name (string) FSINFO_ATTR_NAME_ENCODING - Filename encoding (string) FSINFO_ATTR_NAME_CODEPAGE - Filename codepage (string) FSINFO_ATTR_IO_SIZE - I/O size hints Some attributes (such as the servers backing a network filesystem) can have multiple values. These can be enumerated by setting params->Nth and params->Mth to 0, 1, ... until ENODATA is returned. buffer and buf_size point to the reply buffer. The buffer is filled up to the specified size, even if this means truncating the reply. The full size of the reply is returned. In future versions, this will allow extra fields to be tacked on to the end of the reply, but anyone not expecting them will only get the subset they're expecting. If either buffer of buf_size are 0, no copy will take place and the data size will be returned. At the moment, this will only work on x86_64 and i386 as it requires the system call to be wired up. Signed-off-by: David Howells cc: linux-api@vger.kernel.org --- arch/x86/entry/syscalls/syscall_32.tbl | 1 arch/x86/entry/syscalls/syscall_64.tbl | 1 fs/statfs.c | 461 ++++++++++++++++++++++++++ include/linux/fs.h | 4 include/linux/fsinfo.h | 41 ++ include/linux/syscalls.h | 4 include/uapi/linux/fsinfo.h | 234 +++++++++++++ samples/vfs/Makefile | 4 samples/vfs/test-fsinfo.c | 572 ++++++++++++++++++++++++++++++++ 9 files changed, 1322 insertions(+) create mode 100644 include/linux/fsinfo.h create mode 100644 include/uapi/linux/fsinfo.h create mode 100644 samples/vfs/test-fsinfo.c diff --git a/arch/x86/entry/syscalls/syscall_32.tbl b/arch/x86/entry/syscalls/syscall_32.tbl index d1eb6c815790..806760188a31 100644 --- a/arch/x86/entry/syscalls/syscall_32.tbl +++ b/arch/x86/entry/syscalls/syscall_32.tbl @@ -404,3 +404,4 @@ 390 i386 fsconfig sys_fsconfig __ia32_sys_fsconfig 391 i386 fsmount sys_fsmount __ia32_sys_fsmount 392 i386 fspick sys_fspick __ia32_sys_fspick +393 i386 fsinfo sys_fsinfo __ia32_sys_fsinfo diff --git a/arch/x86/entry/syscalls/syscall_64.tbl b/arch/x86/entry/syscalls/syscall_64.tbl index d3ab703c02bb..0823eed2b02e 100644 --- a/arch/x86/entry/syscalls/syscall_64.tbl +++ b/arch/x86/entry/syscalls/syscall_64.tbl @@ -349,6 +349,7 @@ 338 common fsconfig __x64_sys_fsconfig 339 common fsmount __x64_sys_fsmount 340 common fspick __x64_sys_fspick +341 common fsinfo __x64_sys_fsinfo # # x32-specific system call numbers start at 512 to avoid cache impact diff --git a/fs/statfs.c b/fs/statfs.c index f0216629621d..f3cf8aa42a3d 100644 --- a/fs/statfs.c +++ b/fs/statfs.c @@ -9,6 +9,7 @@ #include #include #include +#include #include "internal.h" static int flags_by_mnt(int mnt_flags) @@ -394,3 +395,463 @@ COMPAT_SYSCALL_DEFINE2(ustat, unsigned, dev, struct compat_ustat __user *, u) return 0; } #endif + +/* + * Get basic filesystem stats from statfs. + */ +static int fsinfo_generic_statfs(struct dentry *dentry, + struct fsinfo_statfs *p) +{ + struct kstatfs buf; + int ret; + + ret = statfs_by_dentry(dentry, &buf); + if (ret < 0) + return ret; + + p->f_blocks = buf.f_blocks; + p->f_bfree = buf.f_bfree; + p->f_bavail = buf.f_bavail; + p->f_files = buf.f_files; + p->f_ffree = buf.f_ffree; + p->f_favail = buf.f_ffree; + p->f_bsize = buf.f_bsize; + p->f_frsize = buf.f_frsize; + return sizeof(*p); +} + +static int fsinfo_generic_ids(struct dentry *dentry, + struct fsinfo_ids *p) +{ + struct super_block *sb; + struct kstatfs buf; + int ret; + + ret = statfs_by_dentry(dentry, &buf); + if (ret < 0) + return ret; + + sb = dentry->d_sb; + p->f_fstype = sb->s_magic; + p->f_dev_major = MAJOR(sb->s_dev); + p->f_dev_minor = MINOR(sb->s_dev); + p->f_flags = ST_VALID | flags_by_sb(sb->s_flags); + + memcpy(&p->f_fsid, &buf.f_fsid, sizeof(p->f_fsid)); + strlcpy(p->f_fs_name, dentry->d_sb->s_type->name, sizeof(p->f_fs_name)); + return sizeof(*p); +} + +static int fsinfo_generic_limits(struct dentry *dentry, + struct fsinfo_limits *lim) +{ + struct super_block *sb = dentry->d_sb; + + lim->max_file_size = sb->s_maxbytes; + lim->max_hard_links = sb->s_max_links; + lim->max_uid = UINT_MAX; + lim->max_gid = UINT_MAX; + lim->max_projid = UINT_MAX; + lim->max_filename_len = NAME_MAX; + lim->max_symlink_len = PAGE_SIZE; + lim->max_xattr_name_len = XATTR_NAME_MAX; + lim->max_xattr_body_len = XATTR_SIZE_MAX; + lim->max_dev_major = 0xffffff; + lim->max_dev_minor = 0xff; + return sizeof(*lim); +} + +static int fsinfo_generic_supports(struct dentry *dentry, + struct fsinfo_supports *c) +{ + struct super_block *sb = dentry->d_sb; + + c->stx_mask = STATX_BASIC_STATS; + if (sb->s_d_op && sb->s_d_op->d_automount) + c->stx_attributes |= STATX_ATTR_AUTOMOUNT; + return sizeof(*c); +} + +static int fsinfo_generic_capabilities(struct dentry *dentry, + struct fsinfo_capabilities *c) +{ + struct super_block *sb = dentry->d_sb; + + if (sb->s_mtd) + fsinfo_set_cap(c, FSINFO_CAP_IS_FLASH_FS); + else if (sb->s_bdev) + fsinfo_set_cap(c, FSINFO_CAP_IS_BLOCK_FS); + + if (sb->s_quota_types & QTYPE_MASK_USR) + fsinfo_set_cap(c, FSINFO_CAP_USER_QUOTAS); + if (sb->s_quota_types & QTYPE_MASK_GRP) + fsinfo_set_cap(c, FSINFO_CAP_GROUP_QUOTAS); + if (sb->s_quota_types & QTYPE_MASK_PRJ) + fsinfo_set_cap(c, FSINFO_CAP_PROJECT_QUOTAS); + if (sb->s_d_op && sb->s_d_op->d_automount) + fsinfo_set_cap(c, FSINFO_CAP_AUTOMOUNTS); + if (sb->s_id[0]) + fsinfo_set_cap(c, FSINFO_CAP_VOLUME_ID); + + fsinfo_set_cap(c, FSINFO_CAP_HAS_ATIME); + fsinfo_set_cap(c, FSINFO_CAP_HAS_CTIME); + fsinfo_set_cap(c, FSINFO_CAP_HAS_MTIME); + return sizeof(*c); +} + +static int fsinfo_generic_timestamp_info(struct dentry *dentry, + struct fsinfo_timestamp_info *ts) +{ + struct super_block *sb = dentry->d_sb; + + /* If unset, assume 1s granularity */ + u16 mantissa = 1; + s8 exponent = 0; + + ts->minimum_timestamp = S64_MIN; + ts->maximum_timestamp = S64_MAX; + if (sb->s_time_gran < 1000000000) { + if (sb->s_time_gran < 1000) + exponent = -9; + else if (sb->s_time_gran < 1000000) + exponent = -6; + else + exponent = -3; + } +#define set_gran(x) \ + do { \ + ts->x##_mantissa = mantissa; \ + ts->x##_exponent = exponent; \ + } while (0) + set_gran(atime_gran); + set_gran(btime_gran); + set_gran(ctime_gran); + set_gran(mtime_gran); + return sizeof(*ts); +} + +static int fsinfo_generic_volume_uuid(struct dentry *dentry, + struct fsinfo_volume_uuid *vu) +{ + struct super_block *sb = dentry->d_sb; + + memcpy(vu, &sb->s_uuid, sizeof(*vu)); + return sizeof(*vu); +} + +static int fsinfo_generic_volume_id(struct dentry *dentry, char *buf) +{ + struct super_block *sb = dentry->d_sb; + size_t len = strlen(sb->s_id); + + memcpy(buf, sb->s_id, len + 1); + return len; +} + +static int fsinfo_generic_name_encoding(struct dentry *dentry, char *buf) +{ + static const char encoding[] = "utf8"; + + memcpy(buf, encoding, sizeof(encoding) - 1); + return sizeof(encoding) - 1; +} + +static int fsinfo_generic_io_size(struct dentry *dentry, + struct fsinfo_io_size *c) +{ + struct super_block *sb = dentry->d_sb; + struct kstatfs buf; + int ret; + + if (sb->s_op->statfs == simple_statfs) { + c->dio_size_gran = 1; + c->dio_mem_align = 1; + } else { + ret = statfs_by_dentry(dentry, &buf); + if (ret < 0) + return ret; + c->dio_size_gran = buf.f_bsize; + c->dio_mem_align = buf.f_bsize; + } + return sizeof(*c); +} + +/* + * Implement some queries generically from stuff in the superblock. + */ +int generic_fsinfo(struct path *path, struct fsinfo_kparams *params) +{ + struct dentry *dentry = path->dentry; + +#define _gen(X, Y) FSINFO_ATTR_##X: return fsinfo_generic_##Y(dentry, params->buffer) + + switch (params->request) { + case _gen(STATFS, statfs); + case _gen(IDS, ids); + case _gen(LIMITS, limits); + case _gen(SUPPORTS, supports); + case _gen(CAPABILITIES, capabilities); + case _gen(TIMESTAMP_INFO, timestamp_info); + case _gen(VOLUME_UUID, volume_uuid); + case _gen(VOLUME_ID, volume_id); + case _gen(NAME_ENCODING, name_encoding); + case _gen(IO_SIZE, io_size); + default: + return -EOPNOTSUPP; + } +} +EXPORT_SYMBOL(generic_fsinfo); + +/* + * Retrieve the filesystem info. We make some stuff up if the operation is not + * supported. + */ +int vfs_fsinfo(struct path *path, struct fsinfo_kparams *params) +{ + struct dentry *dentry = path->dentry; + int (*fsinfo)(struct path *, struct fsinfo_kparams *); + int ret; + + if (params->request == FSINFO_ATTR_FSINFO) { + struct fsinfo_fsinfo *info = params->buffer; + + info->max_attr = FSINFO_ATTR__NR; + info->max_cap = FSINFO_CAP__NR; + return sizeof(*info); + } + + fsinfo = dentry->d_sb->s_op->fsinfo; + if (!fsinfo) { + if (!dentry->d_sb->s_op->statfs) + return -EOPNOTSUPP; + fsinfo = generic_fsinfo; + } + + ret = security_sb_statfs(dentry); + if (ret) + return ret; + + ret = fsinfo(path, params); + if (ret < 0) + return ret; + + if (params->request == FSINFO_ATTR_IDS && + params->buffer) { + struct fsinfo_ids *p = params->buffer; + + p->f_flags |= flags_by_mnt(path->mnt->mnt_flags); + } + return ret; +} + +static int vfs_fsinfo_path(int dfd, const char __user *filename, + struct fsinfo_kparams *params) +{ + struct path path; + unsigned lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT; + int ret = -EINVAL; + + if ((params->at_flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT | + AT_EMPTY_PATH)) != 0) + return -EINVAL; + + if (params->at_flags & AT_SYMLINK_NOFOLLOW) + lookup_flags &= ~LOOKUP_FOLLOW; + if (params->at_flags & AT_NO_AUTOMOUNT) + lookup_flags &= ~LOOKUP_AUTOMOUNT; + if (params->at_flags & AT_EMPTY_PATH) + lookup_flags |= LOOKUP_EMPTY; + +retry: + ret = user_path_at(dfd, filename, lookup_flags, &path); + if (ret) + goto out; + + ret = vfs_fsinfo(&path, params); + path_put(&path); + if (retry_estale(ret, lookup_flags)) { + lookup_flags |= LOOKUP_REVAL; + goto retry; + } +out: + return ret; +} + +static int vfs_fsinfo_fd(unsigned int fd, struct fsinfo_kparams *params) +{ + struct fd f = fdget_raw(fd); + int ret = -EBADF; + + if (f.file) { + ret = vfs_fsinfo(&f.file->f_path, params); + fdput(f); + } + return ret; +} + +/* + * Return buffer information by requestable attribute. + * + * STRUCT indicates a fixed-size structure with only one instance. + * STRUCT_N indicates a 1D array of STRUCT, indexed by Nth + * STRUCT_NM indicates a 2D-array of STRUCT, indexed by Nth, Mth + * STRING indicates a string with only one instance. + * STRING_N indicates a 1D array of STRING, indexed by Nth + * STRING_NM indicates a 2D-array of STRING, indexed by Nth, Mth + * + * If an entry is marked STRUCT, STRUCT_N or STRUCT_NM then if no buffer is + * supplied to sys_fsinfo(), sys_fsinfo() will handle returning the buffer size + * without calling vfs_fsinfo() and the filesystem. + * + * No struct may have more than 252 bytes (ie. 0x3f * 4) + */ +#define FSINFO_STRING(X,Y) [FSINFO_ATTR_##X] = 0x0000 +#define FSINFO_STRUCT(X,Y) [FSINFO_ATTR_##X] = sizeof(struct fsinfo_##Y) +#define FSINFO_STRING_N(X,Y) [FSINFO_ATTR_##X] = 0x4000 +#define FSINFO_STRUCT_N(X,Y) [FSINFO_ATTR_##X] = 0x4000 | sizeof(struct fsinfo_##Y) +#define FSINFO_STRUCT_NM(X,Y) [FSINFO_ATTR_##X] = 0x8000 | sizeof(struct fsinfo_##Y) +#define FSINFO_STRING_NM(X,Y) [FSINFO_ATTR_##X] = 0x8000 +static const u16 fsinfo_buffer_sizes[FSINFO_ATTR__NR] = { + FSINFO_STRUCT (STATFS, statfs), + FSINFO_STRUCT (FSINFO, fsinfo), + FSINFO_STRUCT (IDS, ids), + FSINFO_STRUCT (LIMITS, limits), + FSINFO_STRUCT (CAPABILITIES, capabilities), + FSINFO_STRUCT (SUPPORTS, supports), + FSINFO_STRUCT (TIMESTAMP_INFO, timestamp_info), + FSINFO_STRING (VOLUME_ID, volume_id), + FSINFO_STRUCT (VOLUME_UUID, volume_uuid), + FSINFO_STRING (VOLUME_NAME, volume_name), + FSINFO_STRING (CELL_NAME, cell_name), + FSINFO_STRING (DOMAIN_NAME, domain_name), + FSINFO_STRING_N (SERVER_NAME, server_name), + FSINFO_STRUCT_NM (SERVER_ADDRESS, server_address), + FSINFO_STRING_NM (PARAMETER, parameter), + FSINFO_STRING_N (SOURCE, source), + FSINFO_STRING (NAME_ENCODING, name_encoding), + FSINFO_STRING (NAME_CODEPAGE, name_codepage), + FSINFO_STRUCT (IO_SIZE, io_size), +}; + +/** + * sys_fsinfo - System call to get filesystem information + * @dfd: Base directory to pathwalk from or fd referring to filesystem. + * @filename: Filesystem to query or NULL. + * @_params: Parameters to define request (or NULL for enhanced statfs). + * @_buffer: Result buffer. + * @buf_size: Size of result buffer. + * + * Get information on a filesystem. The filesystem attribute to be queried is + * indicated by @_params->request, and some of the attributes can have multiple + * values, indexed by @_params->Nth and @_params->Mth. If @_params is NULL, + * then the 0th fsinfo_attr_statfs attribute is queried. If an attribute does + * not exist, EOPNOTSUPP is returned; if the Nth,Mth value does not exist, + * ENODATA is returned. + * + * On success, the size of the attribute's value is returned. If @buf_size is + * 0 or @_buffer is NULL, only the size is returned. If the size of the value + * is larger than @buf_size, it will be truncated by the copy. If the size of + * the value is smaller than @buf_size then the excess buffer space will be + * cleared. The full size of the value will be returned, irrespective of how + * much data is actually placed in the buffer. + */ +SYSCALL_DEFINE5(fsinfo, + int, dfd, const char __user *, filename, + struct fsinfo_params __user *, _params, + void __user *, _buffer, size_t, buf_size) +{ + struct fsinfo_params user_params; + struct fsinfo_kparams params; + size_t size, n; + int ret; + + if (_params) { + if (copy_from_user(&user_params, _params, sizeof(user_params))) + return -EFAULT; + if (user_params.__reserved[0] || + user_params.__reserved[1] || + user_params.__reserved[2] || + user_params.__reserved[3] || + user_params.__reserved[4] || + user_params.__reserved[5]) + return -EINVAL; + if (user_params.request >= FSINFO_ATTR__NR) + return -EOPNOTSUPP; + params.at_flags = user_params.at_flags; + params.request = user_params.request; + params.Nth = user_params.Nth; + params.Mth = user_params.Mth; + } else { + params.at_flags = 0; + params.request = FSINFO_ATTR_STATFS; + params.Nth = 0; + params.Mth = 0; + } + + if (!_buffer || !buf_size) { + buf_size = 0; + _buffer = NULL; + } + + /* Allocate an appropriately-sized buffer. We will truncate the + * contents when we write the contents back to userspace. + */ + size = fsinfo_buffer_sizes[params.request]; + switch (size & 0xc000) { + case 0x0000: + if (params.Nth != 0) + return -ENODATA; + /* Fall through */ + case 0x4000: + if (params.Mth != 0) + return -ENODATA; + /* Fall through */ + case 0x8000: + break; + case 0xc000: + return -ENOBUFS; + } + + size &= ~0xc000; + if (size == 0) { + params.string_val = true; + params.buf_size = 4096; + } else { + params.string_val = false; + params.buf_size = size; + if (buf_size == 0) + return size; /* We know how big the buffer should be */ + } + + /* We always allocate a buffer for a string, even if buf_size == 0 and + * we're not going to return any data. This means that the filesystem + * code needn't care about whether the buffer actually exists or not. + */ + params.buffer = kzalloc(params.buf_size, GFP_KERNEL); + if (!params.buffer) + return -ENOMEM; + + if (filename) + ret = vfs_fsinfo_path(dfd, filename, ¶ms); + else + ret = vfs_fsinfo_fd(dfd, ¶ms); + if (ret < 0) + goto error; + + n = ret; + if (n > buf_size) + n = buf_size; + + if (n > 0 && copy_to_user(_buffer, params.buffer, buf_size)) + ret = -EFAULT; + + /* Clear any part of the buffer that we won't fill if we're putting a + * struct in there rather than a string. + */ + if (buf_size > n && !params.string_val && + clear_user(_buffer + n, buf_size - n) != 0) + return -EFAULT; +error: + kfree(params.buffer); + return ret; +} diff --git a/include/linux/fs.h b/include/linux/fs.h index 14e08020890f..580dea058002 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -63,6 +63,8 @@ struct fscrypt_info; struct fscrypt_operations; struct fs_context; struct fs_parameter_description; +struct fsinfo_kparams; +enum fsinfo_attribute; extern void __init inode_init(void); extern void __init inode_init_early(void); @@ -1864,6 +1866,7 @@ struct super_operations { int (*thaw_super) (struct super_block *); int (*unfreeze_fs) (struct super_block *); int (*statfs) (struct dentry *, struct kstatfs *); + int (*fsinfo) (struct path *, struct fsinfo_kparams *); int (*remount_fs) (struct super_block *, int *, char *, size_t); void (*umount_begin) (struct super_block *); @@ -2244,6 +2247,7 @@ extern int iterate_mounts(int (*)(struct vfsmount *, void *), void *, extern int vfs_statfs(const struct path *, struct kstatfs *); extern int user_statfs(const char __user *, struct kstatfs *); extern int fd_statfs(int, struct kstatfs *); +extern int vfs_fsinfo(struct path *, struct fsinfo_kparams *); extern int freeze_super(struct super_block *super); extern int thaw_super(struct super_block *super); extern bool our_mnt(struct vfsmount *mnt); diff --git a/include/linux/fsinfo.h b/include/linux/fsinfo.h new file mode 100644 index 000000000000..e488701c5c04 --- /dev/null +++ b/include/linux/fsinfo.h @@ -0,0 +1,41 @@ +/* Filesystem information query + * + * Copyright (C) 2018 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifndef _LINUX_FSINFO_H +#define _LINUX_FSINFO_H + +#include + +struct fsinfo_kparams { + __u32 at_flags; /* AT_SYMLINK_NOFOLLOW and similar */ + enum fsinfo_attribute request; /* What is being asking for */ + __u32 Nth; /* Instance of it (some may have multiple) */ + __u32 Mth; /* Subinstance */ + bool string_val; /* T if variable-length string value */ + void *buffer; /* Where to place the reply */ + size_t buf_size; /* Size of the buffer */ +}; + +extern int generic_fsinfo(struct path *, struct fsinfo_kparams *); + +static inline void fsinfo_set_cap(struct fsinfo_capabilities *c, + enum fsinfo_capability cap) +{ + c->capabilities[cap / 8] |= 1 << (cap % 8); +} + +static inline void fsinfo_clear_cap(struct fsinfo_capabilities *c, + enum fsinfo_capability cap) +{ + c->capabilities[cap / 8] &= ~(1 << (cap % 8)); +} + +#endif /* _LINUX_FSINFO_H */ diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index eb8d62f4ee24..39253ed76227 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -50,6 +50,7 @@ struct stat64; struct statfs; struct statfs64; struct statx; +struct fsinfo_params; struct __sysctl_args; struct sysinfo; struct timespec; @@ -915,6 +916,9 @@ asmlinkage long sys_fsconfig(int fs_fd, unsigned int cmd, const char __user *key const void __user *value, int aux); asmlinkage long sys_fsmount(int fs_fd, unsigned int flags, unsigned int ms_flags); asmlinkage long sys_fspick(int dfd, const char __user *path, unsigned int flags); +asmlinkage long sys_fsinfo(int dfd, const char __user *path, + struct fsinfo_params __user *params, + void __user *buffer, size_t buf_size); /* * Architecture-specific system calls diff --git a/include/uapi/linux/fsinfo.h b/include/uapi/linux/fsinfo.h new file mode 100644 index 000000000000..ab7f1e6ab60b --- /dev/null +++ b/include/uapi/linux/fsinfo.h @@ -0,0 +1,234 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* fsinfo() definitions. + * + * Copyright (C) 2018 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ +#ifndef _UAPI_LINUX_FSINFO_H +#define _UAPI_LINUX_FSINFO_H + +#include +#include + +/* + * The filesystem attributes that can be requested. Note that some attributes + * may have multiple instances which can be switched in the parameter block. + */ +enum fsinfo_attribute { + FSINFO_ATTR_STATFS = 0, /* statfs()-style state */ + FSINFO_ATTR_FSINFO = 1, /* Information about fsinfo() */ + FSINFO_ATTR_IDS = 2, /* Filesystem IDs */ + FSINFO_ATTR_LIMITS = 3, /* Filesystem limits */ + FSINFO_ATTR_SUPPORTS = 4, /* What's supported in statx, iocflags, ... */ + FSINFO_ATTR_CAPABILITIES = 5, /* Filesystem capabilities (bits) */ + FSINFO_ATTR_TIMESTAMP_INFO = 6, /* Inode timestamp info */ + FSINFO_ATTR_VOLUME_ID = 7, /* Volume ID (string) */ + FSINFO_ATTR_VOLUME_UUID = 8, /* Volume UUID (LE uuid) */ + FSINFO_ATTR_VOLUME_NAME = 9, /* Volume name (string) */ + FSINFO_ATTR_CELL_NAME = 10, /* Cell name (string) */ + FSINFO_ATTR_DOMAIN_NAME = 11, /* Domain name (string) */ + FSINFO_ATTR_SERVER_NAME = 12, /* Name of the Nth server */ + FSINFO_ATTR_SERVER_ADDRESS = 13, /* Mth address of the Nth server */ + FSINFO_ATTR_PARAMETER = 14, /* Nth mount parameter (string) */ + FSINFO_ATTR_SOURCE = 15, /* Nth mount source name (string) */ + FSINFO_ATTR_NAME_ENCODING = 16, /* Filename encoding (string) */ + FSINFO_ATTR_NAME_CODEPAGE = 17, /* Filename codepage (string) */ + FSINFO_ATTR_IO_SIZE = 18, /* Optimal I/O sizes */ + FSINFO_ATTR__NR +}; + +/* + * Optional fsinfo() parameter structure. + * + * If this is not given, it is assumed that fsinfo_attr_statfs instance 0,0 is + * desired. + */ +struct fsinfo_params { + __u32 at_flags; /* AT_SYMLINK_NOFOLLOW and similar flags */ + __u32 request; /* What is being asking for (enum fsinfo_attribute) */ + __u32 Nth; /* Instance of it (some may have multiple) */ + __u32 Mth; /* Subinstance of Nth instance */ + __u32 __reserved[6]; /* Reserved params; all must be 0 */ +}; + +/* + * Information struct for fsinfo(fsinfo_attr_statfs). + * - This gives extended filesystem information. + */ +struct fsinfo_statfs { + __u64 f_blocks; /* Total number of blocks in fs */ + __u64 f_bfree; /* Total number of free blocks */ + __u64 f_bavail; /* Number of free blocks available to ordinary user */ + __u64 f_files; /* Total number of file nodes in fs */ + __u64 f_ffree; /* Number of free file nodes */ + __u64 f_favail; /* Number of free file nodes available to ordinary user */ + __u32 f_bsize; /* Optimal block size */ + __u32 f_frsize; /* Fragment size */ +}; + +/* + * Information struct for fsinfo(fsinfo_attr_ids). + * + * List of basic identifiers as is normally found in statfs(). + */ +struct fsinfo_ids { + char f_fs_name[15 + 1]; + __u64 f_flags; /* Filesystem mount flags (MS_*) */ + __u64 f_fsid; /* Short 64-bit Filesystem ID (as statfs) */ + __u64 f_sb_id; /* Internal superblock ID for sbnotify()/mntnotify() */ + __u32 f_fstype; /* Filesystem type from linux/magic.h [uncond] */ + __u32 f_dev_major; /* As st_dev_* from struct statx [uncond] */ + __u32 f_dev_minor; + __u32 __reserved[1]; +}; + +/* + * Information struct for fsinfo(fsinfo_attr_limits). + * + * List of supported filesystem limits. + */ +struct fsinfo_limits { + __u64 max_file_size; /* Maximum file size */ + __u64 max_uid; /* Maximum UID supported */ + __u64 max_gid; /* Maximum GID supported */ + __u64 max_projid; /* Maximum project ID supported */ + __u32 max_dev_major; /* Maximum device major representable */ + __u32 max_dev_minor; /* Maximum device minor representable */ + __u32 max_hard_links; /* Maximum number of hard links on a file */ + __u32 max_xattr_body_len; /* Maximum xattr content length */ + __u32 max_xattr_name_len; /* Maximum xattr name length */ + __u32 max_filename_len; /* Maximum filename length */ + __u32 max_symlink_len; /* Maximum symlink content length */ + __u32 __reserved[1]; +}; + +/* + * Information struct for fsinfo(fsinfo_attr_supports). + * + * What's supported in various masks, such as statx() attribute and mask bits + * and IOC flags. + */ +struct fsinfo_supports { + __u64 stx_attributes; /* What statx::stx_attributes are supported */ + __u32 stx_mask; /* What statx::stx_mask bits are supported */ + __u32 ioc_flags; /* What FS_IOC_* flags are supported */ + __u32 win_file_attrs; /* What DOS/Windows FILE_* attributes are supported */ + __u32 __reserved[1]; +}; + +/* + * Information struct for fsinfo(fsinfo_attr_capabilities). + * + * Bitmask indicating filesystem capabilities where renderable as single bits. + */ +enum fsinfo_capability { + FSINFO_CAP_IS_KERNEL_FS = 0, /* fs is kernel-special filesystem */ + FSINFO_CAP_IS_BLOCK_FS = 1, /* fs is block-based filesystem */ + FSINFO_CAP_IS_FLASH_FS = 2, /* fs is flash filesystem */ + FSINFO_CAP_IS_NETWORK_FS = 3, /* fs is network filesystem */ + FSINFO_CAP_IS_AUTOMOUNTER_FS = 4, /* fs is automounter special filesystem */ + FSINFO_CAP_AUTOMOUNTS = 5, /* fs supports automounts */ + FSINFO_CAP_ADV_LOCKS = 6, /* fs supports advisory file locking */ + FSINFO_CAP_MAND_LOCKS = 7, /* fs supports mandatory file locking */ + FSINFO_CAP_LEASES = 8, /* fs supports file leases */ + FSINFO_CAP_UIDS = 9, /* fs supports numeric uids */ + FSINFO_CAP_GIDS = 10, /* fs supports numeric gids */ + FSINFO_CAP_PROJIDS = 11, /* fs supports numeric project ids */ + FSINFO_CAP_ID_NAMES = 12, /* fs supports user names */ + FSINFO_CAP_ID_GUIDS = 13, /* fs supports user guids */ + FSINFO_CAP_WINDOWS_ATTRS = 14, /* fs has windows attributes */ + FSINFO_CAP_USER_QUOTAS = 15, /* fs has per-user quotas */ + FSINFO_CAP_GROUP_QUOTAS = 16, /* fs has per-group quotas */ + FSINFO_CAP_PROJECT_QUOTAS = 17, /* fs has per-project quotas */ + FSINFO_CAP_XATTRS = 18, /* fs has xattrs */ + FSINFO_CAP_JOURNAL = 19, /* fs has a journal */ + FSINFO_CAP_DATA_IS_JOURNALLED = 20, /* fs is using data journalling */ + FSINFO_CAP_O_SYNC = 21, /* fs supports O_SYNC */ + FSINFO_CAP_O_DIRECT = 22, /* fs supports O_DIRECT */ + FSINFO_CAP_VOLUME_ID = 23, /* fs has a volume ID */ + FSINFO_CAP_VOLUME_UUID = 24, /* fs has a volume UUID */ + FSINFO_CAP_VOLUME_NAME = 25, /* fs has a volume name */ + FSINFO_CAP_VOLUME_FSID = 26, /* fs has a volume FSID */ + FSINFO_CAP_CELL_NAME = 27, /* fs has a cell name */ + FSINFO_CAP_DOMAIN_NAME = 28, /* fs has a domain name */ + FSINFO_CAP_REALM_NAME = 29, /* fs has a realm name */ + FSINFO_CAP_IVER_ALL_CHANGE = 30, /* i_version represents data + meta changes */ + FSINFO_CAP_IVER_DATA_CHANGE = 31, /* i_version represents data changes only */ + FSINFO_CAP_IVER_MONO_INCR = 32, /* i_version incremented monotonically */ + FSINFO_CAP_SYMLINKS = 33, /* fs supports symlinks */ + FSINFO_CAP_HARD_LINKS = 34, /* fs supports hard links */ + FSINFO_CAP_HARD_LINKS_1DIR = 35, /* fs supports hard links in same dir only */ + FSINFO_CAP_DEVICE_FILES = 36, /* fs supports bdev, cdev */ + FSINFO_CAP_UNIX_SPECIALS = 37, /* fs supports pipe, fifo, socket */ + FSINFO_CAP_RESOURCE_FORKS = 38, /* fs supports resource forks/streams */ + FSINFO_CAP_NAME_CASE_INDEP = 39, /* Filename case independence is mandatory */ + FSINFO_CAP_NAME_NON_UTF8 = 40, /* fs has non-utf8 names */ + FSINFO_CAP_NAME_HAS_CODEPAGE = 41, /* fs has a filename codepage */ + FSINFO_CAP_SPARSE = 42, /* fs supports sparse files */ + FSINFO_CAP_NOT_PERSISTENT = 43, /* fs is not persistent */ + FSINFO_CAP_NO_UNIX_MODE = 44, /* fs does not support unix mode bits */ + FSINFO_CAP_HAS_ATIME = 45, /* fs supports access time */ + FSINFO_CAP_HAS_BTIME = 46, /* fs supports birth/creation time */ + FSINFO_CAP_HAS_CTIME = 47, /* fs supports change time */ + FSINFO_CAP_HAS_MTIME = 48, /* fs supports modification time */ + FSINFO_CAP__NR +}; + +struct fsinfo_capabilities { + __u8 capabilities[(FSINFO_CAP__NR + 7) / 8]; +}; + +/* + * Information struct for fsinfo(fsinfo_attr_timestamp_info). + */ +struct fsinfo_timestamp_info { + __s64 minimum_timestamp; /* Minimum timestamp value in seconds */ + __s64 maximum_timestamp; /* Maximum timestamp value in seconds */ + __u16 atime_gran_mantissa; /* Granularity(secs) = mant * 10^exp */ + __u16 btime_gran_mantissa; + __u16 ctime_gran_mantissa; + __u16 mtime_gran_mantissa; + __s8 atime_gran_exponent; + __s8 btime_gran_exponent; + __s8 ctime_gran_exponent; + __s8 mtime_gran_exponent; + __u32 __reserved[1]; +}; + +/* + * Information struct for fsinfo(fsinfo_attr_volume_uuid). + */ +struct fsinfo_volume_uuid { + __u8 uuid[16]; +}; + +/* + * Information struct for fsinfo(fsinfo_attr_server_addresses). + * + * Find the Mth address of the Nth server for a network mount. + */ +struct fsinfo_server_address { + struct __kernel_sockaddr_storage address; +}; + +/* + * Information struct for fsinfo(fsinfo_attr_io_size). + * + * Retrieve I/O size hints for a filesystem. + */ +struct fsinfo_io_size { + __u32 dio_size_gran; /* Size granularity for O_DIRECT */ + __u32 dio_mem_align; /* Memory alignment for O_DIRECT */ +}; + +/* + * Information struct for fsinfo(fsinfo_attr_fsinfo). + * + * This gives information about fsinfo() itself. + */ +struct fsinfo_fsinfo { + __u32 max_attr; /* Number of supported attributes (fsinfo_attr__nr) */ + __u32 max_cap; /* Number of supported capabilities (fsinfo_cap__nr) */ +}; + +#endif /* _UAPI_LINUX_FSINFO_H */ diff --git a/samples/vfs/Makefile b/samples/vfs/Makefile index 4ac9690fb3c4..d2e1ac8d66ea 100644 --- a/samples/vfs/Makefile +++ b/samples/vfs/Makefile @@ -1,10 +1,14 @@ # List of programs to build hostprogs-$(CONFIG_SAMPLE_VFS) := \ + test-fsinfo \ test-fsmount \ test-statx # Tell kbuild to always build the programs always := $(hostprogs-y) +HOSTCFLAGS_test-fsinfo.o += -I$(objtree)/usr/include +HOSTLDLIBS_test-fsinfo += -lm + HOSTCFLAGS_test-fsmount.o += -I$(objtree)/usr/include HOSTCFLAGS_test-statx.o += -I$(objtree)/usr/include diff --git a/samples/vfs/test-fsinfo.c b/samples/vfs/test-fsinfo.c new file mode 100644 index 000000000000..dbb231c30153 --- /dev/null +++ b/samples/vfs/test-fsinfo.c @@ -0,0 +1,572 @@ +/* Test the fsinfo() system call + * + * Copyright (C) 2018 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#define _GNU_SOURCE +#define _ATFILE_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static bool debug = 0; + +static __attribute__((unused)) +ssize_t fsinfo(int dfd, const char *filename, struct fsinfo_params *params, + void *buffer, size_t buf_size) +{ + return syscall(__NR_fsinfo, dfd, filename, params, buffer, buf_size); +} + +#define FSINFO_STRING(X,Y) [FSINFO_ATTR_##X] = 0x0000 +#define FSINFO_STRUCT(X,Y) [FSINFO_ATTR_##X] = sizeof(struct fsinfo_##Y) +#define FSINFO_STRING_N(X,Y) [FSINFO_ATTR_##X] = 0x4000 +#define FSINFO_STRUCT_N(X,Y) [FSINFO_ATTR_##X] = 0x4000 | sizeof(struct fsinfo_##Y) +#define FSINFO_STRUCT_NM(X,Y) [FSINFO_ATTR_##X] = 0x8000 | sizeof(struct fsinfo_##Y) +#define FSINFO_STRING_NM(X,Y) [FSINFO_ATTR_##X] = 0x8000 +static const __u16 fsinfo_buffer_sizes[FSINFO_ATTR__NR] = { + FSINFO_STRUCT (STATFS, statfs), + FSINFO_STRUCT (FSINFO, fsinfo), + FSINFO_STRUCT (IDS, ids), + FSINFO_STRUCT (LIMITS, limits), + FSINFO_STRUCT (CAPABILITIES, capabilities), + FSINFO_STRUCT (SUPPORTS, supports), + FSINFO_STRUCT (TIMESTAMP_INFO, timestamp_info), + FSINFO_STRING (VOLUME_ID, volume_id), + FSINFO_STRUCT (VOLUME_UUID, volume_uuid), + FSINFO_STRING (VOLUME_NAME, volume_name), + FSINFO_STRING (CELL_NAME, cell_name), + FSINFO_STRING (DOMAIN_NAME, domain_name), + FSINFO_STRING_N (SERVER_NAME, server_name), + FSINFO_STRUCT_NM (SERVER_ADDRESS, server_address), + FSINFO_STRING_NM (PARAMETER, parameter), + FSINFO_STRING_N (SOURCE, source), + FSINFO_STRING (NAME_ENCODING, name_encoding), + FSINFO_STRING (NAME_CODEPAGE, name_codepage), + FSINFO_STRUCT (IO_SIZE, io_size), +}; + +#define FSINFO_NAME(X,Y) [FSINFO_ATTR_##X] = #Y +static const char *fsinfo_attr_names[FSINFO_ATTR__NR] = { + FSINFO_NAME (STATFS, statfs), + FSINFO_NAME (FSINFO, fsinfo), + FSINFO_NAME (IDS, ids), + FSINFO_NAME (LIMITS, limits), + FSINFO_NAME (CAPABILITIES, capabilities), + FSINFO_NAME (SUPPORTS, supports), + FSINFO_NAME (TIMESTAMP_INFO, timestamp_info), + FSINFO_NAME (VOLUME_ID, volume_id), + FSINFO_NAME (VOLUME_UUID, volume_uuid), + FSINFO_NAME (VOLUME_NAME, volume_name), + FSINFO_NAME (CELL_NAME, cell_name), + FSINFO_NAME (DOMAIN_NAME, domain_name), + FSINFO_NAME (SERVER_NAME, server_name), + FSINFO_NAME (SERVER_ADDRESS, server_address), + FSINFO_NAME (PARAMETER, parameter), + FSINFO_NAME (SOURCE, source), + FSINFO_NAME (NAME_ENCODING, name_encoding), + FSINFO_NAME (NAME_CODEPAGE, name_codepage), + FSINFO_NAME (IO_SIZE, io_size), +}; + +union reply { + char buffer[4096]; + struct fsinfo_statfs statfs; + struct fsinfo_fsinfo fsinfo; + struct fsinfo_ids ids; + struct fsinfo_limits limits; + struct fsinfo_supports supports; + struct fsinfo_capabilities caps; + struct fsinfo_timestamp_info timestamps; + struct fsinfo_volume_uuid uuid; + struct fsinfo_server_address srv_addr; + struct fsinfo_io_size io_size; +}; + +static void dump_hex(unsigned int *data, int from, int to) +{ + unsigned offset, print_offset = 1, col = 0; + + from /= 4; + to = (to + 3) / 4; + + for (offset = from; offset < to; offset++) { + if (print_offset) { + printf("%04x: ", offset * 8); + print_offset = 0; + } + printf("%08x", data[offset]); + col++; + if ((col & 3) == 0) { + printf("\n"); + print_offset = 1; + } else { + printf(" "); + } + } + + if (!print_offset) + printf("\n"); +} + +static void dump_attr_STATFS(union reply *r, int size) +{ + struct fsinfo_statfs *f = &r->statfs; + + printf("\n"); + printf("\tblocks: n=%llu fr=%llu av=%llu\n", + (unsigned long long)f->f_blocks, + (unsigned long long)f->f_bfree, + (unsigned long long)f->f_bavail); + + printf("\tfiles : n=%llu fr=%llu av=%llu\n", + (unsigned long long)f->f_files, + (unsigned long long)f->f_ffree, + (unsigned long long)f->f_favail); + printf("\tbsize : %u\n", f->f_bsize); + printf("\tfrsize: %u\n", f->f_frsize); +} + +static void dump_attr_FSINFO(union reply *r, int size) +{ + struct fsinfo_fsinfo *f = &r->fsinfo; + + printf("max_attr=%u max_cap=%u\n", f->max_attr, f->max_cap); +} + +static void dump_attr_IDS(union reply *r, int size) +{ + struct fsinfo_ids *f = &r->ids; + + printf("\n"); + printf("\tdev : %02x:%02x\n", f->f_dev_major, f->f_dev_minor); + printf("\tfs : type=%x name=%s\n", f->f_fstype, f->f_fs_name); + printf("\tflags : %llx\n", (unsigned long long)f->f_flags); + printf("\tfsid : %llx\n", (unsigned long long)f->f_fsid); +} + +static void dump_attr_LIMITS(union reply *r, int size) +{ + struct fsinfo_limits *f = &r->limits; + + printf("\n"); + printf("\tmax file size: %llx\n", + (unsigned long long)f->max_file_size); + printf("\tmax ids : u=%llx g=%llx p=%llx\n", + (unsigned long long)f->max_uid, + (unsigned long long)f->max_gid, + (unsigned long long)f->max_projid); + printf("\tmax dev : maj=%x min=%x\n", + f->max_dev_major, f->max_dev_minor); + printf("\tmax links : %x\n", f->max_hard_links); + printf("\tmax xattr : n=%x b=%x\n", + f->max_xattr_name_len, f->max_xattr_body_len); + printf("\tmax len : file=%x sym=%x\n", + f->max_filename_len, f->max_symlink_len); +} + +static void dump_attr_SUPPORTS(union reply *r, int size) +{ + struct fsinfo_supports *f = &r->supports; + + printf("\n"); + printf("\tstx_attr=%llx\n", (unsigned long long)f->stx_attributes); + printf("\tstx_mask=%x\n", f->stx_mask); + printf("\tioc_flags=%x\n", f->ioc_flags); + printf("\twin_fattrs=%x\n", f->win_file_attrs); +} + +#define FSINFO_CAP_NAME(C) [FSINFO_CAP_##C] = #C +static const char *fsinfo_cap_names[FSINFO_CAP__NR] = { + FSINFO_CAP_NAME(IS_KERNEL_FS), + FSINFO_CAP_NAME(IS_BLOCK_FS), + FSINFO_CAP_NAME(IS_FLASH_FS), + FSINFO_CAP_NAME(IS_NETWORK_FS), + FSINFO_CAP_NAME(IS_AUTOMOUNTER_FS), + FSINFO_CAP_NAME(AUTOMOUNTS), + FSINFO_CAP_NAME(ADV_LOCKS), + FSINFO_CAP_NAME(MAND_LOCKS), + FSINFO_CAP_NAME(LEASES), + FSINFO_CAP_NAME(UIDS), + FSINFO_CAP_NAME(GIDS), + FSINFO_CAP_NAME(PROJIDS), + FSINFO_CAP_NAME(ID_NAMES), + FSINFO_CAP_NAME(ID_GUIDS), + FSINFO_CAP_NAME(WINDOWS_ATTRS), + FSINFO_CAP_NAME(USER_QUOTAS), + FSINFO_CAP_NAME(GROUP_QUOTAS), + FSINFO_CAP_NAME(PROJECT_QUOTAS), + FSINFO_CAP_NAME(XATTRS), + FSINFO_CAP_NAME(JOURNAL), + FSINFO_CAP_NAME(DATA_IS_JOURNALLED), + FSINFO_CAP_NAME(O_SYNC), + FSINFO_CAP_NAME(O_DIRECT), + FSINFO_CAP_NAME(VOLUME_ID), + FSINFO_CAP_NAME(VOLUME_UUID), + FSINFO_CAP_NAME(VOLUME_NAME), + FSINFO_CAP_NAME(VOLUME_FSID), + FSINFO_CAP_NAME(CELL_NAME), + FSINFO_CAP_NAME(DOMAIN_NAME), + FSINFO_CAP_NAME(REALM_NAME), + FSINFO_CAP_NAME(IVER_ALL_CHANGE), + FSINFO_CAP_NAME(IVER_DATA_CHANGE), + FSINFO_CAP_NAME(IVER_MONO_INCR), + FSINFO_CAP_NAME(SYMLINKS), + FSINFO_CAP_NAME(HARD_LINKS), + FSINFO_CAP_NAME(HARD_LINKS_1DIR), + FSINFO_CAP_NAME(DEVICE_FILES), + FSINFO_CAP_NAME(UNIX_SPECIALS), + FSINFO_CAP_NAME(RESOURCE_FORKS), + FSINFO_CAP_NAME(NAME_CASE_INDEP), + FSINFO_CAP_NAME(NAME_NON_UTF8), + FSINFO_CAP_NAME(NAME_HAS_CODEPAGE), + FSINFO_CAP_NAME(SPARSE), + FSINFO_CAP_NAME(NOT_PERSISTENT), + FSINFO_CAP_NAME(NO_UNIX_MODE), + FSINFO_CAP_NAME(HAS_ATIME), + FSINFO_CAP_NAME(HAS_BTIME), + FSINFO_CAP_NAME(HAS_CTIME), + FSINFO_CAP_NAME(HAS_MTIME), +}; + +static void dump_attr_CAPABILITIES(union reply *r, int size) +{ + struct fsinfo_capabilities *f = &r->caps; + int i; + + for (i = 0; i < sizeof(f->capabilities); i++) + printf("%02x", f->capabilities[i]); + printf("\n"); + for (i = 0; i < FSINFO_CAP__NR; i++) + if (f->capabilities[i / 8] & (1 << (i % 8))) + printf("\t- %s\n", fsinfo_cap_names[i]); +} + +static void dump_attr_TIMESTAMP_INFO(union reply *r, int size) +{ + struct fsinfo_timestamp_info *f = &r->timestamps; + + printf("range=%llx-%llx\n", + (unsigned long long)f->minimum_timestamp, + (unsigned long long)f->maximum_timestamp); + +#define print_time(G) \ + printf("\t"#G"time : gran=%gs\n", \ + (f->G##time_gran_mantissa * \ + pow(10., f->G##time_gran_exponent))) + print_time(a); + print_time(b); + print_time(c); + print_time(m); +} + +static void dump_attr_VOLUME_UUID(union reply *r, int size) +{ + struct fsinfo_volume_uuid *f = &r->uuid; + + printf("%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x" + "-%02x%02x%02x%02x%02x%02x\n", + f->uuid[ 0], f->uuid[ 1], + f->uuid[ 2], f->uuid[ 3], + f->uuid[ 4], f->uuid[ 5], + f->uuid[ 6], f->uuid[ 7], + f->uuid[ 8], f->uuid[ 9], + f->uuid[10], f->uuid[11], + f->uuid[12], f->uuid[13], + f->uuid[14], f->uuid[15]); +} + +static void dump_attr_SERVER_ADDRESS(union reply *r, int size) +{ + struct fsinfo_server_address *f = &r->srv_addr; + struct sockaddr_in6 *sin6; + struct sockaddr_in *sin; + char buf[1024]; + + switch (f->address.ss_family) { + case AF_INET: + sin = (struct sockaddr_in *)&f->address; + if (!inet_ntop(AF_INET, &sin->sin_addr, buf, sizeof(buf))) + break; + printf("IPv4: %s\n", buf); + return; + case AF_INET6: + sin6 = (struct sockaddr_in6 *)&f->address; + if (!inet_ntop(AF_INET6, &sin6->sin6_addr, buf, sizeof(buf))) + break; + printf("IPv6: %s\n", buf); + return; + } + + printf("family=%u\n", f->address.ss_family); +} + +static void dump_attr_IO_SIZE(union reply *r, int size) +{ + struct fsinfo_io_size *f = &r->io_size; + + printf("dio_size=%u\n", f->dio_size_gran); +} + +/* + * + */ +typedef void (*dumper_t)(union reply *r, int size); + +#define FSINFO_DUMPER(N) [FSINFO_ATTR_##N] = dump_attr_##N +static const dumper_t fsinfo_attr_dumper[FSINFO_ATTR__NR] = { + FSINFO_DUMPER(STATFS), + FSINFO_DUMPER(FSINFO), + FSINFO_DUMPER(IDS), + FSINFO_DUMPER(LIMITS), + FSINFO_DUMPER(SUPPORTS), + FSINFO_DUMPER(CAPABILITIES), + FSINFO_DUMPER(TIMESTAMP_INFO), + FSINFO_DUMPER(VOLUME_UUID), + FSINFO_DUMPER(SERVER_ADDRESS), + FSINFO_DUMPER(IO_SIZE), +}; + +static void dump_fsinfo(enum fsinfo_attribute attr, __u8 about, + union reply *r, int size) +{ + dumper_t dumper = fsinfo_attr_dumper[attr]; + unsigned int len; + + if (!dumper) { + printf("\n"); + return; + } + + len = about & 0x3fff; + if (size < len) { + printf("\n", size, len); + return; + } + + dumper(r, size); +} + +/* + * Try one subinstance of an attribute. + */ +static int try_one(const char *file, struct fsinfo_params *params, bool raw) +{ + union reply r; + char *p; + int ret; + __u16 about; + + memset(&r.buffer, 0xbd, sizeof(r.buffer)); + + errno = 0; + ret = fsinfo(AT_FDCWD, file, params, r.buffer, sizeof(r.buffer)); + if (params->request >= FSINFO_ATTR__NR) { + if (ret == -1 && errno == EOPNOTSUPP) + exit(0); + fprintf(stderr, "Unexpected error for too-large command %u: %m\n", + params->request); + exit(1); + } + + if (debug) + printf("fsinfo(%s,%s,%u,%u) = %d: %m\n", + file, fsinfo_attr_names[params->request], + params->Nth, params->Mth, ret); + + about = fsinfo_buffer_sizes[params->request]; + if (ret == -1) { + if (errno == ENODATA) { + switch (about & 0xc000) { + case 0x0000: + if (params->Nth == 0 && params->Mth == 0) { + fprintf(stderr, + "Unexpected ENODATA1 (%u[%u][%u])\n", + params->request, params->Nth, params->Mth); + exit(1); + } + break; + case 0x4000: + if (params->Nth == 0 && params->Mth == 0) { + fprintf(stderr, + "Unexpected ENODATA2 (%u[%u][%u])\n", + params->request, params->Nth, params->Mth); + exit(1); + } + break; + } + return (params->Mth == 0) ? 2 : 1; + } + if (errno == EOPNOTSUPP) { + if (params->Nth > 0 || params->Mth > 0) { + fprintf(stderr, + "Should return -ENODATA (%u[%u][%u])\n", + params->request, params->Nth, params->Mth); + exit(1); + } + //printf("\e[33m%s\e[m: \n", + // fsinfo_attr_names[attr]); + return 2; + } + perror(file); + exit(1); + } + + if (raw) { + if (ret > 4096) + ret = 4096; + dump_hex((unsigned int *)&r.buffer, 0, ret); + return 0; + } + + switch (params->request) { + case FSINFO_ATTR_PARAMETER: + if (ret == 0) + return 0; + } + + switch (about & 0xc000) { + case 0x0000: + printf("\e[33m%s\e[m: ", + fsinfo_attr_names[params->request]); + break; + case 0x4000: + printf("\e[33m%s[%u]\e[m: ", + fsinfo_attr_names[params->request], + params->Nth); + break; + case 0x8000: + printf("\e[33m%s[%u][%u]\e[m: ", + fsinfo_attr_names[params->request], + params->Nth, params->Mth); + break; + } + + switch (about) { + /* Struct */ + case 0x0001 ... 0x3fff: + case 0x4001 ... 0x7fff: + case 0x8001 ... 0xbfff: + dump_fsinfo(params->request, about, &r, ret); + return 0; + + /* String */ + case 0x0000: + case 0x4000: + case 0x8000: + if (ret >= 4096) { + ret = 4096; + r.buffer[4092] = '.'; + r.buffer[4093] = '.'; + r.buffer[4094] = '.'; + r.buffer[4095] = 0; + } else { + r.buffer[ret] = 0; + } + for (p = r.buffer; *p; p++) { + if (!isprint(*p)) { + printf("\n"); + continue; + } + } + printf("%s\n", r.buffer); + return 0; + + default: + fprintf(stderr, "Fishy about %u %02x\n", params->request, about); + exit(1); + } +} + +/* + * + */ +int main(int argc, char **argv) +{ + struct fsinfo_params params = { + .at_flags = AT_SYMLINK_NOFOLLOW, + }; + unsigned int attr; + int raw = 0, opt, Nth, Mth; + + while ((opt = getopt(argc, argv, "adlr"))) { + switch (opt) { + case 'a': + params.at_flags |= AT_NO_AUTOMOUNT; + continue; + case 'd': + debug = true; + continue; + case 'l': + params.at_flags &= ~AT_SYMLINK_NOFOLLOW; + continue; + case 'r': + raw = 1; + continue; + } + break; + } + + argc -= optind; + argv += optind; + + if (argc != 1) { + printf("Format: test-fsinfo [-alr] \n"); + exit(2); + } + + for (attr = 0; attr <= FSINFO_ATTR__NR; attr++) { + Nth = 0; + do { + Mth = 0; + do { + params.request = attr; + params.Nth = Nth; + params.Mth = Mth; + + switch (try_one(argv[0], ¶ms, raw)) { + case 0: + continue; + case 1: + goto done_M; + case 2: + goto done_N; + } + } while (++Mth < 100); + + done_M: + if (Mth >= 100) { + fprintf(stderr, "Fishy: Mth == %u\n", Mth); + break; + } + + } while (++Nth < 100); + + done_N: + if (Nth >= 100) { + fprintf(stderr, "Fishy: Nth == %u\n", Nth); + break; + } + } + + return 0; +} From patchwork Fri Sep 21 16:38:04 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Howells X-Patchwork-Id: 10610701 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 6F560913 for ; Fri, 21 Sep 2018 16:38:08 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 5B8EA2DB32 for ; Fri, 21 Sep 2018 16:38:08 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 502702DE7A; Fri, 21 Sep 2018 16:38:08 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id C48272DB32 for ; Fri, 21 Sep 2018 16:38:07 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2390744AbeIUW1q (ORCPT ); Fri, 21 Sep 2018 18:27:46 -0400 Received: from mx1.redhat.com ([209.132.183.28]:32876 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2389683AbeIUW1q (ORCPT ); Fri, 21 Sep 2018 18:27:46 -0400 Received: from smtp.corp.redhat.com (int-mx11.intmail.prod.int.phx2.redhat.com [10.5.11.26]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 31DA5804E2; Fri, 21 Sep 2018 16:38:06 +0000 (UTC) Received: from warthog.procyon.org.uk (ovpn-123-84.rdu2.redhat.com [10.10.123.84]) by smtp.corp.redhat.com (Postfix) with ESMTP id C108030912F4; Fri, 21 Sep 2018 16:38:04 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH 2/5] afs: Add fsinfo support [ver #12] From: David Howells To: viro@zeniv.linux.org.uk Cc: torvalds@linux-foundation.org, dhowells@redhat.com, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, mszeredi@redhat.com Date: Fri, 21 Sep 2018 17:38:04 +0100 Message-ID: <153754788403.19213.2591548882315053681.stgit@warthog.procyon.org.uk> In-Reply-To: <153754786825.19213.16907802981157370740.stgit@warthog.procyon.org.uk> References: <153754786825.19213.16907802981157370740.stgit@warthog.procyon.org.uk> User-Agent: StGit/unknown-version MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.26 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.27]); Fri, 21 Sep 2018 16:38:06 +0000 (UTC) Sender: linux-fsdevel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Add fsinfo support to the AFS filesystem. Signed-off-by: David Howells --- fs/afs/super.c | 129 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) diff --git a/fs/afs/super.c b/fs/afs/super.c index 944985e0a3c8..1fe5026b1104 100644 --- a/fs/afs/super.c +++ b/fs/afs/super.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include "internal.h" @@ -34,6 +35,7 @@ static void afs_kill_super(struct super_block *sb); static struct inode *afs_alloc_inode(struct super_block *sb); static void afs_destroy_inode(struct inode *inode); static int afs_statfs(struct dentry *dentry, struct kstatfs *buf); +static int afs_fsinfo(struct path *path, struct fsinfo_kparams *params); static int afs_show_devname(struct seq_file *m, struct dentry *root); static int afs_show_options(struct seq_file *m, struct dentry *root); static int afs_init_fs_context(struct fs_context *fc, struct dentry *reference); @@ -53,6 +55,7 @@ int afs_net_id; static const struct super_operations afs_super_ops = { .statfs = afs_statfs, + .fsinfo = afs_fsinfo, .alloc_inode = afs_alloc_inode, .drop_inode = afs_drop_inode, .destroy_inode = afs_destroy_inode, @@ -769,3 +772,129 @@ static int afs_statfs(struct dentry *dentry, struct kstatfs *buf) return ret; } + +/* + * Get filesystem information. + */ +static int afs_fsinfo(struct path *path, struct fsinfo_kparams *params) +{ + struct fsinfo_timestamp_info *tsinfo; + struct fsinfo_server_address *addr; + struct fsinfo_capabilities *caps; + struct fsinfo_supports *sup; + struct dentry *dentry = path->dentry; + struct afs_server_list *slist; + struct afs_super_info *as = AFS_FS_S(dentry->d_sb); + struct afs_addr_list *alist; + struct afs_server *server; + struct afs_volume *volume = as->volume; + struct afs_cell *cell = as->cell; + struct afs_net *net = afs_d2net(dentry); + bool dyn_root = as->dyn_root; + int ret; + + switch (params->request) { + case FSINFO_ATTR_TIMESTAMP_INFO: + tsinfo = params->buffer; + tsinfo->minimum_timestamp = 0; + tsinfo->maximum_timestamp = UINT_MAX; + tsinfo->mtime_gran_mantissa = 1; + tsinfo->mtime_gran_exponent = 0; + return sizeof(*tsinfo); + + case FSINFO_ATTR_SUPPORTS: + sup = params->buffer; + sup->stx_mask = (STATX_TYPE | STATX_MODE | + STATX_NLINK | + STATX_UID | STATX_GID | + STATX_MTIME | STATX_INO | + STATX_SIZE); + sup->stx_attributes = STATX_ATTR_AUTOMOUNT; + return sizeof(*sup); + + case FSINFO_ATTR_CAPABILITIES: + caps = params->buffer; + if (dyn_root) { + fsinfo_set_cap(caps, FSINFO_CAP_IS_AUTOMOUNTER_FS); + fsinfo_set_cap(caps, FSINFO_CAP_AUTOMOUNTS); + } else { + fsinfo_set_cap(caps, FSINFO_CAP_IS_NETWORK_FS); + fsinfo_set_cap(caps, FSINFO_CAP_AUTOMOUNTS); + fsinfo_set_cap(caps, FSINFO_CAP_ADV_LOCKS); + fsinfo_set_cap(caps, FSINFO_CAP_UIDS); + fsinfo_set_cap(caps, FSINFO_CAP_GIDS); + fsinfo_set_cap(caps, FSINFO_CAP_VOLUME_ID); + fsinfo_set_cap(caps, FSINFO_CAP_VOLUME_NAME); + fsinfo_set_cap(caps, FSINFO_CAP_CELL_NAME); + fsinfo_set_cap(caps, FSINFO_CAP_IVER_MONO_INCR); + fsinfo_set_cap(caps, FSINFO_CAP_SYMLINKS); + fsinfo_set_cap(caps, FSINFO_CAP_HARD_LINKS_1DIR); + fsinfo_set_cap(caps, FSINFO_CAP_HAS_MTIME); + } + return sizeof(*caps); + + case FSINFO_ATTR_VOLUME_NAME: + if (dyn_root) + return -EOPNOTSUPP; + memcpy(params->buffer, volume->name, volume->name_len); + return volume->name_len; + + case FSINFO_ATTR_CELL_NAME: + if (dyn_root) + return -EOPNOTSUPP; + memcpy(params->buffer, cell->name, cell->name_len); + return cell->name_len; + + case FSINFO_ATTR_SERVER_NAME: + if (dyn_root) + return -EOPNOTSUPP; + read_lock(&volume->servers_lock); + slist = afs_get_serverlist(volume->servers); + read_unlock(&volume->servers_lock); + + if (params->Nth < slist->nr_servers) { + server = slist->servers[params->Nth].server; + ret = sprintf(params->buffer, "%pU", &server->uuid); + } else { + ret = -ENODATA; + } + + afs_put_serverlist(net, slist); + return ret; + + case FSINFO_ATTR_SERVER_ADDRESS: + addr = params->buffer; + if (dyn_root) + return -EOPNOTSUPP; + read_lock(&volume->servers_lock); + slist = afs_get_serverlist(volume->servers); + read_unlock(&volume->servers_lock); + + ret = -ENODATA; + if (params->Nth >= slist->nr_servers) + goto put_slist; + server = slist->servers[params->Nth].server; + + read_lock(&server->fs_lock); + alist = afs_get_addrlist(rcu_access_pointer(server->addresses)); + read_unlock(&server->fs_lock); + if (!alist) + goto put_slist; + + if (params->Mth >= alist->nr_addrs) + goto put_alist; + + memcpy(addr, &alist->addrs[params->Mth], + sizeof(struct sockaddr_rxrpc)); + ret = sizeof(*addr); + + put_alist: + afs_put_addrlist(alist); + put_slist: + afs_put_serverlist(net, slist); + return ret; + + default: + return generic_fsinfo(path, params); + } +} From patchwork Fri Sep 21 16:38:11 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Howells X-Patchwork-Id: 10610703 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 7F8B2913 for ; Fri, 21 Sep 2018 16:38:20 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 6E5A42DB32 for ; Fri, 21 Sep 2018 16:38:20 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 628052DE7A; Fri, 21 Sep 2018 16:38:20 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 116A12DB32 for ; Fri, 21 Sep 2018 16:38:20 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2390697AbeIUW1y (ORCPT ); Fri, 21 Sep 2018 18:27:54 -0400 Received: from mx1.redhat.com ([209.132.183.28]:50599 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2390433AbeIUW1y (ORCPT ); Fri, 21 Sep 2018 18:27:54 -0400 Received: from smtp.corp.redhat.com (int-mx10.intmail.prod.int.phx2.redhat.com [10.5.11.25]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id C0152307D910; Fri, 21 Sep 2018 16:38:13 +0000 (UTC) Received: from warthog.procyon.org.uk (ovpn-123-84.rdu2.redhat.com [10.10.123.84]) by smtp.corp.redhat.com (Postfix) with ESMTP id 933422010CF7; Fri, 21 Sep 2018 16:38:11 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH 3/5] vfs: Allow fsinfo() to query what's in an fs_context [ver #12] From: David Howells To: viro@zeniv.linux.org.uk Cc: torvalds@linux-foundation.org, dhowells@redhat.com, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, mszeredi@redhat.com Date: Fri, 21 Sep 2018 17:38:11 +0100 Message-ID: <153754789135.19213.18110372705954700799.stgit@warthog.procyon.org.uk> In-Reply-To: <153754786825.19213.16907802981157370740.stgit@warthog.procyon.org.uk> References: <153754786825.19213.16907802981157370740.stgit@warthog.procyon.org.uk> User-Agent: StGit/unknown-version MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.25 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.48]); Fri, 21 Sep 2018 16:38:13 +0000 (UTC) Sender: linux-fsdevel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Allow fsinfo() to be used to query the filesystem attached to an fs_context once a superblock has been created or if it comes from fspick(). This is done with something like: fd = fsopen("ext4", 0); ... fsconfig(fd, fsconfig_cmd_create, ...); fsinfo(fd, NULL, ...); Signed-off-by: David Howells --- fs/statfs.c | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/fs/statfs.c b/fs/statfs.c index f3cf8aa42a3d..790e21367d70 100644 --- a/fs/statfs.c +++ b/fs/statfs.c @@ -636,7 +636,8 @@ int vfs_fsinfo(struct path *path, struct fsinfo_kparams *params) return ret; if (params->request == FSINFO_ATTR_IDS && - params->buffer) { + params->buffer && + path->mnt) { struct fsinfo_ids *p = params->buffer; p->f_flags |= flags_by_mnt(path->mnt->mnt_flags); @@ -677,13 +678,40 @@ static int vfs_fsinfo_path(int dfd, const char __user *filename, return ret; } +static int vfs_fsinfo_fscontext(struct fs_context *fc, + struct fsinfo_kparams *params) +{ + int ret; + + if (fc->ops == &legacy_fs_context_ops) + return -EOPNOTSUPP; + + ret = mutex_lock_interruptible(&fc->uapi_mutex); + if (ret < 0) + return ret; + + ret = -EIO; + if (fc->root) { + struct path path = { .dentry = fc->root }; + + ret = vfs_fsinfo(&path, params); + } + + mutex_unlock(&fc->uapi_mutex); + return ret; +} + static int vfs_fsinfo_fd(unsigned int fd, struct fsinfo_kparams *params) { struct fd f = fdget_raw(fd); int ret = -EBADF; if (f.file) { - ret = vfs_fsinfo(&f.file->f_path, params); + if (f.file->f_op == &fscontext_fops) + ret = vfs_fsinfo_fscontext(f.file->private_data, + params); + else + ret = vfs_fsinfo(&f.file->f_path, params); fdput(f); } return ret; From patchwork Fri Sep 21 16:38:19 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Howells X-Patchwork-Id: 10610705 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 5BDE5112B for ; Fri, 21 Sep 2018 16:38:27 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 49BAA2DB32 for ; Fri, 21 Sep 2018 16:38:27 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 3C35E2DE7A; Fri, 21 Sep 2018 16:38:27 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 3CA6E2DB32 for ; Fri, 21 Sep 2018 16:38:26 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2390854AbeIUW2C (ORCPT ); Fri, 21 Sep 2018 18:28:02 -0400 Received: from mx1.redhat.com ([209.132.183.28]:38078 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2389683AbeIUW2C (ORCPT ); Fri, 21 Sep 2018 18:28:02 -0400 Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.phx2.redhat.com [10.5.11.22]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 3DC7830833C4; Fri, 21 Sep 2018 16:38:21 +0000 (UTC) Received: from warthog.procyon.org.uk (ovpn-123-84.rdu2.redhat.com [10.10.123.84]) by smtp.corp.redhat.com (Postfix) with ESMTP id B856810694C4; Fri, 21 Sep 2018 16:38:19 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH 4/5] vfs: Allow fsinfo() to be used to query an fs parameter description [ver #12] From: David Howells To: viro@zeniv.linux.org.uk Cc: torvalds@linux-foundation.org, dhowells@redhat.com, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, mszeredi@redhat.com Date: Fri, 21 Sep 2018 17:38:19 +0100 Message-ID: <153754789899.19213.6434349758145214487.stgit@warthog.procyon.org.uk> In-Reply-To: <153754786825.19213.16907802981157370740.stgit@warthog.procyon.org.uk> References: <153754786825.19213.16907802981157370740.stgit@warthog.procyon.org.uk> User-Agent: StGit/unknown-version MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.22 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.44]); Fri, 21 Sep 2018 16:38:21 +0000 (UTC) Sender: linux-fsdevel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Provide fsinfo() attributes that can be used to query a filesystem parameter description. To do this, fsinfo() can be called on an fs_context that doesn't yet have a superblock created and attached. It can be obtained by doing, for example: fd = fsopen("ext4", 0); struct fsinfo_param_name name; struct fsinfo_params params = { .request = fsinfo_attr_param_name, .Nth = 3, }; fsinfo(fd, NULL, ¶ms, &name, sizeof(name)); to query the 4th parameter name in the name to parameter ID mapping table. Signed-off-by: David Howells --- fs/statfs.c | 98 +++++++++++++++++++++++++++++++ include/uapi/linux/fsinfo.h | 68 +++++++++++++++++++++ samples/vfs/Makefile | 2 + samples/vfs/test-fs-query.c | 137 +++++++++++++++++++++++++++++++++++++++++++ samples/vfs/test-fsinfo.c | 17 +++++ 5 files changed, 322 insertions(+) create mode 100644 samples/vfs/test-fs-query.c diff --git a/fs/statfs.c b/fs/statfs.c index 790e21367d70..0e348b6c8241 100644 --- a/fs/statfs.c +++ b/fs/statfs.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "internal.h" static int flags_by_mnt(int mnt_flags) @@ -576,14 +577,90 @@ static int fsinfo_generic_io_size(struct dentry *dentry, return sizeof(*c); } +static int fsinfo_generic_param_description(struct file_system_type *f, + struct fsinfo_kparams *params) +{ + const struct fs_parameter_description *desc = f->parameters; + struct fsinfo_param_description *p = params->buffer; + + if (!desc) + return -ENODATA; + + p->nr_params = desc->nr_params; + p->nr_names = desc->nr_params + desc->nr_alt_keys; + p->nr_enum_names = desc->nr_enums; + p->source_param = desc->no_source ? UINT_MAX : desc->source_param; + return sizeof(*p); +} + +static int fsinfo_generic_param_specification(struct file_system_type *f, + struct fsinfo_kparams *params) +{ + const struct fs_parameter_description *desc = f->parameters; + struct fsinfo_param_specification *p = params->buffer; + + if (!desc || !desc->specs || params->Nth >= desc->nr_params) + return -ENODATA; + + p->type = desc->specs[params->Nth].type; + p->flags = desc->specs[params->Nth].flags; + return sizeof(*p); +} + +static int fsinfo_generic_param_name(struct file_system_type *f, + struct fsinfo_kparams *params) +{ + const struct fs_parameter_description *desc = f->parameters; + struct fsinfo_param_name *p = params->buffer; + const char *name; + unsigned int n = params->Nth; + + if (!desc || !desc->keys) + return -ENODATA; + + if (n < desc->nr_params) { + p->param_index = n; + name = desc->keys[n]; + goto out; + } + + n -= desc->nr_params; + if (n < desc->nr_alt_keys) { + p->param_index = desc->alt_keys[n].value; + name = desc->alt_keys[n].name; + goto out; + } + return -ENODATA; + +out: + strcpy(p->name, name); + return sizeof(*p); +} + +static int fsinfo_generic_param_enum(struct file_system_type *f, + struct fsinfo_kparams *params) +{ + const struct fs_parameter_description *desc = f->parameters; + struct fsinfo_param_enum *p = params->buffer; + + if (!desc || !desc->enums || params->Nth >= desc->nr_enums) + return -ENODATA; + + p->param_index = desc->enums[params->Nth].param_id; + strcpy(p->name, desc->enums[params->Nth].name); + return sizeof(*p); +} + /* * Implement some queries generically from stuff in the superblock. */ int generic_fsinfo(struct path *path, struct fsinfo_kparams *params) { struct dentry *dentry = path->dentry; + struct file_system_type *f = dentry->d_sb->s_type; #define _gen(X, Y) FSINFO_ATTR_##X: return fsinfo_generic_##Y(dentry, params->buffer) +#define _genf(X, Y) FSINFO_ATTR_##X: return fsinfo_generic_##Y(f, params) switch (params->request) { case _gen(STATFS, statfs); @@ -596,6 +673,10 @@ int generic_fsinfo(struct path *path, struct fsinfo_kparams *params) case _gen(VOLUME_ID, volume_id); case _gen(NAME_ENCODING, name_encoding); case _gen(IO_SIZE, io_size); + case _genf(PARAM_DESCRIPTION, param_description); + case _genf(PARAM_SPECIFICATION, param_specification); + case _genf(PARAM_NAME, param_name); + case _genf(PARAM_ENUM, param_enum); default: return -EOPNOTSUPP; } @@ -681,11 +762,24 @@ static int vfs_fsinfo_path(int dfd, const char __user *filename, static int vfs_fsinfo_fscontext(struct fs_context *fc, struct fsinfo_kparams *params) { + struct file_system_type *f = fc->fs_type; int ret; if (fc->ops == &legacy_fs_context_ops) return -EOPNOTSUPP; + /* Filesystem parameter query is static information and doesn't need a + * lock to read it. + */ + switch (params->request) { + case _genf(PARAM_DESCRIPTION, param_description); + case _genf(PARAM_SPECIFICATION, param_specification); + case _genf(PARAM_NAME, param_name); + case _genf(PARAM_ENUM, param_enum); + default: + break; + } + ret = mutex_lock_interruptible(&fc->uapi_mutex); if (ret < 0) return ret; @@ -759,6 +853,10 @@ static const u16 fsinfo_buffer_sizes[FSINFO_ATTR__NR] = { FSINFO_STRING (NAME_ENCODING, name_encoding), FSINFO_STRING (NAME_CODEPAGE, name_codepage), FSINFO_STRUCT (IO_SIZE, io_size), + FSINFO_STRUCT (PARAM_DESCRIPTION, param_description), + FSINFO_STRUCT_N (PARAM_SPECIFICATION, param_specification), + FSINFO_STRUCT_N (PARAM_NAME, param_name), + FSINFO_STRUCT_N (PARAM_ENUM, param_enum), }; /** diff --git a/include/uapi/linux/fsinfo.h b/include/uapi/linux/fsinfo.h index ab7f1e6ab60b..ec2ff86f49ce 100644 --- a/include/uapi/linux/fsinfo.h +++ b/include/uapi/linux/fsinfo.h @@ -34,6 +34,10 @@ enum fsinfo_attribute { FSINFO_ATTR_NAME_ENCODING = 16, /* Filename encoding (string) */ FSINFO_ATTR_NAME_CODEPAGE = 17, /* Filename codepage (string) */ FSINFO_ATTR_IO_SIZE = 18, /* Optimal I/O sizes */ + FSINFO_ATTR_PARAM_DESCRIPTION = 19, /* General fs parameter description */ + FSINFO_ATTR_PARAM_SPECIFICATION = 20, /* Nth parameter specification */ + FSINFO_ATTR_PARAM_NAME = 21, /* Nth name to param index */ + FSINFO_ATTR_PARAM_ENUM = 22, /* Nth enum-to-val */ FSINFO_ATTR__NR }; @@ -231,4 +235,68 @@ struct fsinfo_fsinfo { __u32 max_cap; /* Number of supported capabilities (fsinfo_cap__nr) */ }; +/* + * Information struct for fsinfo(fsinfo_attr_param_description). + * + * Query the parameter set for a filesystem. + */ +struct fsinfo_param_description { + __u32 nr_params; /* Number of individual parameters */ + __u32 nr_names; /* Number of parameter names */ + __u32 nr_enum_names; /* Number of enum names */ + __u32 source_param; /* Source parameter index (or UINT_MAX) */ +}; + +/* + * Information struct for fsinfo(fsinfo_attr_param_specification). + * + * Query the specification of the Nth filesystem parameter. + */ +struct fsinfo_param_specification { + __u32 type; /* enum fsinfo_param_specification_type */ + __u32 flags; /* Qualifiers */ +}; + +enum fsinfo_param_specification_type { + FSINFO_PARAM_SPEC_NOT_DEFINED, + FSINFO_PARAM_SPEC_TAKES_NO_VALUE, + FSINFO_PARAM_SPEC_IS_BOOL, + FSINFO_PARAM_SPEC_IS_U32, + FSINFO_PARAM_SPEC_IS_U32_OCTAL, + FSINFO_PARAM_SPEC_IS_U32_HEX, + FSINFO_PARAM_SPEC_IS_S32, + FSINFO_PARAM_SPEC_IS_ENUM, + FSINFO_PARAM_SPEC_IS_STRING, + FSINFO_PARAM_SPEC_IS_BLOB, + FSINFO_PARAM_SPEC_IS_BLOCKDEV, + FSINFO_PARAM_SPEC_IS_PATH, + FSINFO_PARAM_SPEC_IS_FD, + NR__FSINFO_PARAM_SPEC +}; + +#define FSINFO_PARAM_SPEC_VALUE_IS_OPTIONAL 0X00000001 +#define FSINFO_PARAM_SPEC_PREFIX_NO_IS_NEG 0X00000002 +#define FSINFO_PARAM_SPEC_EMPTY_STRING_IS_NEG 0X00000004 +#define FSINFO_PARAM_SPEC_DEPRECATED 0X00000008 + +/* + * Information struct for fsinfo(fsinfo_attr_param_name). + * + * Query the Nth filesystem parameter name + */ +struct fsinfo_param_name { + __u32 param_index; /* Index of the parameter specification */ + char name[252]; /* Name of the parameter */ +}; + +/* + * Information struct for fsinfo(fsinfo_attr_param_enum). + * + * Query the Nth filesystem enum parameter value name. + */ +struct fsinfo_param_enum { + __u32 param_index; /* Index of the relevant parameter specification */ + char name[252]; /* Name of the enum value */ +}; + #endif /* _UAPI_LINUX_FSINFO_H */ diff --git a/samples/vfs/Makefile b/samples/vfs/Makefile index d2e1ac8d66ea..8552a347ccc2 100644 --- a/samples/vfs/Makefile +++ b/samples/vfs/Makefile @@ -1,6 +1,7 @@ # List of programs to build hostprogs-$(CONFIG_SAMPLE_VFS) := \ test-fsinfo \ + test-fs-query \ test-fsmount \ test-statx @@ -10,5 +11,6 @@ always := $(hostprogs-y) HOSTCFLAGS_test-fsinfo.o += -I$(objtree)/usr/include HOSTLDLIBS_test-fsinfo += -lm +HOSTCFLAGS_test-fs-query.o += -I$(objtree)/usr/include HOSTCFLAGS_test-fsmount.o += -I$(objtree)/usr/include HOSTCFLAGS_test-statx.o += -I$(objtree)/usr/include diff --git a/samples/vfs/test-fs-query.c b/samples/vfs/test-fs-query.c new file mode 100644 index 000000000000..25c69d0df084 --- /dev/null +++ b/samples/vfs/test-fs-query.c @@ -0,0 +1,137 @@ +/* Test using the fsinfo() system call to query mount parameters. + * + * Copyright (C) 2018 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#define _GNU_SOURCE +#define _ATFILE_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int fsopen(const char *fs_name, unsigned int flags) +{ + return syscall(__NR_fsopen, fs_name, flags); +} + +static ssize_t fsinfo(int dfd, const char *filename, struct fsinfo_params *params, + void *buffer, size_t buf_size) +{ + return syscall(__NR_fsinfo, dfd, filename, params, buffer, buf_size); +} + +static const char *param_types[NR__FSINFO_PARAM_SPEC] = { + [FSINFO_PARAM_SPEC_NOT_DEFINED] = "?undef", + [FSINFO_PARAM_SPEC_TAKES_NO_VALUE] = "no-val", + [FSINFO_PARAM_SPEC_IS_BOOL] = "bool", + [FSINFO_PARAM_SPEC_IS_U32] = "u32", + [FSINFO_PARAM_SPEC_IS_U32_OCTAL] = "octal", + [FSINFO_PARAM_SPEC_IS_U32_HEX] = "hex", + [FSINFO_PARAM_SPEC_IS_S32] = "s32", + [FSINFO_PARAM_SPEC_IS_ENUM] = "enum", + [FSINFO_PARAM_SPEC_IS_STRING] = "string", + [FSINFO_PARAM_SPEC_IS_BLOB] = "binary", + [FSINFO_PARAM_SPEC_IS_BLOCKDEV] = "blockdev", + [FSINFO_PARAM_SPEC_IS_PATH] = "path", + [FSINFO_PARAM_SPEC_IS_FD] = "fd", +}; + +/* + * + */ +int main(int argc, char **argv) +{ + struct fsinfo_param_description desc; + struct fsinfo_param_specification spec; + struct fsinfo_param_name name; + struct fsinfo_param_enum enum_name; + + struct fsinfo_params params = { + .at_flags = AT_SYMLINK_NOFOLLOW, + }; + int fd; + + if (argc != 2) { + printf("Format: test-fs-query \n"); + exit(2); + } + + fd = fsopen(argv[1], 0); + if (fd == -1) { + perror(argv[1]); + exit(1); + } + + params.request = FSINFO_ATTR_PARAM_DESCRIPTION; + if (fsinfo(fd, NULL, ¶ms, &desc, sizeof(desc)) == -1) { + perror("fsinfo/desc"); + exit(1); + } + + printf("Filesystem %s has %u parameters\n", argv[1], desc.nr_params); + + params.request = FSINFO_ATTR_PARAM_SPECIFICATION; + for (params.Nth = 0; params.Nth < desc.nr_params; params.Nth++) { + if (fsinfo(fd, NULL, ¶ms, &spec, sizeof(spec)) == -1) { + if (errno == ENODATA) + break; + perror("fsinfo/spec"); + exit(1); + } + printf("- PARAM[%3u] type=%u(%s)%s%s%s%s\n", + params.Nth, + spec.type, + spec.type < NR__FSINFO_PARAM_SPEC ? param_types[spec.type] : "?type", + spec.flags & FSINFO_PARAM_SPEC_VALUE_IS_OPTIONAL ? " -opt" : "", + spec.flags & FSINFO_PARAM_SPEC_PREFIX_NO_IS_NEG ? " -neg-no" : "", + spec.flags & FSINFO_PARAM_SPEC_EMPTY_STRING_IS_NEG ? " -neg-empty" : "", + spec.flags & FSINFO_PARAM_SPEC_DEPRECATED ? " -dep" : ""); + } + + printf("Filesystem has %u parameter names\n", desc.nr_names); + + params.request = FSINFO_ATTR_PARAM_NAME; + for (params.Nth = 0; params.Nth < desc.nr_names; params.Nth++) { + if (fsinfo(fd, NULL, ¶ms, &name, sizeof(name)) == -1) { + if (errno == ENODATA) + break; + perror("fsinfo/name"); + exit(1); + } + printf("- NAME[%3u] %s -> %u\n", + params.Nth, name.name, name.param_index); + } + + printf("Filesystem has %u enumeration values\n", desc.nr_enum_names); + + params.request = FSINFO_ATTR_PARAM_ENUM; + for (params.Nth = 0; params.Nth < desc.nr_enum_names; params.Nth++) { + if (fsinfo(fd, NULL, ¶ms, &enum_name, sizeof(enum_name)) == -1) { + if (errno == ENODATA) + break; + perror("fsinfo/enum"); + exit(1); + } + printf("- ENUM[%3u] %3u.%s\n", + params.Nth, enum_name.param_index, enum_name.name); + } + return 0; +} diff --git a/samples/vfs/test-fsinfo.c b/samples/vfs/test-fsinfo.c index dbb231c30153..75f5c2a61445 100644 --- a/samples/vfs/test-fsinfo.c +++ b/samples/vfs/test-fsinfo.c @@ -63,6 +63,10 @@ static const __u16 fsinfo_buffer_sizes[FSINFO_ATTR__NR] = { FSINFO_STRING (NAME_ENCODING, name_encoding), FSINFO_STRING (NAME_CODEPAGE, name_codepage), FSINFO_STRUCT (IO_SIZE, io_size), + FSINFO_STRUCT (PARAM_DESCRIPTION, param_description), + FSINFO_STRUCT_N (PARAM_SPECIFICATION, param_specification), + FSINFO_STRUCT_N (PARAM_NAME, param_name), + FSINFO_STRUCT_N (PARAM_ENUM, param_enum), }; #define FSINFO_NAME(X,Y) [FSINFO_ATTR_##X] = #Y @@ -86,6 +90,10 @@ static const char *fsinfo_attr_names[FSINFO_ATTR__NR] = { FSINFO_NAME (NAME_ENCODING, name_encoding), FSINFO_NAME (NAME_CODEPAGE, name_codepage), FSINFO_NAME (IO_SIZE, io_size), + FSINFO_NAME (PARAM_DESCRIPTION, param_description), + FSINFO_NAME (PARAM_SPECIFICATION, param_specification), + FSINFO_NAME (PARAM_NAME, param_name), + FSINFO_NAME (PARAM_ENUM, param_enum), }; union reply { @@ -535,6 +543,15 @@ int main(int argc, char **argv) } for (attr = 0; attr <= FSINFO_ATTR__NR; attr++) { + switch (attr) { + case FSINFO_ATTR_PARAM_DESCRIPTION: + case FSINFO_ATTR_PARAM_SPECIFICATION: + case FSINFO_ATTR_PARAM_NAME: + case FSINFO_ATTR_PARAM_ENUM: + /* See test-fs-query.c instead */ + continue; + } + Nth = 0; do { Mth = 0; From patchwork Fri Sep 21 16:38:26 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Howells X-Patchwork-Id: 10610707 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 9BADE913 for ; Fri, 21 Sep 2018 16:38:31 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 47D522DB32 for ; Fri, 21 Sep 2018 16:38:31 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 3B7C22E019; Fri, 21 Sep 2018 16:38:31 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 4AEB92DB32 for ; Fri, 21 Sep 2018 16:38:30 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2390876AbeIUW2J (ORCPT ); Fri, 21 Sep 2018 18:28:09 -0400 Received: from mx1.redhat.com ([209.132.183.28]:52650 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2390873AbeIUW2J (ORCPT ); Fri, 21 Sep 2018 18:28:09 -0400 Received: from smtp.corp.redhat.com (int-mx12.intmail.prod.int.phx2.redhat.com [10.5.11.27]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 8C99430001D8; Fri, 21 Sep 2018 16:38:28 +0000 (UTC) Received: from warthog.procyon.org.uk (ovpn-123-84.rdu2.redhat.com [10.10.123.84]) by smtp.corp.redhat.com (Postfix) with ESMTP id 2A56689247; Fri, 21 Sep 2018 16:38:27 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH 5/5] vfs: Implement parameter value retrieval with fsinfo() [ver #12] From: David Howells To: viro@zeniv.linux.org.uk Cc: torvalds@linux-foundation.org, dhowells@redhat.com, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, mszeredi@redhat.com Date: Fri, 21 Sep 2018 17:38:26 +0100 Message-ID: <153754790645.19213.6268357372779664850.stgit@warthog.procyon.org.uk> In-Reply-To: <153754786825.19213.16907802981157370740.stgit@warthog.procyon.org.uk> References: <153754786825.19213.16907802981157370740.stgit@warthog.procyon.org.uk> User-Agent: StGit/unknown-version MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.27 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.40]); Fri, 21 Sep 2018 16:38:28 +0000 (UTC) Sender: linux-fsdevel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Implement parameter value retrieval with fsinfo() - akin to parsing /proc/mounts. This allows the values of each parameter to be retrieved in an order corresponding to the Nth index used by FSINFO_ATTR_PARAM_SPECIFICATION. If a parameter isn't set, an empty string is returned. Parameters may have multiple values, which can be accessed using the Mth index. For example, set: struct fsinfo_params params = { .request = FSINFO_ATTR_PARAM_SPECIFICATION, .Nth = 3, }; this can be passed to fsinfo() to retrieve the type information for the parameter #3. Value #2 of parameter #3 can then be retrieved using: struct fsinfo_params params = { .request = FSINFO_ATTR_PARAM_SPECIFICATION, .Nth = 3, .Mth = 2, }; fsinfo() returned -ENODATA if Nth is beyond the last parameter or Mth is beyond the last value of that parameter. Note that it is permissible for the filesystem to add extra values on the end beyond the number of specs returned by FSINFO_ATTR_PARAM_SPECIFICATION. This is used by cgroup-v1 to list the supported subsystem names after the standard parameters. Signed-off-by: David Howells --- fs/afs/internal.h | 1 + fs/afs/super.c | 39 ++++++++++++++++++++++- fs/hugetlbfs/inode.c | 66 +++++++++++++++++++++++++++++++++++++++ fs/kernfs/mount.c | 16 ++++++++++ include/linux/kernfs.h | 2 + include/uapi/linux/fsinfo.h | 1 + kernel/cgroup/cgroup-v1.c | 72 +++++++++++++++++++++++++++++++++++++++++++ kernel/cgroup/cgroup.c | 31 +++++++++++++++++++ samples/vfs/test-fs-query.c | 1 + 9 files changed, 227 insertions(+), 2 deletions(-) diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 7a603398b69e..2ecfa32f26ac 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -196,6 +196,7 @@ struct afs_super_info { struct afs_cell *cell; /* The cell in which the volume resides */ struct afs_volume *volume; /* volume record */ bool dyn_root; /* True if dynamic root */ + bool autocell; /* True if autocell */ }; static inline struct afs_super_info *AFS_FS_S(struct super_block *sb) diff --git a/fs/afs/super.c b/fs/afs/super.c index 1fe5026b1104..14abf604e8d3 100644 --- a/fs/afs/super.c +++ b/fs/afs/super.c @@ -197,7 +197,7 @@ static int afs_show_options(struct seq_file *m, struct dentry *root) if (as->dyn_root) seq_puts(m, ",dyn"); - if (test_bit(AFS_VNODE_AUTOCELL, &AFS_FS_I(d_inode(root))->flags)) + if (as->autocell) seq_puts(m, ",autocell"); return 0; } @@ -444,7 +444,7 @@ static int afs_fill_super(struct super_block *sb, struct afs_fs_context *ctx) if (IS_ERR(inode)) return PTR_ERR(inode); - if (ctx->autocell || as->dyn_root) + if (as->autocell || as->dyn_root) set_bit(AFS_VNODE_AUTOCELL, &AFS_FS_I(inode)->flags); ret = -ENOMEM; @@ -483,6 +483,8 @@ static struct afs_super_info *afs_alloc_sbi(struct fs_context *fc) as->cell = afs_get_cell(ctx->cell); as->volume = __afs_get_volume(ctx->volume); } + if (ctx->autocell) + as->autocell = true; } return as; } @@ -790,6 +792,7 @@ static int afs_fsinfo(struct path *path, struct fsinfo_kparams *params) struct afs_volume *volume = as->volume; struct afs_cell *cell = as->cell; struct afs_net *net = afs_d2net(dentry); + const char *str = NULL; bool dyn_root = as->dyn_root; int ret; @@ -894,7 +897,39 @@ static int afs_fsinfo(struct path *path, struct fsinfo_kparams *params) afs_put_serverlist(net, slist); return ret; + case FSINFO_ATTR_PARAMETER: + if (params->Mth) + return -ENODATA; + switch (params->Nth) { + case Opt_source: + if (dyn_root) + return 0; + return sprintf(params->buffer, "source=%c%s:%s%s", + volume->type == AFSVL_RWVOL ? '%' : '#', + cell->name, + volume->name, + volume->type == AFSVL_RWVOL ? "" : + volume->type == AFSVL_ROVOL ? ".readonly" : + ".backup"); + case Opt_autocell: + if (as->autocell) + str = "autocell"; + goto string; + case Opt_dyn: + if (dyn_root) + str = "dyn"; + goto string; + default: + return -ENODATA; + } + default: return generic_fsinfo(path, params); } + +string: + if (!str) + return 0; + strcpy(params->buffer, str); + return strlen(params->buffer); } diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c index 700b009af8e4..762028994f47 100644 --- a/fs/hugetlbfs/inode.c +++ b/fs/hugetlbfs/inode.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -947,6 +948,70 @@ static int hugetlbfs_show_options(struct seq_file *m, struct dentry *root) return 0; } +static int hugetlbfs_fsinfo(struct path *path, struct fsinfo_kparams *params) +{ + struct dentry *dentry = path->dentry; + struct hugetlbfs_sb_info *sbinfo = HUGETLBFS_SB(dentry->d_sb); + struct hugepage_subpool *spool = sbinfo->spool; + unsigned long hpage_size = huge_page_size(sbinfo->hstate); + unsigned hpage_shift = huge_page_shift(sbinfo->hstate); + char mod; + + switch (params->request) { + case FSINFO_ATTR_PARAMETER: + if (params->Mth) + return -ENODATA; + switch (params->Nth) { + case Opt_uid: + if (!uid_eq(sbinfo->uid, GLOBAL_ROOT_UID)) + return sprintf(params->buffer, "uid=%u", + from_kuid_munged(&init_user_ns, + sbinfo->uid)); + return 0; + case Opt_gid: + if (!gid_eq(sbinfo->gid, GLOBAL_ROOT_GID)) + return sprintf(params->buffer, "gid=%u", + from_kgid_munged(&init_user_ns, + sbinfo->gid)); + return 0; + + case Opt_size: + if (!spool || spool->max_hpages == -1) + return 0; + return sprintf(params->buffer, "size=%llu", + (unsigned long long)spool->max_hpages << hpage_shift); + case Opt_min_size: + if (!spool || spool->min_hpages == -1) + return 0; + return sprintf(params->buffer, "min_size=%llu", + (unsigned long long)spool->min_hpages << hpage_shift); + case Opt_pagesize: + hpage_size /= 1024; + mod = 'K'; + if (hpage_size >= 1024) { + hpage_size /= 1024; + mod = 'M'; + } + return sprintf(params->buffer, "pagesize=%lu%c", + hpage_size, mod); + + case Opt_mode: + if (sbinfo->mode == 0755) + return 0; + return sprintf(params->buffer, "mode=%o", sbinfo->mode); + case Opt_nr_inodes: + if (sbinfo->max_inodes == -1) + return 0; + return sprintf(params->buffer, "nr_inodes=%lu", + sbinfo->max_inodes); + default: + return -ENODATA; + } + default: + return generic_fsinfo(path, params); + } +} + static int hugetlbfs_statfs(struct dentry *dentry, struct kstatfs *buf) { struct hugetlbfs_sb_info *sbinfo = HUGETLBFS_SB(dentry->d_sb); @@ -1106,6 +1171,7 @@ static const struct super_operations hugetlbfs_ops = { .statfs = hugetlbfs_statfs, .put_super = hugetlbfs_put_super, .show_options = hugetlbfs_show_options, + .fsinfo = hugetlbfs_fsinfo, }; /* diff --git a/fs/kernfs/mount.c b/fs/kernfs/mount.c index 56742632956c..1bd43f6947f3 100644 --- a/fs/kernfs/mount.c +++ b/fs/kernfs/mount.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "kernfs-internal.h" @@ -55,6 +56,20 @@ static int kernfs_sop_show_path(struct seq_file *sf, struct dentry *dentry) return 0; } +static int kernfs_sop_fsinfo(struct path *path, struct fsinfo_kparams *params) +{ + struct kernfs_root *root = kernfs_root(kernfs_dentry_node(path->dentry)); + struct kernfs_syscall_ops *scops = root->syscall_ops; + int ret; + + if (scops && scops->fsinfo) { + ret = scops->fsinfo(root, params); + if (ret != -EAGAIN) + return ret; + } + return generic_fsinfo(path, params); +} + const struct super_operations kernfs_sops = { .statfs = simple_statfs, .drop_inode = generic_delete_inode, @@ -62,6 +77,7 @@ const struct super_operations kernfs_sops = { .show_options = kernfs_sop_show_options, .show_path = kernfs_sop_show_path, + .fsinfo = kernfs_sop_fsinfo, }; /* diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index 051709212f55..631b52861763 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -27,6 +27,7 @@ struct vm_area_struct; struct super_block; struct file_system_type; struct fs_context; +struct fsinfo_kparams; struct kernfs_fs_context; struct kernfs_open_node; @@ -172,6 +173,7 @@ struct kernfs_node { struct kernfs_syscall_ops { int (*reconfigure)(struct kernfs_root *root, struct fs_context *fc); int (*show_options)(struct seq_file *sf, struct kernfs_root *root); + int (*fsinfo)(struct kernfs_root *root, struct fsinfo_kparams *params); int (*mkdir)(struct kernfs_node *parent, const char *name, umode_t mode); diff --git a/include/uapi/linux/fsinfo.h b/include/uapi/linux/fsinfo.h index ec2ff86f49ce..50e6cd50fe63 100644 --- a/include/uapi/linux/fsinfo.h +++ b/include/uapi/linux/fsinfo.h @@ -265,6 +265,7 @@ enum fsinfo_param_specification_type { FSINFO_PARAM_SPEC_IS_U32_OCTAL, FSINFO_PARAM_SPEC_IS_U32_HEX, FSINFO_PARAM_SPEC_IS_S32, + FSINFO_PARAM_SPEC_IS_U64, FSINFO_PARAM_SPEC_IS_ENUM, FSINFO_PARAM_SPEC_IS_STRING, FSINFO_PARAM_SPEC_IS_BLOB, diff --git a/kernel/cgroup/cgroup-v1.c b/kernel/cgroup/cgroup-v1.c index d5ae888b8c57..d20128d00fbe 100644 --- a/kernel/cgroup/cgroup-v1.c +++ b/kernel/cgroup/cgroup-v1.c @@ -14,6 +14,7 @@ #include #include #include +#include #include @@ -948,6 +949,76 @@ const struct fs_parameter_description cgroup1_fs_parameters = { .no_source = true, }; +static int cgroup1_fsinfo(struct kernfs_root *kf_root, struct fsinfo_kparams *params) +{ + struct cgroup_root *root = cgroup_root_from_kf(kf_root); + struct cgroup_subsys *ss; + const char *str = NULL; + unsigned int Mth; + int ret = 0, ssid; + + switch (params->request) { + case FSINFO_ATTR_PARAMETER: + if (params->Mth && params->Nth != nr__cgroup1_params) + return -ENODATA; + switch (params->Nth) { + case Opt_all: + return 0; + case Opt_clone_children: + if (test_bit(CGRP_CPUSET_CLONE_CHILDREN, &root->cgrp.flags)) + str = "clone_children"; + goto string; + case Opt_cpuset_v2_mode: + if (root->flags & CGRP_ROOT_CPUSET_V2_MODE) + str = "noprefix"; + goto string; + case Opt_name: + if (strlen(root->name)) + return sprintf(params->buffer, "name=%s", root->name); + return 0; + case Opt_none: + return 0; + case Opt_noprefix: + if (root->flags & CGRP_ROOT_NOPREFIX) + str = "noprefix"; + goto string; + case Opt_release_agent: + spin_lock(&release_agent_path_lock); + if (strlen(root->release_agent_path)) + ret = sprintf(params->buffer, "release_agent=%s", + root->release_agent_path); + spin_unlock(&release_agent_path_lock); + return ret; + case Opt_xattr: + if (root->flags & CGRP_ROOT_XATTR) + str = "noprefix"; + goto string; + case nr__cgroup1_params: + Mth = params->Mth; + for_each_subsys(ss, ssid) { + if (Mth == 0) { + if (root->subsys_mask & (1 << ssid)) + str = ss->legacy_name; + goto string; + } + Mth--; + } + return -ENODATA; + default: + return -ENODATA; + } + + default: + return -EAGAIN; /* Tell kernfs to call generic_fsinfo() */ + } + +string: + if (!str) + return 0; + strcpy(params->buffer, str); + return strlen(params->buffer); +} + int cgroup1_parse_param(struct fs_context *fc, struct fs_parameter *param) { struct cgroup_fs_context *ctx = cgroup_fc2context(fc); @@ -1138,6 +1209,7 @@ static int cgroup1_reconfigure(struct kernfs_root *kf_root, struct fs_context *f struct kernfs_syscall_ops cgroup1_kf_syscall_ops = { .rename = cgroup1_rename, .show_options = cgroup1_show_options, + .fsinfo = cgroup1_fsinfo, .reconfigure = cgroup1_reconfigure, .mkdir = cgroup_mkdir, .rmdir = cgroup_rmdir, diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c index 3c3c40cad257..6d147377d318 100644 --- a/kernel/cgroup/cgroup.c +++ b/kernel/cgroup/cgroup.c @@ -55,6 +55,7 @@ #include #include #include +#include #include #include @@ -1786,6 +1787,35 @@ static int cgroup_show_options(struct seq_file *seq, struct kernfs_root *kf_root return 0; } +static int cgroup_fsinfo(struct kernfs_root *kf_root, struct fsinfo_kparams *params) +{ + const char *str = NULL; + + switch (params->request) { + case FSINFO_ATTR_PARAMETER: + if (params->Mth) + return -ENODATA; + switch (params->Nth) { + case Opt_nsdelegate: + if (current->nsproxy->cgroup_ns == &init_cgroup_ns && + cgrp_dfl_root.flags & CGRP_ROOT_NS_DELEGATE) + str = "nsdelegate"; + goto string; + default: + return -ENODATA; + } + + default: + return -EAGAIN; /* Tell kernfs to call generic_fsinfo() */ + } + +string: + if (!str) + return 0; + strcpy(params->buffer, str); + return strlen(params->buffer); +} + static void apply_cgroup_root_flags(unsigned int root_flags) { if (current->nsproxy->cgroup_ns == &init_cgroup_ns) { @@ -5260,6 +5290,7 @@ int cgroup_rmdir(struct kernfs_node *kn) static struct kernfs_syscall_ops cgroup_kf_syscall_ops = { .show_options = cgroup_show_options, + .fsinfo = cgroup_fsinfo, .reconfigure = cgroup_reconfigure, .mkdir = cgroup_mkdir, .rmdir = cgroup_rmdir, diff --git a/samples/vfs/test-fs-query.c b/samples/vfs/test-fs-query.c index 25c69d0df084..511541d12b9e 100644 --- a/samples/vfs/test-fs-query.c +++ b/samples/vfs/test-fs-query.c @@ -46,6 +46,7 @@ static const char *param_types[NR__FSINFO_PARAM_SPEC] = { [FSINFO_PARAM_SPEC_IS_U32_OCTAL] = "octal", [FSINFO_PARAM_SPEC_IS_U32_HEX] = "hex", [FSINFO_PARAM_SPEC_IS_S32] = "s32", + [FSINFO_PARAM_SPEC_IS_U64] = "u64", [FSINFO_PARAM_SPEC_IS_ENUM] = "enum", [FSINFO_PARAM_SPEC_IS_STRING] = "string", [FSINFO_PARAM_SPEC_IS_BLOB] = "binary",