Re: HAMMER per-PFS permissions for ioctl syscalls
Vasily Postnicov <[email protected]> Thu, 1 Oct 2015 19:08:00 +0300
| Newsgroups | gmane.os.dragonfly-bsd.kernel |
|---|---|
| Message-ID | <CADnZ6B=ZRwpC=a=1jzakQx_8KRn5bnzbmPucEkC0xCg7XTLG1A@mail.gmail.com> |
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 <[email protected]>: > 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 <[email protected]> > 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? >> > >
hammer.patch
(text/x-patch, 27.2 KB)
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: