HAMMER Utilities: Sync with 60I
[dragonfly.git] / sbin / hammer / cmd_pseudofs.c
1 /*
2  * Copyright (c) 2008 The DragonFly Project.  All rights reserved.
3  * 
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.com>
6  * 
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  * 
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  * 
34  * $DragonFly: src/sbin/hammer/cmd_pseudofs.c,v 1.5 2008/07/09 10:32:30 dillon Exp $
35  */
36
37 #include "hammer.h"
38
39 static void parse_pfsd_options(char **av, int ac, hammer_pseudofs_data_t pfsd);
40 static void init_pfsd(hammer_pseudofs_data_t pfsd, int is_slave);
41 static void dump_pfsd(hammer_pseudofs_data_t pfsd);
42 static void pseudofs_usage(int code);
43
44 /*
45  * Calculate the pfs_id given a path to a directory or a @@PFS or @@%llx:%d
46  * softlink.
47  */
48 int
49 getpfs(struct hammer_ioc_pseudofs_rw *pfs, const char *path)
50 {
51         hammer_tid_t dummy_tid;
52         struct stat st;
53         char *dirpath;
54         char buf[64];
55         int fd;
56         int n;
57
58         bzero(pfs, sizeof(*pfs));
59         pfs->ondisk = malloc(sizeof(*pfs->ondisk));
60         bzero(pfs->ondisk, sizeof(*pfs->ondisk));
61         pfs->bytes = sizeof(*pfs->ondisk);
62
63         /*
64          * Calculate the directory containing the softlink
65          */
66         dirpath = strdup(path);
67         if (strrchr(dirpath, '/'))
68                 *strrchr(dirpath, '/') = 0;
69         else
70                 dirpath = strdup(".");
71
72         if (lstat(path, &st) == 0 && S_ISLNK(st.st_mode)) {
73                 n = readlink(path, buf, sizeof(buf) - 1);
74                 if (n < 0)
75                         n = 0;
76                 buf[n] = 0;
77                 if (sscanf(buf, "@@PFS%d", &pfs->pfs_id) == 1) {
78                         fd = open(dirpath, O_RDONLY);
79                         goto done;
80                 }
81                 if (sscanf(buf, "@@%llx:%d", &dummy_tid, &pfs->pfs_id) == 2) {
82                         fd = open(dirpath, O_RDONLY);
83                         goto done;
84                 }
85         }
86
87         /*
88          * Try to open the path and request the pfs_id that way.
89          */
90         fd = open(path, O_RDONLY);
91         if (fd >= 0) {
92                 pfs->pfs_id = -1;
93                 ioctl(fd, HAMMERIOC_GET_PSEUDOFS, pfs);
94                 if (pfs->pfs_id == -1) {
95                         close(fd);
96                         fd = -1;
97                 }
98         }
99
100         /*
101          * Cleanup
102          */
103 done:
104         if (fd < 0) {
105                 fprintf(stderr, "Cannot access PFS %s: %s\n",
106                         path, strerror(errno));
107                 exit(1);
108         }
109         if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, pfs) < 0) {
110                 fprintf(stderr, "Cannot access PFS %s: %s\n",
111                         path, strerror(errno));
112                 exit(1);
113         }
114         free(dirpath);
115         return(fd);
116 }
117
118 void
119 hammer_cmd_pseudofs_status(char **av, int ac)
120 {
121         struct hammer_ioc_pseudofs_rw pfs;
122         int i;
123         int fd;
124
125         for (i = 0; i < ac; ++i) {
126                 printf("%s\t", av[i]);
127                 fd = getpfs(&pfs, av[i]);
128                 if (fd < 0 || ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) < 0) {
129                         printf("Not a HAMMER root\n");
130                 } else {
131                         printf("PFS #%d {\n", pfs.pfs_id);
132                         dump_pfsd(pfs.ondisk);
133                         printf("}\n");
134                 }
135                 if (fd >= 0)
136                         close(fd);
137                 if (pfs.ondisk)
138                         free(pfs.ondisk);
139         }
140 }
141
142 void
143 hammer_cmd_pseudofs_create(char **av, int ac, int is_slave)
144 {
145         struct hammer_ioc_pseudofs_rw pfs;
146         struct hammer_pseudofs_data pfsd;
147         struct stat st;
148         const char *path;
149         char *dirpath;
150         char *linkpath;
151         int pfs_id;
152         int fd;
153         int error;
154
155         if (ac == 0)
156                 pseudofs_usage(1);
157         path = av[0];
158         if (lstat(path, &st) == 0) {
159                 fprintf(stderr, "cannot create %s, file exists!\n", path);
160                 exit(1);
161         }
162
163         dirpath = strdup(path);
164         if (strrchr(dirpath, '/') != NULL)
165                 *strrchr(dirpath, '/') = 0;
166         else
167                 dirpath = strdup(".");
168         fd = open(dirpath, O_RDONLY);
169         if (fd < 0) {
170                 fprintf(stderr, "Cannot open directory %s\n", dirpath);
171                 exit(1);
172         }
173
174         error = 0;
175         for (pfs_id = 0; pfs_id < HAMMER_MAX_PFS; ++pfs_id) {
176                 bzero(&pfs, sizeof(pfs));
177                 bzero(&pfsd, sizeof(pfsd));
178                 pfs.pfs_id = pfs_id;
179                 pfs.ondisk = &pfsd;
180                 pfs.bytes = sizeof(pfsd);
181                 pfs.version = HAMMER_IOC_PSEUDOFS_VERSION;
182                 if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) < 0) {
183                         error = errno;
184                         break;
185                 }
186         }
187         if (pfs_id == HAMMER_MAX_PFS) {
188                 fprintf(stderr, "Cannot create %s, all PFSs in use\n", path);
189                 exit(1);
190         }
191         if (error != ENOENT) {
192                 fprintf(stderr, "Cannot create %s, got %s during scan\n",
193                         path, strerror(error));
194                 exit(1);
195         }
196
197         /*
198          * Create the new PFS
199          */
200         printf("Creating PFS #%d\t", pfs_id);
201         bzero(&pfsd, sizeof(pfsd));
202         init_pfsd(&pfsd, is_slave);
203         pfs.pfs_id = pfs_id;
204         pfs.ondisk = &pfsd;
205         pfs.bytes = sizeof(pfsd);
206         pfs.version = HAMMER_IOC_PSEUDOFS_VERSION;
207
208         if (ioctl(fd, HAMMERIOC_SET_PSEUDOFS, &pfs) < 0) {
209                 printf("failed: %s\n", strerror(errno));
210         } else {
211                 /* special symlink, must be exactly 10 characters */
212                 asprintf(&linkpath, "@@PFS%05d", pfs_id);
213                 if (symlink(linkpath, path) < 0) {
214                         printf("failed: cannot create symlink: %s\n",
215                                 strerror(errno));
216                 } else {
217                         printf("succeeded!\n");
218                         hammer_cmd_pseudofs_update(av, ac);
219                 }
220         }
221         close(fd);
222 }
223
224 void
225 hammer_cmd_pseudofs_destroy(char **av, int ac)
226 {
227         fprintf(stderr, "pfs-destroy not implemented yet\n");
228 }
229
230 void
231 hammer_cmd_pseudofs_update(char **av, int ac)
232 {
233         struct hammer_ioc_pseudofs_rw pfs;
234         int fd;
235
236         if (ac == 0)
237                 pseudofs_usage(1);
238         bzero(&pfs, sizeof(pfs));
239         fd = getpfs(&pfs, av[0]);
240
241         printf("%s\n", av[0]);
242         fflush(stdout);
243
244         if (fd >= 0 && ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) == 0) {
245                 parse_pfsd_options(av + 1, ac - 1, pfs.ondisk);
246                 if ((pfs.ondisk->mirror_flags & HAMMER_PFSD_SLAVE) &&
247                     pfs.pfs_id == 0) {
248                         fprintf(stderr, "The real mount point cannot be made a PFS slave, only PFS sub-directories can be made slaves\n");
249                         exit(1);
250                 }
251                 pfs.bytes = sizeof(*pfs.ondisk);
252                 if (ioctl(fd, HAMMERIOC_SET_PSEUDOFS, &pfs) == 0) {
253                         if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) == 0) {
254                                 dump_pfsd(pfs.ondisk);
255                         } else {
256                                 printf("Unable to retrieve pfs configuration after successful update: %s\n", strerror(errno));
257                                 exit(1);
258                         }
259                 } else {
260                         printf("Unable to adjust pfs configuration: %s\n", strerror(errno));
261                         exit(1);
262                 }
263         } else {
264                 printf("PFS Creation failed: %s\n", strerror(errno));
265                 exit(1);
266         }
267 }
268
269 static void
270 init_pfsd(hammer_pseudofs_data_t pfsd, int is_slave)
271 {
272         uint32_t status;
273
274         pfsd->sync_beg_tid = 1;
275         pfsd->sync_end_tid = 1;
276         pfsd->sync_beg_ts = 0;
277         pfsd->sync_end_ts = 0;
278         uuid_create(&pfsd->shared_uuid, &status);
279         uuid_create(&pfsd->unique_uuid, &status);
280         if (is_slave) {
281                 pfsd->master_id = -1;
282                 pfsd->mirror_flags |= HAMMER_PFSD_SLAVE;
283         } else {
284                 pfsd->master_id = 0;
285         }
286 }
287
288 static
289 void
290 dump_pfsd(hammer_pseudofs_data_t pfsd)
291 {
292         u_int32_t status;
293         char *str = NULL;
294
295         printf("    sync-beg-tid=0x%016llx\n", pfsd->sync_beg_tid);
296         printf("    sync-end-tid=0x%016llx\n", pfsd->sync_end_tid);
297         uuid_to_string(&pfsd->shared_uuid, &str, &status);
298         printf("    shared-uuid=%s\n", str);
299         uuid_to_string(&pfsd->unique_uuid, &str, &status);
300         printf("    unique-uuid=%s\n", str);
301         if (pfsd->mirror_flags & HAMMER_PFSD_SLAVE) {
302                 printf("    slave\n");
303         } else if (pfsd->master_id < 0) {
304                 printf("    no-mirror\n");
305         } else {
306                 printf("    master=%d\n", pfsd->master_id);
307         }
308         printf("    label=\"%s\"\n", pfsd->label);
309 }
310
311 static void
312 parse_pfsd_options(char **av, int ac, hammer_pseudofs_data_t pfsd)
313 {
314         char *cmd;
315         char *ptr;
316         int len;
317         uint32_t status;
318
319         while (ac) {
320                 cmd = *av;
321                 if ((ptr = strchr(cmd, '=')) != NULL)
322                         *ptr++ = 0;
323
324                 /*
325                  * Basic assignment value test
326                  */
327                 if (strcmp(cmd, "no-mirror") == 0 ||
328                     strcmp(cmd, "slave") == 0) {
329                         if (ptr) {
330                                 fprintf(stderr,
331                                         "option %s should not have "
332                                         "an assignment\n",
333                                         cmd);
334                                 exit(1);
335                         }
336                 } else {
337                         if (ptr == NULL) {
338                                 fprintf(stderr,
339                                         "option %s requires an assignment\n",
340                                         cmd);
341                                 exit(1);
342                         }
343                 }
344
345                 status = uuid_s_ok;
346                 if (strcmp(cmd, "sync-beg-tid") == 0) {
347                         pfsd->sync_beg_tid = strtoull(ptr, NULL, 16);
348                 } else if (strcmp(cmd, "sync-end-tid") == 0) {
349                         pfsd->sync_end_tid = strtoull(ptr, NULL, 16);
350                 } else if (strcmp(cmd, "shared-uuid") == 0) {
351                         uuid_from_string(ptr, &pfsd->shared_uuid, &status);
352                 } else if (strcmp(cmd, "unique-uuid") == 0) {
353                         uuid_from_string(ptr, &pfsd->unique_uuid, &status);
354                 } else if (strcmp(cmd, "master") == 0) {
355                         if (pfsd->mirror_flags & HAMMER_PFSD_SLAVE) {
356                                 fprintf(stderr, "master mode cannot be set on a slave PFS\n");
357                                 exit(1);
358                         }
359                         pfsd->master_id = strtol(ptr, NULL, 0);
360                         pfsd->mirror_flags &= ~HAMMER_PFSD_SLAVE;
361                 } else if (strcmp(cmd, "slave") == 0) {
362                         if ((pfsd->mirror_flags & HAMMER_PFSD_SLAVE) == 0) {
363                                 fprintf(stderr, "slave mode cannot be set on a master PFS\n");
364                                 exit(1);
365                         }
366                         pfsd->master_id = -1;
367                         pfsd->mirror_flags |= HAMMER_PFSD_SLAVE;
368                 } else if (strcmp(cmd, "no-mirror") == 0) {
369                         if (pfsd->mirror_flags & HAMMER_PFSD_SLAVE) {
370                                 fprintf(stderr, "no-mirror master mode cannot be set on a slave PFS\n");
371                                 exit(1);
372                         }
373                         pfsd->master_id = -1;
374                         pfsd->mirror_flags &= ~HAMMER_PFSD_SLAVE;
375                 } else if (strcmp(cmd, "label") == 0) {
376                         len = strlen(ptr);
377                         if (ptr[0] == '"' && ptr[len-1] == '"') {
378                                 ptr[len-1] = 0;
379                                 ++ptr;
380                         } else if (ptr[0] == '"') {
381                                 fprintf(stderr,
382                                         "option %s: malformed string\n",
383                                         cmd);
384                                 exit(1);
385                         }
386                         snprintf(pfsd->label, sizeof(pfsd->label), "%s", ptr);
387                 } else {
388                         fprintf(stderr, "invalid option: %s\n", cmd);
389                         exit(1);
390                 }
391                 if (status != uuid_s_ok) {
392                         fprintf(stderr, "option %s: error parsing uuid %s\n",
393                                 cmd, ptr);
394                         exit(1);
395                 }
396                 --ac;
397                 ++av;
398         }
399 }
400
401 static
402 void
403 pseudofs_usage(int code)
404 {
405         fprintf(stderr, 
406                 "hammer pfs-status <dirpath1>...<dirpathN>\n"
407                 "hammer pfs-master <dirpath> [options]\n"
408                 "hammer pfs-slave <dirpath> [options]\n"
409                 "hammer pfs-destroy <dirpath>\n"
410                 "hammer pfs-update <dirpath> [options]\n"
411                 "\n"
412                 "    sync-beg-tid=0x16llx\n"
413                 "    sync-end-tid=0x16llx\n"
414                 "    shared-uuid=0x16llx\n"
415                 "    unique-uuid=0x16llx\n"
416                 "    master=0-15\n"
417                 "    slave\n"
418                 "    no-mirror\n"
419                 "    label=\"string\"\n"
420         );
421         exit(code);
422 }
423