Skip to content

Commit

Permalink
Expose additional file level attributes
Browse files Browse the repository at this point in the history
ZFS allows to update and retrieve additional file level attributes for
FreeBSD. This commit allows additional file level attributes to be
updated and retrieved for Linux. These include the flags stored in the
upper half of z_pflags only.

Two new IOCTLs have been added for this purpose. ZFS_IOC_GETDOSFLAGS
can be used to retieve the attributes, while ZFS_IOC_SETDOSFLAGS can
be used to update the attributes.

Attributes that are allowed to be updated include ZFS_IMMUTABLE,
ZFS_APPENDONLY, ZFS_NOUNLINK, ZFS_ARCHIVE, ZFS_NODUMP, ZFS_SYSTEM,
ZFS_HIDDEN, ZFS_READONLY, ZFS_REPARSE, ZFS_OFFLINE and ZFS_SPARSE.
Flags can be or'd together while calling ZFS_IOC_SETDOSFLAGS.

Signed-off-by: Umer Saleem <[email protected]>
  • Loading branch information
usaleem-ix committed Feb 21, 2022
1 parent 7901b62 commit db4c190
Show file tree
Hide file tree
Showing 19 changed files with 492 additions and 1 deletion.
3 changes: 3 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -226,12 +226,14 @@ AC_CONFIG_FILES([
tests/zfs-tests/cmd/randfree_file/Makefile
tests/zfs-tests/cmd/randwritecomp/Makefile
tests/zfs-tests/cmd/readmmap/Makefile
tests/zfs-tests/cmd/read_dos_attributes/Makefile
tests/zfs-tests/cmd/rename_dir/Makefile
tests/zfs-tests/cmd/rm_lnkcnt_zero_file/Makefile
tests/zfs-tests/cmd/send_doall/Makefile
tests/zfs-tests/cmd/stride_dd/Makefile
tests/zfs-tests/cmd/threadsappend/Makefile
tests/zfs-tests/cmd/user_ns_exec/Makefile
tests/zfs-tests/cmd/write_dos_attributes/Makefile
tests/zfs-tests/cmd/xattrtest/Makefile
tests/zfs-tests/include/Makefile
tests/zfs-tests/tests/Makefile
Expand Down Expand Up @@ -333,6 +335,7 @@ AC_CONFIG_FILES([
tests/zfs-tests/tests/functional/deadman/Makefile
tests/zfs-tests/tests/functional/delegate/Makefile
tests/zfs-tests/tests/functional/devices/Makefile
tests/zfs-tests/tests/functional/dos_attributes/Makefile
tests/zfs-tests/tests/functional/events/Makefile
tests/zfs-tests/tests/functional/exec/Makefile
tests/zfs-tests/tests/functional/fallocate/Makefile
Expand Down
31 changes: 31 additions & 0 deletions include/sys/fs/zfs.h
Original file line number Diff line number Diff line change
Expand Up @@ -1458,6 +1458,37 @@ typedef enum zfs_ioc {
*/
#define BLKZNAME _IOR(0x12, 125, char[ZFS_MAX_DATASET_NAME_LEN])

#ifdef __linux__

/*
* IOCTLs to update and retrieve additional file level attributes on
* Linux.
*/
#define ZFS_IOC_GETDOSFLAGS _IOR(0x83, 1, uint64_t)
#define ZFS_IOC_SETDOSFLAGS _IOW(0x83, 2, uint64_t)

/*
* Additional file level attributes, that are stored
* in the upper half of z_pflags
*/
#define ZFS_READONLY 0x0000000100000000ull
#define ZFS_HIDDEN 0x0000000200000000ull
#define ZFS_SYSTEM 0x0000000400000000ull
#define ZFS_ARCHIVE 0x0000000800000000ull
#define ZFS_IMMUTABLE 0x0000001000000000ull
#define ZFS_NOUNLINK 0x0000002000000000ull
#define ZFS_APPENDONLY 0x0000004000000000ull
#define ZFS_NODUMP 0x0000008000000000ull
#define ZFS_OPAQUE 0x0000010000000000ull
#define ZFS_AV_QUARANTINED 0x0000020000000000ull
#define ZFS_AV_MODIFIED 0x0000040000000000ull
#define ZFS_REPARSE 0x0000080000000000ull
#define ZFS_OFFLINE 0x0000100000000000ull
#define ZFS_SPARSE 0x0000200000000000ull

#endif


/*
* ZFS-specific error codes used for returning descriptive errors
* to the userland through zfs ioctls.
Expand Down
2 changes: 1 addition & 1 deletion include/sys/zfs_znode.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ extern "C" {

/*
* Additional file level attributes, that are stored
* in the upper half of zp_flags
* in the upper half of z_pflags
*/
#define ZFS_READONLY 0x0000000100000000ull
#define ZFS_HIDDEN 0x0000000200000000ull
Expand Down
132 changes: 132 additions & 0 deletions module/os/linux/zfs/zpl_file.c
Original file line number Diff line number Diff line change
Expand Up @@ -998,6 +998,134 @@ zpl_ioctl_setxattr(struct file *filp, void __user *arg)
return (err);
}

/*
* Mask to scrub the lower bits of file level attributes
*/
#define ZFS_KEEP_ADDITIONAL_FLAGS 0xFFFFFFFF00000000ull

/*
* Expose Additional File Level Attributes of ZFS.
*/
static int
zpl_ioctl_getdosflags(struct file *filp, void __user *arg)
{
struct inode *ip = file_inode(filp);
uint64_t dosflags = ITOZ(ip)->z_pflags;
dosflags &= ZFS_KEEP_ADDITIONAL_FLAGS;
int err = copy_to_user(arg, &dosflags, sizeof (dosflags));

return (err);
}

static int
__zpl_ioctl_setdosflags(struct inode *ip, uint64_t ioctl_flags, xvattr_t *xva)
{
uint64_t zfs_flags = ITOZ(ip)->z_pflags;
xoptattr_t *xoap;

if (ioctl_flags & ~(ZFS_IMMUTABLE | ZFS_APPENDONLY | ZFS_NOUNLINK |
ZFS_ARCHIVE | ZFS_NODUMP | ZFS_SYSTEM | ZFS_HIDDEN |
ZFS_READONLY | ZFS_REPARSE | ZFS_OFFLINE | ZFS_SPARSE))
return (-EOPNOTSUPP);

if ((fchange(ioctl_flags, zfs_flags, ZFS_IMMUTABLE, ZFS_IMMUTABLE) ||
fchange(ioctl_flags, zfs_flags, ZFS_APPENDONLY, ZFS_APPENDONLY)) &&
!capable(CAP_LINUX_IMMUTABLE))
return (-EPERM);

if (!zpl_inode_owner_or_capable(kcred->user_ns, ip))
return (-EACCES);

xva_init(xva);
xoap = xva_getxoptattr(xva);

if (ioctl_flags & ZFS_IMMUTABLE) {
XVA_SET_REQ(xva, XAT_IMMUTABLE);
xoap->xoa_immutable = B_TRUE;
}

if (ioctl_flags & ZFS_APPENDONLY) {
XVA_SET_REQ(xva, XAT_APPENDONLY);
xoap->xoa_appendonly = B_TRUE;
}

if (ioctl_flags & ZFS_NODUMP) {
XVA_SET_REQ(xva, XAT_NODUMP);
xoap->xoa_nodump = B_TRUE;
}

if ((ioctl_flags & ZFS_READONLY)) {
XVA_SET_REQ(xva, XAT_READONLY);
xoap->xoa_readonly = B_TRUE;
}

if ((ioctl_flags & ZFS_HIDDEN)) {
XVA_SET_REQ(xva, XAT_HIDDEN);
xoap->xoa_hidden = B_TRUE;
}

if ((ioctl_flags & ZFS_SYSTEM)) {
XVA_SET_REQ(xva, XAT_SYSTEM);
xoap->xoa_system = B_TRUE;
}

if ((ioctl_flags & ZFS_ARCHIVE)) {
XVA_SET_REQ(xva, XAT_ARCHIVE);
xoap->xoa_archive = B_TRUE;
}

if ((ioctl_flags & ZFS_NOUNLINK)) {
XVA_SET_REQ(xva, XAT_NOUNLINK);
xoap->xoa_nounlink = B_TRUE;
}

if ((ioctl_flags & ZFS_REPARSE)) {
XVA_SET_REQ(xva, XAT_REPARSE);
xoap->xoa_reparse = B_TRUE;
}

if ((ioctl_flags & ZFS_OFFLINE)) {
XVA_SET_REQ(xva, XAT_OFFLINE);
xoap->xoa_offline = B_TRUE;
}

if ((ioctl_flags & ZFS_SPARSE)) {
XVA_SET_REQ(xva, XAT_SPARSE);
xoap->xoa_sparse = B_TRUE;
}

return (0);
}

/*
* Set Additional File Level Attributes of ZFS.
*/
static int
zpl_ioctl_setdosflags(struct file *filp, void __user *arg)
{
struct inode *ip = file_inode(filp);
uint64_t dosflags;
cred_t *cr = CRED();
xvattr_t xva;
int err;
fstrans_cookie_t cookie;

if (copy_from_user(&dosflags, arg, sizeof (dosflags)))
return (-EFAULT);

err = __zpl_ioctl_setdosflags(ip, dosflags, &xva);
if (err)
return (err);

crhold(cr);
cookie = spl_fstrans_mark();
err = -zfs_setattr(ITOZ(ip), (vattr_t *)&xva, 0, cr);
spl_fstrans_unmark(cookie);
crfree(cr);

return (err);
}

static long
zpl_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
Expand All @@ -1012,6 +1140,10 @@ zpl_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
return (zpl_ioctl_getxattr(filp, (void *)arg));
case ZFS_IOC_FSSETXATTR:
return (zpl_ioctl_setxattr(filp, (void *)arg));
case ZFS_IOC_GETDOSFLAGS:
return (zpl_ioctl_getdosflags(filp, (void *)arg));
case ZFS_IOC_SETDOSFLAGS:
return (zpl_ioctl_setdosflags(filp, (void *)arg));
default:
return (-ENOTTY);
}
Expand Down
4 changes: 4 additions & 0 deletions tests/runfiles/linux.run
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,10 @@ tests = ['projectid_001_pos', 'projectid_002_pos', 'projectid_003_pos',
'projecttree_001_pos', 'projecttree_002_pos', 'projecttree_003_neg']
tags = ['functional', 'projectquota']

[tests/functional/dos_attributes:Linux]
tests = ['read_dos_attrs_001', 'write_dos_attrs_001']
tags = ['functional', 'dos_attributes']

[tests/functional/rsend:Linux]
tests = ['send_realloc_dnode_size', 'send_encrypted_files']
tags = ['functional', 'rsend']
Expand Down
2 changes: 2 additions & 0 deletions tests/zfs-tests/cmd/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ if BUILD_LINUX
SUBDIRS += \
getversion \
randfree_file \
read_dos_attributes \
user_ns_exec \
write_dos_attributes \
xattrtest
endif
1 change: 1 addition & 0 deletions tests/zfs-tests/cmd/read_dos_attributes/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/read_dos_attributes
6 changes: 6 additions & 0 deletions tests/zfs-tests/cmd/read_dos_attributes/Makefile.am
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
include $(top_srcdir)/config/Rules.am

pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/bin

pkgexec_PROGRAMS = read_dos_attributes
read_dos_attributes_SOURCES = read_dos_attributes.c
50 changes: 50 additions & 0 deletions tests/zfs-tests/cmd/read_dos_attributes/read_dos_attributes.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* http://www.illumos.org/license/CDDL.
*/

/*
* Copyright 2022 iXsystems, Inc.
*/

/*
* FreeBSD allows to update and retreive additional file level attributes.
* For Linux, two IOCTLs have been added to update and retrieve additional
* level attributes.
*/

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <err.h>
#include <sys/fs/zfs.h>

int
main(int argc, const char * const argv[])
{
if (argc != 2)
errx(EXIT_FAILURE, "Usage: %s filename", argv[0]);

int fd = open(argv[1], O_RDONLY);
if (fd < 0)
err(EXIT_FAILURE, "Failed to open %s", argv[1]);

uint64_t dosflags = -1;
if (ioctl(fd, ZFS_IOC_GETDOSFLAGS, &dosflags) == -1)
err(EXIT_FAILURE, "ZFS_IOC_GETDOSFLAGS failed");

(void) close(fd);

(void) printf("Dos Flags: %#" PRIx64 "\n", dosflags);

return (EXIT_SUCCESS);
}
1 change: 1 addition & 0 deletions tests/zfs-tests/cmd/write_dos_attributes/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/write_dos_attributes
6 changes: 6 additions & 0 deletions tests/zfs-tests/cmd/write_dos_attributes/Makefile.am
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
include $(top_srcdir)/config/Rules.am

pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/bin

pkgexec_PROGRAMS = write_dos_attributes
write_dos_attributes_SOURCES = write_dos_attributes.c
55 changes: 55 additions & 0 deletions tests/zfs-tests/cmd/write_dos_attributes/write_dos_attributes.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* http://www.illumos.org/license/CDDL.
*/

/*
* Copyright 2022 iXsystems, Inc.
*/

/*
* FreeBSD allows to update and retreive additional file level attributes.
* For Linux, two IOCTLs have been added to update and retrieve additional
* level attributes.
*/

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <err.h>
#include <sys/fs/zfs.h>

int
main(int argc, const char * const argv[])
{
if (argc != 2)
errx(EXIT_FAILURE, "Usage: %s filename", argv[0]);

int fd = open(argv[1], O_RDWR);
if (fd < 0)
err(EXIT_FAILURE, "Failed to open %s", argv[1]);

uint64_t dosflags = ZFS_OFFLINE;
if (ioctl(fd, ZFS_IOC_SETDOSFLAGS, &dosflags) == -1)
err(EXIT_FAILURE, "ZFS_IOC_SETDOSFLAGS failed");

dosflags = -1;
if (ioctl(fd, ZFS_IOC_GETDOSFLAGS, &dosflags) == -1)
err(EXIT_FAILURE, "ZFS_IOC_GETDOSFLAGS failed");

(void) close(fd);

if (!(dosflags & ZFS_OFFLINE))
errx(EXIT_FAILURE, "Could not set ZFS_OFFLINE attribute");

return (EXIT_SUCCESS);
}
2 changes: 2 additions & 0 deletions tests/zfs-tests/include/commands.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -214,10 +214,12 @@ export ZFSTEST_FILES='badsend
randfree_file
randwritecomp
readmmap
read_dos_attributes
rename_dir
rm_lnkcnt_zero_file
send_doall
threadsappend
user_ns_exec
write_dos_attributes
xattrtest
stride_dd'
1 change: 1 addition & 0 deletions tests/zfs-tests/tests/functional/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ SUBDIRS = \
deadman \
delegate \
devices \
dos_attributes \
events \
exec \
fallocate \
Expand Down
8 changes: 8 additions & 0 deletions tests/zfs-tests/tests/functional/dos_attributes/Makefile.am
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
include $(top_srcdir)/config/Rules.am

pkgdatadir = $(datadir)/@PACKAGE@/zfs-tests/tests/functional/dos_attributes
dist_pkgdata_SCRIPTS = \
cleanup.ksh \
read_dos_attrs_001.ksh \
write_dos_attrs_001.ksh \
setup.ksh
Loading

0 comments on commit db4c190

Please sign in to comment.