Ok, let it be as you decide. But maybe this last argument will change your mind. Imagine a user who has two machines, say, A and B. The machine A has a PFS mounted to /home (or /home/user) and B has a slave mirror of that PFS mounted to /backup/user. If that user wants to update his mirror on B, he must login as root and launch 'hammer mirror-copy /home/user B:/backup/user' which means he must run hammer utility as root and also ssh as root on both machines. Is it 100% secure to have a root access over ssh? Has not ssh any yet unseen bugs? I propose something like that:
On A: su (it will be necessary as only root has the right to change permissions) <root password> hammer -u user add-perm /home mirror-read On B: su <root password> hammer -u user add-perm /backup/home mirror-write And now the mirror can be updated not only by root, but also by user. As the permissions are set on per-PFS basis, user will not be able to make a mirror of any PFS, but only of his own home. zfs allow/unallow works the same way. Here is a résumé: 1) Only root has the right to change permissions. 2) Only root has the right to make any changes to filesystem, which are not on per-PFS basis (like volume management, upgrading filesystem to a new version). 3) Permissions a per-PFS. I attach a proof-of-concept. Apply this patch to DragonFly_RELEASE_4_0. You will be able to set the following permissions: mirror-read, mirror-write, snap-add, snap-del as shown above. Three new commands are added to hammer utility: add-perm, del-perm, show-perm. Let me know, if you are interested. 2015-10-01 6:56 GMT+03:00 Matthew Dillon <dil...@backplane.com>: > Well, I think its a bit too dangerous to give snapshotting power to the > user in this case. The snapshots are managed on a per-PFS basis so the > user would be able to interfere with whatever root intended on doing with > the capability. > > -Matt > > On Sat, Sep 26, 2015 at 7:06 AM, Vasily Postnicov <shamaz.ma...@gmail.com> > wrote: > >> Hello. I have noticed, that some ioctls, like HAMMERIOC_GETHISTORY or >> HAMMERIOC_GET_INFO can be made by any user, and there are some like >> HAMMERIOC_ADD_SNAPSHOT, which only root can do. I find this somewhat >> "unfair", because why a user cannot, for example, make a snapshot of his >> own home directory, if there is a PFS mounted to that directory? I think >> something like zfs allow/unallow is needed here. Any ideas how I can >> implement this? >> >> Maybe I should add a new record type to vfs/hammer/hammer_disk.h, say >> HAMMER_RECTYPE_PERM, and use it in the similar way to >> HAMMER_RECTYPE_CONFIG, like writing functions similar to >> hammer_ioc_get/set_config? So when a user calls ioctl() it will be like >> this in the kernel space: >> >> 1) Start a new transactions and initialize a cursor. >> 2) setup the cursor. Set cursor.key_beg.rec_type = HAMMER_RECTYPE_PERM; >> 3) do hammer_btree_lookup(&cursor); >> 4) If lookup succeeded, extract permission info and act accordingly to it. >> >> So what you think? Will it work? Maybe I need to cache the results >> somehow and do not call hammer_btree_lookup() each time ioctl is called? Or >> it is already done automatically? >> > >
diff --git a/sbin/hammer/Makefile b/sbin/hammer/Makefile index 3578807..4d2aa3f 100644 --- a/sbin/hammer/Makefile +++ b/sbin/hammer/Makefile @@ -5,7 +5,7 @@ SRCS= hammer.c ondisk.c blockmap.c cache.c misc.c cycle.c \ cmd_synctid.c cmd_stats.c cmd_remote.c \ cmd_pseudofs.c cmd_snapshot.c cmd_mirror.c \ cmd_cleanup.c cmd_info.c cmd_version.c cmd_volume.c \ - cmd_config.c cmd_recover.c cmd_dedup.c + cmd_config.c cmd_recover.c cmd_dedup.c cmd_perm.c MAN= hammer.8 CFLAGS+= -I${.CURDIR}/../../sys -DALIST_NO_DEBUG diff --git a/sbin/hammer/cmd_perm.c b/sbin/hammer/cmd_perm.c new file mode 100644 index 0000000..ebb662f --- /dev/null +++ b/sbin/hammer/cmd_perm.c @@ -0,0 +1,128 @@ +#include <pwd.h> +#include "hammer.h" + +static const struct { + const char *command; + const char *desc; + u_int64_t permission; +} perm_command[] = { + {"snap-add", "Snapshot creation", HAMMER_PERM_ADD_SNAPSHOT}, + {"snap-del", "Snapshot deletion", HAMMER_PERM_DEL_SNAPSHOT}, + {"mirror-write", "Mirror write", HAMMER_PERM_MIRROR_WRITE}, + {"mirror-read", "Mirror read", HAMMER_PERM_MIRROR_READ}, +}; + +static void perm_usage(void); +static int get_perm (const char *cmd); + +void hammer_cmd_show_perm(char **av, int ac) +{ + int fd; + unsigned int i; + int error = 0; + struct hammer_ioc_perm perm; + struct passwd *pass; + + if (!(UserOpt) && !(GroupOpt)) + perm_usage(); + + if (ac != 1) + perm_usage(); + + if (UserOpt) { + pass = getpwnam (UserOpt); + if (pass == NULL) { + err (2, "show-perm: Cannot get user info"); + } + perm.uid = pass->pw_uid; + } + + if (GroupOpt) { + fprintf (stderr, "Not implemented yet\n"); + exit (1); + } + + fd = open (av[0], O_RDONLY); + if (fd < 0) + err(2, "Unable to open %s", av[0]); + if (ioctl (fd, HAMMERIOC_GET_PERM, &perm)) + error = errno; + if (perm.head.error) + error = perm.head.error; + close (fd); + if (error) + errx(1, "show-perm %s failed: %s", av[0], strerror (error)); + + printf ("User permissions:\n"); + for (i=0; i<sizeof(perm_command)/sizeof(perm_command[0]); i++) { + if ((perm_command[i].permission & perm.perm) == perm_command[i].permission) + printf ("%s\n", perm_command[i].desc); + } +} + +void hammer_cmd_change_perm(char **av, int ac, int add) +{ + int fd; + int error = 0; + struct hammer_ioc_perm perm; + struct passwd *pass; + + if (!(UserOpt) && !(GroupOpt)) + perm_usage(); + + if (ac != 2) + perm_usage(); + + if (UserOpt) { + pass = getpwnam (UserOpt); + if (pass == NULL) { + err (2, "%s: Cannot get user info", (add) ? "add-perm" : "del-perm"); + } + perm.uid = pass->pw_uid; + } + + if (GroupOpt) { + fprintf (stderr, "Not implemented yet\n"); + exit (1); + } + + fd = open (av[0], O_RDONLY); + if (fd < 0) + err(2, "Unable to open %s", av[0]); + + perm.changed_perm = get_perm (av[1]); + if (ioctl (fd, (add) ? HAMMERIOC_ADD_PERM : HAMMERIOC_DEL_PERM, &perm)) + error = errno; + if (perm.head.error) + error = perm.head.error; + close (fd); + if (error) + errx(1, "%s %s failed: %s", (add) ? "add-perm" : "del-perm", + av[0], strerror (error)); +} + +static int get_perm (const char *cmd) +{ + unsigned int i; + for (i=0; i<sizeof(perm_command)/sizeof(perm_command[0]); i++) { + if (strcmp (cmd, perm_command[i].command) == 0) + return perm_command[i].permission; + } + fprintf (stderr, "%s: no such permission\n", cmd); + perm_usage (); + return 0; +} + +static void perm_usage(void) +{ + unsigned i; + + fprintf (stderr, "hammer -u user | -g group show-perm <filesystem>\n"); + fprintf (stderr, "hammer -u user | -g group add-perm <filesystem> <perm>\n"); + fprintf (stderr, "hammer -u user | -g group del-perm <filesystem> <perm>\n"); + fprintf (stderr, "Available permissions are:\n"); + for (i=0; i<sizeof(perm_command)/sizeof(perm_command[0]); i++) { + fprintf (stderr, " %s: %s\n", perm_command[i].command, perm_command[i].desc); + } + exit (1); +} diff --git a/sbin/hammer/hammer.c b/sbin/hammer/hammer.c index 2db8f96..187d2fd 100644 --- a/sbin/hammer/hammer.c +++ b/sbin/hammer/hammer.c @@ -58,6 +58,8 @@ int ForceOpt; int RunningIoctl; int DidInterrupt; int BulkOpt; +char *UserOpt; +char *GroupOpt; u_int64_t BandwidthOpt; u_int64_t SplitupOpt = 4ULL * 1024ULL * 1024ULL * 1024ULL; u_int64_t MemoryLimit = 1024LLU * 1024 * 1024; @@ -77,7 +79,7 @@ main(int ac, char **av) int cacheSize = 0; while ((ch = getopt(ac, av, - "b:c:de:hf:i:m:p:qrs:t:v2yBC:FR:S:T:X")) != -1) { + "b:c:de:hf:i:m:p:qrs:t:v2yBC:FR:S:T:Xu:g:")) != -1) { switch(ch) { case '2': TwoWayPipeOpt = 1; @@ -249,6 +251,12 @@ main(int ac, char **av) case 'X': CompressOpt = 1; break; + case 'u': + UserOpt = optarg; + break; + case 'g': + GroupOpt = optarg; + break; default: usage(1); /* not reached */ @@ -552,6 +560,18 @@ main(int ac, char **av) hammer_cmd_checkmap(); exit(0); } + if (strcmp(av[0], "add-perm") == 0) { + hammer_cmd_change_perm(av + 1, ac - 1, 1); + exit(0); + } + if (strcmp(av[0], "del-perm") == 0) { + hammer_cmd_change_perm(av + 1, ac - 1, 0); + exit(0); + } + if (strcmp(av[0], "show-perm") == 0) { + hammer_cmd_show_perm(av + 1, ac - 1); + exit(0); + } usage(1); /* not reached */ return(0); @@ -652,6 +672,9 @@ usage(int exit_code) "hammer volume-add <device> <filesystem>\n" "hammer volume-del <device> <filesystem>\n" "hammer volume-list <filesystem>\n" + "hammer show-perm -u user | -g group <filesystem>\n" + "hammer add-perm -u user | -g group <filesystem> <perm>\n" + "hammer del-perm -u user | -g group <filesystem> <perm>\n" ); fprintf(stderr, "\nHAMMER utility version 3+ commands:\n"); diff --git a/sbin/hammer/hammer.h b/sbin/hammer/hammer.h index d66fd4b..bfc4d8e 100644 --- a/sbin/hammer/hammer.h +++ b/sbin/hammer/hammer.h @@ -84,6 +84,8 @@ extern int RunningIoctl; extern int DidInterrupt; extern int ForceOpt; extern int BulkOpt; +extern char *UserOpt; +extern char *GroupOpt; extern u_int64_t BandwidthOpt; extern u_int64_t SplitupOpt; extern u_int64_t MemoryLimit; @@ -132,6 +134,8 @@ void hammer_cmd_volume_del(char **av, int ac); void hammer_cmd_volume_list(char **av, int ac); void hammer_cmd_dedup_simulate(char **av, int ac); void hammer_cmd_dedup(char **av, int ac); +void hammer_cmd_change_perm(char **av, int ac, int add); +void hammer_cmd_show_perm(char **av, int ac); void hammer_get_cycle(hammer_base_elm_t base, hammer_tid_t *tidp); void hammer_set_cycle(hammer_base_elm_t base, hammer_tid_t tid); diff --git a/sys/conf/files b/sys/conf/files index 68fecf1..afc51cb 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1632,6 +1632,7 @@ vfs/hammer/hammer_redo.c optional hammer vfs/hammer/hammer_vfsops.c optional hammer vfs/hammer/hammer_vnops.c optional hammer vfs/hammer/hammer_dedup.c optional hammer +vfs/hammer/hammer_perm.c optional hammer vfs/puffs/puffs_io.c optional puffs vfs/puffs/puffs_msgif.c optional puffs vfs/puffs/puffs_node.c optional puffs diff --git a/sys/sys/priv.h b/sys/sys/priv.h index ee3471d..670beed 100644 --- a/sys/sys/priv.h +++ b/sys/sys/priv.h @@ -470,7 +470,6 @@ * Hammer privileges. */ #define PRIV_HAMMER_IOCTL 650 /* can hammer_ioctl(). */ -#define PRIV_HAMMER_VOLUME 651 /* HAMMER volume management */ /* * Track end of privilege list. diff --git a/sys/vfs/hammer/Makefile b/sys/vfs/hammer/Makefile index 4d33e40..75be32b 100644 --- a/sys/vfs/hammer/Makefile +++ b/sys/vfs/hammer/Makefile @@ -11,7 +11,7 @@ SRCS= hammer_vfsops.c hammer_vnops.c hammer_inode.c \ hammer_reblock.c hammer_rebalance.c \ hammer_flusher.c hammer_mirror.c \ hammer_pfs.c hammer_prune.c hammer_volume.c \ - hammer_dedup.c + hammer_dedup.c hammer_perm.c SRCS+= opt_ktr.h .include <bsd.kmod.mk> diff --git a/sys/vfs/hammer/hammer.h b/sys/vfs/hammer/hammer.h index 151f1c5..c17971b 100644 --- a/sys/vfs/hammer/hammer.h +++ b/sys/vfs/hammer/hammer.h @@ -1425,6 +1425,8 @@ int hammer_unload_pseudofs(hammer_transaction_t trans, u_int32_t localization); void hammer_rel_pseudofs(hammer_mount_t hmp, hammer_pseudofs_inmem_t pfsm); int hammer_ioctl(hammer_inode_t ip, u_long com, caddr_t data, int fflag, struct ucred *cred); +int hammer_checkperm(hammer_transaction_t trans, hammer_inode_t ip, + u_long com, struct ucred *cred); void hammer_io_init(hammer_io_t io, hammer_volume_t volume, enum hammer_io_type type); @@ -1492,6 +1494,12 @@ int hammer_ioc_volume_list(hammer_transaction_t trans, hammer_inode_t ip, struct hammer_ioc_volume_list *ioc); int hammer_ioc_dedup(hammer_transaction_t trans, hammer_inode_t ip, struct hammer_ioc_dedup *dedup); +int hammer_ioc_get_perm(hammer_transaction_t trans, hammer_inode_t ip, + struct hammer_ioc_perm *perm); +int hammer_ioc_add_perm(hammer_transaction_t trans, hammer_inode_t ip, + struct hammer_ioc_perm *perm); +int hammer_ioc_del_perm(hammer_transaction_t trans, hammer_inode_t ip, + struct hammer_ioc_perm *perm); int hammer_signal_check(hammer_mount_t hmp); diff --git a/sys/vfs/hammer/hammer_disk.h b/sys/vfs/hammer/hammer_disk.h index 136aa1c..18f97f5 100644 --- a/sys/vfs/hammer/hammer_disk.h +++ b/sys/vfs/hammer/hammer_disk.h @@ -673,6 +673,7 @@ typedef struct hammer_volume_ondisk *hammer_volume_ondisk_t; #define HAMMER_RECTYPE_PFS 0x0015 /* PFS management */ #define HAMMER_RECTYPE_SNAPSHOT 0x0016 /* Snapshot management */ #define HAMMER_RECTYPE_CONFIG 0x0017 /* hammer cleanup config */ +#define HAMMER_RECTYPE_PERM 0x0018 /* ioctl() per-PFS permissions */ #define HAMMER_RECTYPE_MOVED 0x8000 /* special recovery flag */ #define HAMMER_RECTYPE_MAX 0xFFFF @@ -885,6 +886,17 @@ struct hammer_config_data { }; /* + * Per-PFS ioctl() permissions for a specific user. + * { ObjId = HAMMER_OBJID_ROOT, Key = <uid>, rectype = PERM }. + * This data is not mirrored, like CONFIG data + */ +#define HAMMER_PERM_ADD_SNAPSHOT 1 +#define HAMMER_PERM_DEL_SNAPSHOT 2 +#define HAMMER_PERM_MIRROR_READ 4 +#define HAMMER_PERM_MIRROR_WRITE 8 +#define HAMMER_MAX_PERM_MASK 15 + +/* * Rollup various structures embedded as record data */ union hammer_data_ondisk { @@ -894,6 +906,7 @@ union hammer_data_ondisk { struct hammer_pseudofs_data pfsd; struct hammer_snapshot_data snap; struct hammer_config_data config; + u_int64_t perm; }; typedef union hammer_data_ondisk *hammer_data_ondisk_t; diff --git a/sys/vfs/hammer/hammer_ioctl.c b/sys/vfs/hammer/hammer_ioctl.c index 0eea44f..1fef9e6 100644 --- a/sys/vfs/hammer/hammer_ioctl.c +++ b/sys/vfs/hammer/hammer_ioctl.c @@ -71,25 +71,26 @@ hammer_ioctl(hammer_inode_t ip, u_long com, caddr_t data, int fflag, int error; error = priv_check_cred(cred, PRIV_HAMMER_IOCTL, 0); - hammer_start_transaction(&trans, ip->hmp); + if (error) + error = hammer_checkperm(&trans, ip, com, cred); + if (error) { + hammer_done_transaction(&trans); + return error; + } switch(com) { case HAMMERIOC_PRUNE: - if (error == 0) { - error = hammer_ioc_prune(&trans, ip, + error = hammer_ioc_prune(&trans, ip, (struct hammer_ioc_prune *)data); - } break; case HAMMERIOC_GETHISTORY: error = hammer_ioc_gethistory(&trans, ip, (struct hammer_ioc_history *)data); break; case HAMMERIOC_REBLOCK: - if (error == 0) { - error = hammer_ioc_reblock(&trans, ip, + error = hammer_ioc_reblock(&trans, ip, (struct hammer_ioc_reblock *)data); - } break; case HAMMERIOC_REBALANCE: /* @@ -97,7 +98,7 @@ hammer_ioctl(hammer_inode_t ip, u_long com, caddr_t data, int fflag, * children and children's children. Systems with very * little memory will not be able to do it. */ - if (error == 0 && nbuf < HAMMER_REBALANCE_MIN_BUFS) { + if (nbuf < HAMMER_REBALANCE_MIN_BUFS) { kprintf("hammer: System has insufficient buffers " "to rebalance the tree. nbuf < %d\n", HAMMER_REBALANCE_MIN_BUFS); @@ -117,46 +118,32 @@ hammer_ioctl(hammer_inode_t ip, u_long com, caddr_t data, int fflag, (struct hammer_ioc_pseudofs_rw *)data); break; case HAMMERIOC_SET_PSEUDOFS: - if (error == 0) { - error = hammer_ioc_set_pseudofs(&trans, ip, cred, + error = hammer_ioc_set_pseudofs(&trans, ip, cred, (struct hammer_ioc_pseudofs_rw *)data); - } break; case HAMMERIOC_UPG_PSEUDOFS: - if (error == 0) { - error = hammer_ioc_upgrade_pseudofs(&trans, ip, + error = hammer_ioc_upgrade_pseudofs(&trans, ip, (struct hammer_ioc_pseudofs_rw *)data); - } break; case HAMMERIOC_DGD_PSEUDOFS: - if (error == 0) { - error = hammer_ioc_downgrade_pseudofs(&trans, ip, + error = hammer_ioc_downgrade_pseudofs(&trans, ip, (struct hammer_ioc_pseudofs_rw *)data); - } break; case HAMMERIOC_RMR_PSEUDOFS: - if (error == 0) { - error = hammer_ioc_destroy_pseudofs(&trans, ip, + error = hammer_ioc_destroy_pseudofs(&trans, ip, (struct hammer_ioc_pseudofs_rw *)data); - } break; case HAMMERIOC_WAI_PSEUDOFS: - if (error == 0) { - error = hammer_ioc_wait_pseudofs(&trans, ip, + error = hammer_ioc_wait_pseudofs(&trans, ip, (struct hammer_ioc_pseudofs_rw *)data); - } break; case HAMMERIOC_MIRROR_READ: - if (error == 0) { - error = hammer_ioc_mirror_read(&trans, ip, + error = hammer_ioc_mirror_read(&trans, ip, (struct hammer_ioc_mirror_rw *)data); - } break; case HAMMERIOC_MIRROR_WRITE: - if (error == 0) { - error = hammer_ioc_mirror_write(&trans, ip, + error = hammer_ioc_mirror_write(&trans, ip, (struct hammer_ioc_mirror_rw *)data); - } break; case HAMMERIOC_GET_VERSION: error = hammer_ioc_get_version(&trans, ip, @@ -167,42 +154,28 @@ hammer_ioctl(hammer_inode_t ip, u_long com, caddr_t data, int fflag, (struct hammer_ioc_info *)data); break; case HAMMERIOC_SET_VERSION: - if (error == 0) { - error = hammer_ioc_set_version(&trans, ip, + error = hammer_ioc_set_version(&trans, ip, (struct hammer_ioc_version *)data); - } break; case HAMMERIOC_ADD_VOLUME: - if (error == 0) { - error = priv_check_cred(cred, PRIV_HAMMER_VOLUME, 0); - if (error == 0) - error = hammer_ioc_volume_add(&trans, ip, + error = hammer_ioc_volume_add(&trans, ip, (struct hammer_ioc_volume *)data); - } break; case HAMMERIOC_DEL_VOLUME: - if (error == 0) { - error = priv_check_cred(cred, PRIV_HAMMER_VOLUME, 0); - if (error == 0) - error = hammer_ioc_volume_del(&trans, ip, + error = hammer_ioc_volume_del(&trans, ip, (struct hammer_ioc_volume *)data); - } break; case HAMMERIOC_LIST_VOLUMES: error = hammer_ioc_volume_list(&trans, ip, (struct hammer_ioc_volume_list *)data); break; case HAMMERIOC_ADD_SNAPSHOT: - if (error == 0) { - error = hammer_ioc_add_snapshot( + error = hammer_ioc_add_snapshot( &trans, ip, (struct hammer_ioc_snapshot *)data); - } break; case HAMMERIOC_DEL_SNAPSHOT: - if (error == 0) { - error = hammer_ioc_del_snapshot( + error = hammer_ioc_del_snapshot( &trans, ip, (struct hammer_ioc_snapshot *)data); - } break; case HAMMERIOC_GET_SNAPSHOT: error = hammer_ioc_get_snapshot( @@ -213,27 +186,33 @@ hammer_ioctl(hammer_inode_t ip, u_long com, caddr_t data, int fflag, &trans, ip, (struct hammer_ioc_config *)data); break; case HAMMERIOC_SET_CONFIG: - if (error == 0) { - error = hammer_ioc_set_config( + error = hammer_ioc_set_config( &trans, ip, (struct hammer_ioc_config *)data); - } break; case HAMMERIOC_DEDUP: - if (error == 0) { - error = hammer_ioc_dedup( + error = hammer_ioc_dedup( &trans, ip, (struct hammer_ioc_dedup *)data); - } break; case HAMMERIOC_GET_DATA: - if (error == 0) { - error = hammer_ioc_get_data( + error = hammer_ioc_get_data( &trans, ip, (struct hammer_ioc_data *)data); - } break; case HAMMERIOC_PFS_ITERATE: error = hammer_ioc_pfs_iterate( &trans, (struct hammer_ioc_pfs_iterate *)data); break; + case HAMMERIOC_GET_PERM: + error = hammer_ioc_get_perm( + &trans, ip, (struct hammer_ioc_perm *)data); + break; + case HAMMERIOC_ADD_PERM: + error = hammer_ioc_add_perm( + &trans, ip, (struct hammer_ioc_perm *)data); + break; + case HAMMERIOC_DEL_PERM: + error = hammer_ioc_del_perm( + &trans, ip, (struct hammer_ioc_perm *)data); + break; default: error = EOPNOTSUPP; break; diff --git a/sys/vfs/hammer/hammer_ioctl.h b/sys/vfs/hammer/hammer_ioctl.h index 0196a96..d39454b 100644 --- a/sys/vfs/hammer/hammer_ioctl.h +++ b/sys/vfs/hammer/hammer_ioctl.h @@ -452,6 +452,25 @@ struct hammer_ioc_config { }; /* + * HAMMERIOC_GET_PERM + * HAMMERIOC_ADD_PERM + * HAMMERIOC_DEL_PERM + * + * Per-PFS ioctl() permissions. + * + * The configuration space is NOT mirrored. mirror-write will ignore + * configuration space records. + */ +struct hammer_ioc_perm { + struct hammer_ioc_head head; + uid_t uid; + gid_t gid; + u_int64_t changed_perm; + u_int64_t perm; + u_int64_t reserved; +}; + +/* * HAMMERIOC_DEDUP */ struct hammer_ioc_dedup { @@ -505,6 +524,9 @@ struct hammer_ioc_data { #define HAMMERIOC_GET_DATA _IOWR('h',26,struct hammer_ioc_data) #define HAMMERIOC_LIST_VOLUMES _IOWR('h',27,struct hammer_ioc_volume_list) #define HAMMERIOC_PFS_ITERATE _IOWR('h',28,struct hammer_ioc_pfs_iterate) +#define HAMMERIOC_GET_PERM _IOWR('h',29,struct hammer_ioc_perm) +#define HAMMERIOC_ADD_PERM _IOWR('h',30,struct hammer_ioc_perm) +#define HAMMERIOC_DEL_PERM _IOWR('h',31,struct hammer_ioc_perm) #endif diff --git a/sys/vfs/hammer/hammer_mirror.c b/sys/vfs/hammer/hammer_mirror.c index c99850a..882f799 100644 --- a/sys/vfs/hammer/hammer_mirror.c +++ b/sys/vfs/hammer/hammer_mirror.c @@ -805,7 +805,8 @@ hammer_mirror_nomirror(struct hammer_base_elm *base) * Certain types of records are never updated when mirroring. * Slaves have their own configuration space. */ - if (base->rec_type == HAMMER_RECTYPE_CONFIG) + if ((base->rec_type == HAMMER_RECTYPE_CONFIG) || + (base->rec_type == HAMMER_RECTYPE_PERM)) return(1); return(0); } diff --git a/sys/vfs/hammer/hammer_ondisk.c b/sys/vfs/hammer/hammer_ondisk.c index 876c5fe..035bb39 100644 --- a/sys/vfs/hammer/hammer_ondisk.c +++ b/sys/vfs/hammer/hammer_ondisk.c @@ -1664,6 +1664,7 @@ hammer_alloc_data(hammer_transaction_t trans, int32_t data_len, case HAMMER_RECTYPE_PFS: case HAMMER_RECTYPE_SNAPSHOT: case HAMMER_RECTYPE_CONFIG: + case HAMMER_RECTYPE_PERM: zone = HAMMER_ZONE_META_INDEX; break; case HAMMER_RECTYPE_DATA: diff --git a/sys/vfs/hammer/hammer_perm.c b/sys/vfs/hammer/hammer_perm.c new file mode 100644 index 0000000..9dd1fa3 --- /dev/null +++ b/sys/vfs/hammer/hammer_perm.c @@ -0,0 +1,278 @@ +#include "hammer.h" + +static int hammer_get_perm (hammer_transaction_t trans, hammer_inode_t ip, + uid_t uid, gid_t gid, u_int64_t *perm); +static int hammer_set_perm (hammer_transaction_t trans, hammer_inode_t ip, + uid_t uid, gid_t gid, u_int64_t *perm); + +/* This function does not fail in the case of ENOENT + * creating dummy permission entry. Returns EINVAL if permission entry + is requested for root. + */ +int hammer_ioc_get_perm(hammer_transaction_t trans, hammer_inode_t ip, + struct hammer_ioc_perm *perm) +{ + int error; + perm->head.error = 0; + + /* Sanity checks */ + if ((perm->gid == 0) && (perm->uid == 0)) { + perm->head.error = EINVAL; + return 0; + } + + error = hammer_get_perm (trans, ip, + perm->uid, perm->gid, &(perm->perm)); + if (error == ENOENT) { + error = 0; + bzero(&(perm->perm), sizeof (perm->perm)); + } + return error; +} + +/* Returns an error in case of HAMMER operations failure. + * Sets the following errors in perm->head.error: + * + * EINVAL: Permission is out of range or both uid and gid are set to 0 + * (root has not a permission entry). User tries to set the same + * permission twice. + */ +int hammer_ioc_add_perm(hammer_transaction_t trans, hammer_inode_t ip, + struct hammer_ioc_perm *perm) +{ + int error; + u_int64_t perm_data; + + perm->head.error = 0; + /* Sanity checks */ + if (perm->changed_perm > HAMMER_MAX_PERM_MASK) { + perm->head.error = EINVAL; + return 0; + } + if ((perm->gid == 0) && (perm->uid == 0)) { + perm->head.error = EINVAL; + return 0; + } + + error = hammer_get_perm (trans, ip, perm->uid, perm->gid, &perm_data); + if (error == ENOENT) { + error = 0; + bzero (&perm_data, sizeof (perm_data)); + } + if (error) + return error; + + /* Check if we must add anything at all */ + if ((perm_data & perm->changed_perm) == perm->changed_perm) { + perm->head.error = EINVAL; + return 0; + } + + perm_data |= perm->changed_perm; + + error = hammer_set_perm (trans, ip, perm->uid, perm->gid, &perm_data); + return error; +} + +/* Returns an error in case of HAMMER operations failure. + * Sets the following errors in perm->head.error: + * + * EINVAL: Permission is out of range or both uid and gid are set to 0 + * (root has not a permission entry). User tries to delete + * a permission which is not set earlier. + */ +int hammer_ioc_del_perm(hammer_transaction_t trans, hammer_inode_t ip, + struct hammer_ioc_perm *perm) +{ + int error; + u_int64_t perm_data; + + perm->head.error = 0; + /* Sanity checks */ + if (perm->changed_perm > HAMMER_MAX_PERM_MASK) { + perm->head.error = EINVAL; + return 0; + } + if ((perm->gid == 0) && (perm->uid == 0)) { + perm->head.error = EINVAL; + return 0; + } + + error = hammer_get_perm (trans, ip, perm->uid, perm->gid, &perm_data); + if (error == ENOENT) { + perm->head.error = EINVAL; + return 0; + } + if (error) + return error; + + /* Nothing to delete */ + if ((perm->changed_perm & perm_data) != perm->changed_perm) { + perm->head.error = EINVAL; + return 0; + } + + perm_data &= ~perm->changed_perm; + + error = hammer_set_perm (trans, ip, perm->uid, perm->gid, &perm_data); + return error; +} + +int hammer_checkperm(hammer_transaction_t trans, hammer_inode_t ip, + u_long com, struct ucred *cred) +{ + int error; + int p = -1; + u_int64_t perm; + + switch (com) { + /* Unprivileged ioctl()'s */ + case HAMMERIOC_GETHISTORY: + case HAMMERIOC_SYNCTID: + case HAMMERIOC_GET_PSEUDOFS: + case HAMMERIOC_GET_VERSION: + case HAMMERIOC_GET_INFO: + case HAMMERIOC_LIST_VOLUMES: + case HAMMERIOC_GET_SNAPSHOT: + case HAMMERIOC_GET_CONFIG: + case HAMMERIOC_PFS_ITERATE: + case HAMMERIOC_GET_PERM: + return 0; + case HAMMERIOC_ADD_SNAPSHOT: + p = HAMMER_PERM_ADD_SNAPSHOT; + break; + case HAMMERIOC_DEL_SNAPSHOT: + p = HAMMER_PERM_DEL_SNAPSHOT; + break; + case HAMMERIOC_MIRROR_READ: + p = HAMMER_PERM_MIRROR_READ; + break; + case HAMMERIOC_MIRROR_WRITE: + p = HAMMER_PERM_MIRROR_WRITE; + break; + case HAMMERIOC_SET_PSEUDOFS: + p = HAMMER_PERM_MIRROR_WRITE; + break; + case HAMMERIOC_WAI_PSEUDOFS: + p = HAMMER_PERM_MIRROR_READ; + break; + /* 'Global' (non per-PFS) ioctl()'s */ + case HAMMERIOC_SET_VERSION: + case HAMMERIOC_ADD_VOLUME: + case HAMMERIOC_DEL_VOLUME: + default: + return EPERM; + } + + if (p == -1) + return EINVAL; /* Unknown ioctl */ + + error = hammer_get_perm(trans, ip, cred->cr_uid, cred->cr_gid, &perm); + if (error == ENOENT) + return EPERM; + if (error) + return error; + + if ((perm & p) == p) + return 0; + return EPERM; +} + +static int hammer_get_perm (hammer_transaction_t trans, hammer_inode_t ip, + uid_t uid, gid_t gid, u_int64_t *perm) +{ + int error; + struct hammer_cursor cursor; + + error = hammer_init_cursor(trans, &cursor, &ip->cache[0], NULL); + if (error) { + hammer_done_cursor(&cursor); + return error; + } + + cursor.key_beg.obj_id = HAMMER_OBJID_ROOT; + cursor.key_beg.create_tid = 0; + cursor.key_beg.delete_tid = 0; + cursor.key_beg.obj_type = 0; + cursor.key_beg.rec_type = HAMMER_RECTYPE_PERM; + cursor.key_beg.localization = ip->obj_localization + HAMMER_LOCALIZE_INODE; + cursor.key_beg.key = uid; + + cursor.asof = HAMMER_MAX_TID; + cursor.flags |= HAMMER_CURSOR_ASOF; + + error = hammer_btree_lookup(&cursor); + if (error == 0) { + error = hammer_btree_extract(&cursor, HAMMER_CURSOR_GET_LEAF | + HAMMER_CURSOR_GET_DATA); + if (error == 0) { + *perm = cursor.data->perm; + KASSERT (*perm <= HAMMER_MAX_PERM_MASK, ("Permissions are invalid %lu", *perm)); + } + } + + hammer_done_cursor(&cursor); + return error; +} + +static int hammer_set_perm (hammer_transaction_t trans, hammer_inode_t ip, + uid_t uid, gid_t gid, u_int64_t *perm) +{ + struct hammer_btree_leaf_elm leaf; + struct hammer_cursor cursor; + hammer_mount_t hmp = ip->hmp; + int error; + +again: + error = hammer_init_cursor(trans, &cursor, &ip->cache[0], NULL); + if (error) { + hammer_done_cursor(&cursor); + return(error); + } + + bzero(&leaf, sizeof(leaf)); + leaf.base.obj_id = HAMMER_OBJID_ROOT; + leaf.base.rec_type = HAMMER_RECTYPE_PERM; + leaf.base.create_tid = hammer_alloc_tid(hmp, 1); + leaf.base.btype = HAMMER_BTREE_TYPE_RECORD; + leaf.base.localization = ip->obj_localization + HAMMER_LOCALIZE_INODE; + leaf.base.key = uid; + leaf.data_len = sizeof(*perm); + + cursor.key_beg = leaf.base; + + cursor.asof = HAMMER_MAX_TID; + cursor.flags |= HAMMER_CURSOR_BACKEND | HAMMER_CURSOR_ASOF; + + error = hammer_btree_lookup(&cursor); + if (error == 0) { + error = hammer_btree_extract(&cursor, HAMMER_CURSOR_GET_LEAF | + HAMMER_CURSOR_GET_DATA); + error = hammer_delete_at_cursor(&cursor, HAMMER_DELETE_DESTROY, + 0, 0, 0, NULL); + if (error == EDEADLK) { + hammer_done_cursor(&cursor); + goto again; + } + } + if (error == ENOENT) + error = 0; + if (error == 0) { + /* + * NOTE: Must reload key_beg after an ASOF search because + * the create_tid may have been modified during the + * search. + */ + cursor.flags &= ~HAMMER_CURSOR_ASOF; + cursor.key_beg = leaf.base; + error = hammer_create_at_cursor(&cursor, &leaf, + perm, + HAMMER_CREATE_MODE_SYS); + if (error == EDEADLK) { + hammer_done_cursor(&cursor); + goto again; + } + } + hammer_done_cursor(&cursor); + return error; +} diff --git a/sys/vfs/hammer/hammer_reblock.c b/sys/vfs/hammer/hammer_reblock.c index 65d987a..fa7971b 100644 --- a/sys/vfs/hammer/hammer_reblock.c +++ b/sys/vfs/hammer/hammer_reblock.c @@ -272,6 +272,7 @@ hammer_reblock_helper(struct hammer_ioc_reblock *reblock, case HAMMER_RECTYPE_INODE: case HAMMER_RECTYPE_SNAPSHOT: case HAMMER_RECTYPE_CONFIG: + case HAMMER_RECTYPE_PERM: iocflags = HAMMER_IOC_DO_INODES; break; case HAMMER_RECTYPE_EXT: