Re: [PATCH 03/19] fsinfo: Provide a bitmap of supported features [ver #16]

From: Darrick J. Wong
Date: Wed Feb 19 2020 - 11:37:23 EST


On Tue, Feb 18, 2020 at 05:05:20PM +0000, David Howells wrote:
> Provide a bitmap of features that a filesystem may provide for the path
> being queried. Features include such things as:
>
> (1) The general class of filesystem, such as kernel-interface,
> block-based, flash-based, network-based.
>
> (2) Supported inode features, such as which timestamps are supported,
> whether simple numeric user, group or project IDs are supported and
> whether user identification is actually more complex behind the
> scenes.
>
> (3) Supported volume features, such as it having a UUID, a name or a
> filesystem ID.
>
> (4) Supported filesystem features, such as what types of file are
> supported, whether sparse files, extended attributes and quotas are
> supported.
>
> (5) Supported interface features, such as whether locking and leases are
> supported, what open flags are honoured and how i_version is managed.
>
> For some filesystems, this may be an immutable set and can just be memcpy'd
> into the reply buffer.
> ---
>
> fs/fsinfo.c | 41 +++++++++++++++++++
> include/linux/fsinfo.h | 32 +++++++++++++++
> include/uapi/linux/fsinfo.h | 78 ++++++++++++++++++++++++++++++++++++
> samples/vfs/test-fsinfo.c | 93 ++++++++++++++++++++++++++++++++++++++++++-
> 4 files changed, 242 insertions(+), 2 deletions(-)
>
> diff --git a/fs/fsinfo.c b/fs/fsinfo.c
> index 3bc35b91f20b..55710d6da327 100644
> --- a/fs/fsinfo.c
> +++ b/fs/fsinfo.c
> @@ -36,6 +36,17 @@ int fsinfo_string(const char *s, struct fsinfo_context *ctx)
> }
> EXPORT_SYMBOL(fsinfo_string);
>
> +/*
> + * Get information about fsinfo() itself.
> + */
> +static int fsinfo_generic_fsinfo(struct path *path, struct fsinfo_context *ctx)
> +{
> + struct fsinfo_fsinfo *info = ctx->buffer;
> +
> + info->max_features = FSINFO_FEAT__NR;
> + return sizeof(*info);
> +}
> +
> /*
> * Get basic filesystem stats from statfs.
> */
> @@ -121,6 +132,33 @@ static int fsinfo_generic_supports(struct path *path, struct fsinfo_context *ctx
> return sizeof(*c);
> }
>
> +static int fsinfo_generic_features(struct path *path, struct fsinfo_context *ctx)
> +{
> + struct fsinfo_features *ft = ctx->buffer;
> + struct super_block *sb = path->dentry->d_sb;
> +
> + if (sb->s_mtd)
> + fsinfo_set_feature(ft, FSINFO_FEAT_IS_FLASH_FS);
> + else if (sb->s_bdev)
> + fsinfo_set_feature(ft, FSINFO_FEAT_IS_BLOCK_FS);
> +
> + if (sb->s_quota_types & QTYPE_MASK_USR)
> + fsinfo_set_feature(ft, FSINFO_FEAT_USER_QUOTAS);
> + if (sb->s_quota_types & QTYPE_MASK_GRP)
> + fsinfo_set_feature(ft, FSINFO_FEAT_GROUP_QUOTAS);
> + if (sb->s_quota_types & QTYPE_MASK_PRJ)
> + fsinfo_set_feature(ft, FSINFO_FEAT_PROJECT_QUOTAS);
> + if (sb->s_d_op && sb->s_d_op->d_automount)
> + fsinfo_set_feature(ft, FSINFO_FEAT_AUTOMOUNTS);
> + if (sb->s_id[0])
> + fsinfo_set_feature(ft, FSINFO_FEAT_VOLUME_ID);
> +
> + fsinfo_set_feature(ft, FSINFO_FEAT_HAS_ATIME);
> + fsinfo_set_feature(ft, FSINFO_FEAT_HAS_CTIME);
> + fsinfo_set_feature(ft, FSINFO_FEAT_HAS_MTIME);
> + return sizeof(*ft);
> +}
> +
> static const struct fsinfo_timestamp_info fsinfo_default_timestamp_info = {
> .atime = {
> .minimum = S64_MIN,
> @@ -252,6 +290,9 @@ static const struct fsinfo_attribute fsinfo_common_attributes[] = {
> FSINFO_VSTRUCT (FSINFO_ATTR_TIMESTAMP_INFO, fsinfo_generic_timestamp_info),
> FSINFO_STRING (FSINFO_ATTR_VOLUME_ID, fsinfo_generic_volume_id),
> FSINFO_VSTRUCT (FSINFO_ATTR_VOLUME_UUID, fsinfo_generic_volume_uuid),
> + FSINFO_VSTRUCT (FSINFO_ATTR_FEATURES, fsinfo_generic_features),
> +
> + FSINFO_VSTRUCT (FSINFO_ATTR_FSINFO, fsinfo_generic_fsinfo),
> FSINFO_VSTRUCT_N(FSINFO_ATTR_FSINFO_ATTRIBUTE_INFO, fsinfo_attribute_info),
> FSINFO_LIST (FSINFO_ATTR_FSINFO_ATTRIBUTES, fsinfo_attributes),
> {}
> diff --git a/include/linux/fsinfo.h b/include/linux/fsinfo.h
> index dcd55dbb02fa..564765c70659 100644
> --- a/include/linux/fsinfo.h
> +++ b/include/linux/fsinfo.h
> @@ -65,6 +65,38 @@ struct fsinfo_attribute {
>
> extern int fsinfo_string(const char *, struct fsinfo_context *);
>
> +static inline void fsinfo_set_feature(struct fsinfo_features *f,
> + enum fsinfo_feature feature)
> +{
> + f->features[feature / 8] |= 1 << (feature % 8);
> +}
> +
> +static inline void fsinfo_clear_feature(struct fsinfo_features *f,
> + enum fsinfo_feature feature)
> +{
> + f->features[feature / 8] &= ~(1 << (feature % 8));
> +}
> +
> +/**
> + * fsinfo_set_unix_features - Set standard UNIX features.
> + * @f: The features mask to alter
> + */
> +static inline void fsinfo_set_unix_features(struct fsinfo_features *f)
> +{
> + fsinfo_set_feature(f, FSINFO_FEAT_UIDS);
> + fsinfo_set_feature(f, FSINFO_FEAT_GIDS);
> + fsinfo_set_feature(f, FSINFO_FEAT_DIRECTORIES);
> + fsinfo_set_feature(f, FSINFO_FEAT_SYMLINKS);
> + fsinfo_set_feature(f, FSINFO_FEAT_HARD_LINKS);
> + fsinfo_set_feature(f, FSINFO_FEAT_DEVICE_FILES);
> + fsinfo_set_feature(f, FSINFO_FEAT_UNIX_SPECIALS);
> + fsinfo_set_feature(f, FSINFO_FEAT_SPARSE);
> + fsinfo_set_feature(f, FSINFO_FEAT_HAS_ATIME);
> + fsinfo_set_feature(f, FSINFO_FEAT_HAS_CTIME);
> + fsinfo_set_feature(f, FSINFO_FEAT_HAS_MTIME);
> + fsinfo_set_feature(f, FSINFO_FEAT_HAS_INODE_NUMBERS);
> +}
> +
> #endif /* CONFIG_FSINFO */
>
> #endif /* _LINUX_FSINFO_H */
> diff --git a/include/uapi/linux/fsinfo.h b/include/uapi/linux/fsinfo.h
> index 365d54fe9290..f40b5c0b5516 100644
> --- a/include/uapi/linux/fsinfo.h
> +++ b/include/uapi/linux/fsinfo.h
> @@ -22,9 +22,11 @@
> #define FSINFO_ATTR_VOLUME_ID 0x05 /* Volume ID (string) */
> #define FSINFO_ATTR_VOLUME_UUID 0x06 /* Volume UUID (LE uuid) */
> #define FSINFO_ATTR_VOLUME_NAME 0x07 /* Volume name (string) */
> +#define FSINFO_ATTR_FEATURES 0x08 /* Filesystem features (bits) */
>
> #define FSINFO_ATTR_FSINFO_ATTRIBUTE_INFO 0x100 /* Information about attr N (for path) */
> #define FSINFO_ATTR_FSINFO_ATTRIBUTES 0x101 /* List of supported attrs (for path) */
> +#define FSINFO_ATTR_FSINFO 0x102 /* Information about fsinfo() as a whole */
>
> /*
> * Optional fsinfo() parameter structure.
> @@ -45,6 +47,17 @@ struct fsinfo_params {
> __u64 __reserved[3];
> };
>
> +/*
> + * Information struct for fsinfo(FSINFO_ATTR_FSINFO).
> + *
> + * This gives information about fsinfo() itself.
> + */
> +struct fsinfo_fsinfo {
> + __u32 max_features; /* Number of supported features (fsinfo_features__nr) */
> +};
> +
> +#define FSINFO_ATTR_FSINFO__STRUCT struct fsinfo_fsinfo
> +
> enum fsinfo_value_type {
> FSINFO_TYPE_VSTRUCT = 0, /* Version-lengthed struct (up to 4096 bytes) */
> FSINFO_TYPE_STRING = 1, /* NUL-term var-length string (up to 4095 chars) */
> @@ -154,6 +167,71 @@ struct fsinfo_supports {
>
> #define FSINFO_ATTR_SUPPORTS__STRUCT struct fsinfo_supports
>
> +/*
> + * Information struct for fsinfo(FSINFO_ATTR_FEATURES).
> + *
> + * Bitmask indicating filesystem features where renderable as single bits.
> + */
> +enum fsinfo_feature {
> + FSINFO_FEAT_IS_KERNEL_FS = 0, /* fs is kernel-special filesystem */
> + FSINFO_FEAT_IS_BLOCK_FS = 1, /* fs is block-based filesystem */
> + FSINFO_FEAT_IS_FLASH_FS = 2, /* fs is flash filesystem */
> + FSINFO_FEAT_IS_NETWORK_FS = 3, /* fs is network filesystem */
> + FSINFO_FEAT_IS_AUTOMOUNTER_FS = 4, /* fs is automounter special filesystem */
> + FSINFO_FEAT_IS_MEMORY_FS = 5, /* fs is memory-based filesystem */
> + FSINFO_FEAT_AUTOMOUNTS = 6, /* fs supports automounts */
> + FSINFO_FEAT_ADV_LOCKS = 7, /* fs supports advisory file locking */
> + FSINFO_FEAT_MAND_LOCKS = 8, /* fs supports mandatory file locking */
> + FSINFO_FEAT_LEASES = 9, /* fs supports file leases */
> + FSINFO_FEAT_UIDS = 10, /* fs supports numeric uids */
> + FSINFO_FEAT_GIDS = 11, /* fs supports numeric gids */
> + FSINFO_FEAT_PROJIDS = 12, /* fs supports numeric project ids */
> + FSINFO_FEAT_STRING_USER_IDS = 13, /* fs supports string user identifiers */
> + FSINFO_FEAT_GUID_USER_IDS = 14, /* fs supports GUID user identifiers */
> + FSINFO_FEAT_WINDOWS_ATTRS = 15, /* fs has windows attributes */
> + FSINFO_FEAT_USER_QUOTAS = 16, /* fs has per-user quotas */
> + FSINFO_FEAT_GROUP_QUOTAS = 17, /* fs has per-group quotas */
> + FSINFO_FEAT_PROJECT_QUOTAS = 18, /* fs has per-project quotas */
> + FSINFO_FEAT_XATTRS = 19, /* fs has xattrs */
> + FSINFO_FEAT_JOURNAL = 20, /* fs has a journal */
> + FSINFO_FEAT_DATA_IS_JOURNALLED = 21, /* fs is using data journalling */
> + FSINFO_FEAT_O_SYNC = 22, /* fs supports O_SYNC */
> + FSINFO_FEAT_O_DIRECT = 23, /* fs supports O_DIRECT */
> + FSINFO_FEAT_VOLUME_ID = 24, /* fs has a volume ID */
> + FSINFO_FEAT_VOLUME_UUID = 25, /* fs has a volume UUID */
> + FSINFO_FEAT_VOLUME_NAME = 26, /* fs has a volume name */
> + FSINFO_FEAT_VOLUME_FSID = 27, /* fs has a volume FSID */
> + FSINFO_FEAT_IVER_ALL_CHANGE = 28, /* i_version represents data + meta changes */
> + FSINFO_FEAT_IVER_DATA_CHANGE = 29, /* i_version represents data changes only */
> + FSINFO_FEAT_IVER_MONO_INCR = 30, /* i_version incremented monotonically */
> + FSINFO_FEAT_DIRECTORIES = 31, /* fs supports (sub)directories */
> + FSINFO_FEAT_SYMLINKS = 32, /* fs supports symlinks */
> + FSINFO_FEAT_HARD_LINKS = 33, /* fs supports hard links */
> + FSINFO_FEAT_HARD_LINKS_1DIR = 34, /* fs supports hard links in same dir only */
> + FSINFO_FEAT_DEVICE_FILES = 35, /* fs supports bdev, cdev */
> + FSINFO_FEAT_UNIX_SPECIALS = 36, /* fs supports pipe, fifo, socket */
> + FSINFO_FEAT_RESOURCE_FORKS = 37, /* fs supports resource forks/streams */
> + FSINFO_FEAT_NAME_CASE_INDEP = 38, /* Filename case independence is mandatory */
> + FSINFO_FEAT_NAME_NON_UTF8 = 39, /* fs has non-utf8 names */
> + FSINFO_FEAT_NAME_HAS_CODEPAGE = 40, /* fs has a filename codepage */
> + FSINFO_FEAT_SPARSE = 41, /* fs supports sparse files */
> + FSINFO_FEAT_NOT_PERSISTENT = 42, /* fs is not persistent */
> + FSINFO_FEAT_NO_UNIX_MODE = 43, /* fs does not support unix mode bits */
> + FSINFO_FEAT_HAS_ATIME = 44, /* fs supports access time */
> + FSINFO_FEAT_HAS_BTIME = 45, /* fs supports birth/creation time */
> + FSINFO_FEAT_HAS_CTIME = 46, /* fs supports change time */
> + FSINFO_FEAT_HAS_MTIME = 47, /* fs supports modification time */
> + FSINFO_FEAT_HAS_ACL = 48, /* fs supports ACLs of some sort */
> + FSINFO_FEAT_HAS_INODE_NUMBERS = 49, /* fs has inode numbers */
> + FSINFO_FEAT__NR
> +};
> +
> +struct fsinfo_features {
> + __u8 features[(FSINFO_FEAT__NR + 7) / 8];

Hm...the structure size is pretty small (56 bytes) and will expand as we
add new _FEAT flags. Is this ok because the fsinfo code will truncate
its response to userspace to whatever buffer size userspace tells it?

--D

> +};
> +
> +#define FSINFO_ATTR_FEATURES__STRUCT struct fsinfo_features
> +
> struct fsinfo_timestamp_one {
> __s64 minimum; /* Minimum timestamp value in seconds */
> __u64 maximum; /* Maximum timestamp value in seconds */
> diff --git a/samples/vfs/test-fsinfo.c b/samples/vfs/test-fsinfo.c
> index 9a4d49db2996..6fbf0ce099b2 100644
> --- a/samples/vfs/test-fsinfo.c
> +++ b/samples/vfs/test-fsinfo.c
> @@ -107,6 +107,13 @@ static void dump_attribute_info(void *reply, unsigned int size)
> attr->name ? attr->name : "");
> }
>
> +static void dump_fsinfo_info(void *reply, unsigned int size)
> +{
> + struct fsinfo_fsinfo *f = reply;
> +
> + printf("Number of feature bits: %u\n", f->max_features);
> +}
> +
> static void dump_fsinfo_generic_statfs(void *reply, unsigned int size)
> {
> struct fsinfo_statfs *f = reply;
> @@ -172,6 +179,74 @@ static void dump_fsinfo_generic_supports(void *reply, unsigned int size)
> printf("\twin_fattrs : %x\n", f->win_file_attrs);
> }
>
> +#define FSINFO_FEATURE_NAME(C) [FSINFO_FEAT_##C] = #C
> +static const char *fsinfo_feature_names[FSINFO_FEAT__NR] = {
> + FSINFO_FEATURE_NAME(IS_KERNEL_FS),
> + FSINFO_FEATURE_NAME(IS_BLOCK_FS),
> + FSINFO_FEATURE_NAME(IS_FLASH_FS),
> + FSINFO_FEATURE_NAME(IS_NETWORK_FS),
> + FSINFO_FEATURE_NAME(IS_AUTOMOUNTER_FS),
> + FSINFO_FEATURE_NAME(IS_MEMORY_FS),
> + FSINFO_FEATURE_NAME(AUTOMOUNTS),
> + FSINFO_FEATURE_NAME(ADV_LOCKS),
> + FSINFO_FEATURE_NAME(MAND_LOCKS),
> + FSINFO_FEATURE_NAME(LEASES),
> + FSINFO_FEATURE_NAME(UIDS),
> + FSINFO_FEATURE_NAME(GIDS),
> + FSINFO_FEATURE_NAME(PROJIDS),
> + FSINFO_FEATURE_NAME(STRING_USER_IDS),
> + FSINFO_FEATURE_NAME(GUID_USER_IDS),
> + FSINFO_FEATURE_NAME(WINDOWS_ATTRS),
> + FSINFO_FEATURE_NAME(USER_QUOTAS),
> + FSINFO_FEATURE_NAME(GROUP_QUOTAS),
> + FSINFO_FEATURE_NAME(PROJECT_QUOTAS),
> + FSINFO_FEATURE_NAME(XATTRS),
> + FSINFO_FEATURE_NAME(JOURNAL),
> + FSINFO_FEATURE_NAME(DATA_IS_JOURNALLED),
> + FSINFO_FEATURE_NAME(O_SYNC),
> + FSINFO_FEATURE_NAME(O_DIRECT),
> + FSINFO_FEATURE_NAME(VOLUME_ID),
> + FSINFO_FEATURE_NAME(VOLUME_UUID),
> + FSINFO_FEATURE_NAME(VOLUME_NAME),
> + FSINFO_FEATURE_NAME(VOLUME_FSID),
> + FSINFO_FEATURE_NAME(IVER_ALL_CHANGE),
> + FSINFO_FEATURE_NAME(IVER_DATA_CHANGE),
> + FSINFO_FEATURE_NAME(IVER_MONO_INCR),
> + FSINFO_FEATURE_NAME(DIRECTORIES),
> + FSINFO_FEATURE_NAME(SYMLINKS),
> + FSINFO_FEATURE_NAME(HARD_LINKS),
> + FSINFO_FEATURE_NAME(HARD_LINKS_1DIR),
> + FSINFO_FEATURE_NAME(DEVICE_FILES),
> + FSINFO_FEATURE_NAME(UNIX_SPECIALS),
> + FSINFO_FEATURE_NAME(RESOURCE_FORKS),
> + FSINFO_FEATURE_NAME(NAME_CASE_INDEP),
> + FSINFO_FEATURE_NAME(NAME_NON_UTF8),
> + FSINFO_FEATURE_NAME(NAME_HAS_CODEPAGE),
> + FSINFO_FEATURE_NAME(SPARSE),
> + FSINFO_FEATURE_NAME(NOT_PERSISTENT),
> + FSINFO_FEATURE_NAME(NO_UNIX_MODE),
> + FSINFO_FEATURE_NAME(HAS_ATIME),
> + FSINFO_FEATURE_NAME(HAS_BTIME),
> + FSINFO_FEATURE_NAME(HAS_CTIME),
> + FSINFO_FEATURE_NAME(HAS_MTIME),
> + FSINFO_FEATURE_NAME(HAS_ACL),
> + FSINFO_FEATURE_NAME(HAS_INODE_NUMBERS),
> +};
> +
> +static void dump_fsinfo_generic_features(void *reply, unsigned int size)
> +{
> + struct fsinfo_features *f = reply;
> + int i;
> +
> + printf("\n\t");
> + for (i = 0; i < sizeof(f->features); i++)
> + printf("%02x", f->features[i]);
> + printf("\n");
> + for (i = 0; i < FSINFO_FEAT__NR; i++)
> + if (f->features[i / 8] & (1 << (i % 8)))
> + printf("\t- %s\n", fsinfo_feature_names[i]);
> +}
> +
> static void print_time(struct fsinfo_timestamp_one *t, char stamp)
> {
> printf("\t%ctime : gran=%gs range=%llx-%llx\n",
> @@ -235,7 +310,6 @@ static void dump_string(void *reply, unsigned int size)
>
> #define dump_fsinfo_generic_volume_id dump_string
> #define dump_fsinfo_generic_volume_name dump_string
> -#define dump_fsinfo_generic_name_encoding dump_string
>
> /*
> *
> @@ -266,6 +340,7 @@ static const struct fsinfo_attribute fsinfo_attributes[] = {
> FSINFO_VSTRUCT (FSINFO_ATTR_IDS, fsinfo_generic_ids),
> FSINFO_VSTRUCT (FSINFO_ATTR_LIMITS, fsinfo_generic_limits),
> FSINFO_VSTRUCT (FSINFO_ATTR_SUPPORTS, fsinfo_generic_supports),
> + FSINFO_VSTRUCT (FSINFO_ATTR_FEATURES, fsinfo_generic_features),
> FSINFO_VSTRUCT (FSINFO_ATTR_TIMESTAMP_INFO, fsinfo_generic_timestamp_info),
> FSINFO_STRING (FSINFO_ATTR_VOLUME_ID, fsinfo_generic_volume_id),
> FSINFO_VSTRUCT (FSINFO_ATTR_VOLUME_UUID, fsinfo_generic_volume_uuid),
> @@ -475,6 +550,7 @@ static int cmp_u32(const void *a, const void *b)
> int main(int argc, char **argv)
> {
> struct fsinfo_attribute_info attr_info;
> + struct fsinfo_fsinfo fsinfo_info;
> struct fsinfo_params params = {
> .at_flags = AT_SYMLINK_NOFOLLOW,
> .flags = FSINFO_FLAGS_QUERY_PATH,
> @@ -531,6 +607,18 @@ int main(int argc, char **argv)
> qsort(attrs, nr, sizeof(attrs[0]), cmp_u32);
>
> if (meta) {
> + params.request = FSINFO_ATTR_FSINFO;
> + params.Nth = 0;
> + params.Mth = 0;
> + ret = fsinfo(AT_FDCWD, argv[0], &params, &fsinfo_info, sizeof(fsinfo_info));
> + if (ret == -1) {
> + fprintf(stderr, "Unable to get fsinfo information: %m\n");
> + exit(1);
> + }
> +
> + dump_fsinfo_info(&fsinfo_info, ret);
> + printf("\n");
> +
> printf("ATTR ID TYPE FLAGS SIZE ESIZE NAME\n");
> printf("======== ============ ======== ===== ===== =========\n");
> for (i = 0; i < nr; i++) {
> @@ -558,7 +646,8 @@ int main(int argc, char **argv)
> exit(1);
> }
>
> - if (attrs[i] == FSINFO_ATTR_FSINFO_ATTRIBUTE_INFO ||
> + if (attrs[i] == FSINFO_ATTR_FSINFO ||
> + attrs[i] == FSINFO_ATTR_FSINFO_ATTRIBUTE_INFO ||
> attrs[i] == FSINFO_ATTR_FSINFO_ATTRIBUTES)
> continue;
>
>
>