hammer2 - Allow @LABEL to be omitted
[dragonfly.git] / sbin / hammer / cmd_snapshot.c
blob93419c92d03f13cc2f32bebae518b66447a06441
1 /*
2 * Copyright (c) 2008 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
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.
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.
34 * $DragonFly: src/sbin/hammer/cmd_snapshot.c,v 1.7 2008/07/10 18:47:22 mneumann Exp $
37 #include "hammer.h"
39 #define DEFAULT_SNAPSHOT_NAME "snap-%Y%m%d-%H%M"
41 static void snapshot_usage(int exit_code);
42 static void snapshot_add(int fd, const char *fsym, const char *tsym,
43 const char *label, hammer_tid_t tid);
44 static void snapshot_ls(const char *path);
45 static void snapshot_del(int fsfd, hammer_tid_t tid);
46 static char *dirpart(const char *path);
49 * hammer snap <path> [<note>]
51 * Path may be a directory, softlink, or non-existent (a softlink will be
52 * created).
54 void
55 hammer_cmd_snap(char **av, int ac, int tostdout, int fsbase)
57 struct hammer_ioc_synctid synctid;
58 struct hammer_ioc_version version;
59 char *dirpath;
60 char *fsym = NULL;
61 char *tsym = NULL;
62 struct stat st;
63 char note[64];
64 int fsfd;
66 if (ac == 0 || ac > 2) {
67 snapshot_usage(1);
68 /* not reached */
71 if (ac == 2)
72 snprintf(note, sizeof(note), "%s", av[1]);
73 else
74 note[0] = 0;
77 * Figure out the softlink path and directory path
79 if (stat(av[0], &st) < 0) {
80 dirpath = dirpart(av[0]);
81 tsym = strdup(av[0]);
82 } else if (S_ISDIR(st.st_mode)) {
83 time_t t = time(NULL);
84 struct tm *tp;
85 char extbuf[64];
87 tp = localtime(&t);
88 strftime(extbuf, sizeof(extbuf), DEFAULT_SNAPSHOT_NAME, tp);
90 dirpath = strdup(av[0]);
91 asprintf(&tsym, "%s/%s", dirpath, extbuf);
92 } else {
93 err(2, "hammer snap: File %s exists and is not a directory",
94 av[0]);
95 /* not reached */
99 * Get a handle on some directory in the filesystem for the
100 * ioctl (so it is stored in the correct PFS).
102 fsfd = open(dirpath, O_RDONLY);
103 if (fsfd < 0) {
104 err(2, "hammer snap: Cannot open directory %s", dirpath);
105 /* not reached */
109 * Must be at least version 3 to use this command.
111 bzero(&version, sizeof(version));
113 if (ioctl(fsfd, HAMMERIOC_GET_VERSION, &version) < 0) {
114 err(2, "Unable to create snapshot");
115 /* not reached */
116 } else if (version.cur_version < 3) {
117 errx(2, "Unable to create snapshot: This directive requires "
118 "you to upgrade\n"
119 "the filesystem to version 3. "
120 "Use 'hammer snapshot' for legacy operation.");
121 /* not reached */
123 HammerVersion = version.cur_version;
126 * Synctid to get a transaction id for the snapshot.
128 bzero(&synctid, sizeof(synctid));
129 synctid.op = HAMMER_SYNCTID_SYNC2;
130 if (ioctl(fsfd, HAMMERIOC_SYNCTID, &synctid) < 0) {
131 err(2, "hammer snap: Synctid %s failed",
132 dirpath);
133 /* not reached */
135 if (tostdout) {
136 if (strcmp(dirpath, ".") == 0 || strcmp(dirpath, "..") == 0) {
137 printf("%s/@@0x%016jx\n",
138 dirpath, (uintmax_t)synctid.tid);
139 } else {
140 printf("%s@@0x%016jx\n",
141 dirpath, (uintmax_t)synctid.tid);
143 fsym = NULL;
144 tsym = NULL;
148 * Contents of the symlink being created.
150 if (fsbase) {
151 struct statfs buf;
153 if (statfs(dirpath, &buf) < 0) {
154 err(2, "hammer snap: Cannot determine mount for %s",
155 dirpath);
156 /* not reached */
158 asprintf(&fsym, "%s/@@0x%016jx",
159 buf.f_mntonname, (uintmax_t)synctid.tid);
160 } else if (strcmp(dirpath, ".") == 0 || strcmp(dirpath, "..") == 0) {
161 asprintf(&fsym, "%s/@@0x%016jx",
162 dirpath, (uintmax_t)synctid.tid);
163 } else {
164 asprintf(&fsym, "%s@@0x%016jx",
165 dirpath, (uintmax_t)synctid.tid);
169 * Create the snapshot.
171 snapshot_add(fsfd, fsym, tsym, note, synctid.tid);
172 free(dirpath);
173 free(fsym);
174 free(tsym);
178 * hammer snapls [<path> ...]
180 * If no arguments are specified snapshots for the PFS containing the
181 * current directory are listed.
183 void
184 hammer_cmd_snapls(char **av, int ac)
186 int i;
188 for (i = 0; i < ac; ++i)
189 snapshot_ls(av[i]);
190 if (ac == 0)
191 snapshot_ls(".");
195 * hammer snaprm <path> ...
196 * hammer snaprm <transid> ...
197 * hammer snaprm <filesystem> <transid> ...
199 void
200 hammer_cmd_snaprm(char **av, int ac)
202 struct stat st;
203 char linkbuf[1024];
204 intmax_t tid;
205 int fsfd = -1;
206 int i;
207 int delete;
208 enum snaprm_mode { none_m, path_m, tid_m } mode = none_m;
209 char *dirpath;
210 char *ptr, *ptr2;
212 if (ac == 0) {
213 snapshot_usage(1);
214 /* not reached */
217 for (i = 0; i < ac; ++i) {
218 if (lstat(av[i], &st) < 0) {
219 tid = strtoull(av[i], &ptr, 16);
220 if (*ptr) {
221 err(2, "hammer snaprm: not a file or tid: %s",
222 av[i]);
223 /* not reached */
225 if (mode == path_m) {
226 snapshot_usage(1);
227 /* not reached */
229 mode = tid_m;
230 if (fsfd < 0)
231 fsfd = open(".", O_RDONLY);
232 snapshot_del(fsfd, tid);
233 } else if (S_ISDIR(st.st_mode)) {
234 if (i != 0 || ac < 2) {
235 snapshot_usage(1);
236 /* not reached */
238 if (fsfd >= 0)
239 close(fsfd);
240 fsfd = open(av[i], O_RDONLY);
241 if (fsfd < 0) {
242 err(2, "hammer snaprm: cannot open dir %s",
243 av[i]);
244 /* not reached */
246 mode = tid_m;
247 } else if (S_ISLNK(st.st_mode)) {
248 dirpath = dirpart(av[i]);
249 bzero(linkbuf, sizeof(linkbuf));
250 if (readlink(av[i], linkbuf, sizeof(linkbuf) - 1) < 0) {
251 err(2, "hammer snaprm: cannot read softlink: "
252 "%s", av[i]);
253 /* not reached */
255 if (linkbuf[0] == '/') {
256 free(dirpath);
257 dirpath = dirpart(linkbuf);
258 } else {
259 asprintf(&ptr, "%s/%s", dirpath, linkbuf);
260 free(dirpath);
261 dirpath = dirpart(ptr);
262 free(ptr);
265 if (fsfd >= 0)
266 close(fsfd);
267 fsfd = open(dirpath, O_RDONLY);
268 if (fsfd < 0) {
269 err(2, "hammer snaprm: cannot open dir %s",
270 dirpath);
271 /* not reached */
274 delete = 1;
275 if (i == 0 && ac > 1) {
276 mode = path_m;
277 if (lstat(av[1], &st) < 0) {
278 tid = strtoull(av[1], &ptr, 16);
279 if (*ptr == '\0') {
280 delete = 0;
281 mode = tid_m;
284 } else {
285 if (mode == tid_m) {
286 snapshot_usage(1);
287 /* not reached */
289 mode = path_m;
291 if (delete && (ptr = strrchr(linkbuf, '@')) &&
292 ptr > linkbuf && ptr[-1] == '@' && ptr[1]) {
293 tid = strtoull(ptr + 1, &ptr2, 16);
294 if (*ptr2 == '\0') {
295 snapshot_del(fsfd, tid);
296 remove(av[i]);
299 free(dirpath);
300 } else {
301 err(2, "hammer snaprm: not directory or snapshot "
302 "softlink: %s", av[i]);
303 /* not reached */
306 if (fsfd >= 0)
307 close(fsfd);
311 * snapshot <softlink-dir>
312 * snapshot <filesystem> <softlink-dir> [<note>]
314 void
315 hammer_cmd_snapshot(char **av, int ac)
317 const char *filesystem;
318 const char *softlink_dir;
319 char *softlink_fmt;
320 struct statfs buf;
321 struct stat st;
322 struct hammer_ioc_synctid synctid;
323 char *from;
324 char *to;
325 char *note = NULL;
327 if (ac == 1) {
328 filesystem = NULL;
329 softlink_dir = av[0];
330 } else if (ac == 2) {
331 filesystem = av[0];
332 softlink_dir = av[1];
333 } else if (ac == 3) {
334 filesystem = av[0];
335 softlink_dir = av[1];
336 note = av[2];
337 } else {
338 snapshot_usage(1);
339 /* not reached */
342 if (stat(softlink_dir, &st) == 0) {
343 if (!S_ISDIR(st.st_mode)) {
344 err(2, "File %s already exists", softlink_dir);
345 /* not reached */
348 if (filesystem == NULL) {
349 if (statfs(softlink_dir, &buf) != 0) {
350 err(2, "Unable to determine filesystem of %s",
351 softlink_dir);
352 /* not reached */
354 filesystem = buf.f_mntonname;
357 softlink_fmt = malloc(strlen(softlink_dir) + 1 + 1 +
358 sizeof(DEFAULT_SNAPSHOT_NAME));
359 if (softlink_fmt == NULL) {
360 err(2, "Failed to allocate string");
361 /* not reached */
364 strcpy(softlink_fmt, softlink_dir);
365 if (softlink_fmt[strlen(softlink_fmt)-1] != '/')
366 strcat(softlink_fmt, "/");
367 strcat(softlink_fmt, DEFAULT_SNAPSHOT_NAME);
368 } else {
369 softlink_fmt = strdup(softlink_dir);
371 if (filesystem == NULL) {
373 * strip-off last '/path' segment to get the softlink
374 * directory, which we need to determine the filesystem
375 * we are on.
377 char *pos = strrchr(softlink_fmt, '/');
378 if (pos != NULL)
379 *pos = '\0';
381 if (stat(softlink_fmt, &st) != 0 ||
382 !S_ISDIR(st.st_mode)) {
383 err(2, "Unable to determine softlink dir %s",
384 softlink_fmt);
385 /* not reached */
387 if (statfs(softlink_fmt, &buf) != 0) {
388 err(2, "Unable to determine filesystem of %s",
389 softlink_fmt);
390 /* not reached */
392 filesystem = buf.f_mntonname;
394 /* restore '/' */
395 if (pos != NULL)
396 *pos = '/';
401 * Synctid
403 bzero(&synctid, sizeof(synctid));
404 synctid.op = HAMMER_SYNCTID_SYNC2;
406 int fd = open(filesystem, O_RDONLY);
407 if (fd < 0) {
408 err(2, "Unable to open %s", filesystem);
409 /* not reached */
411 if (ioctl(fd, HAMMERIOC_SYNCTID, &synctid) < 0) {
412 err(2, "Synctid %s failed", filesystem);
413 /* not reached */
416 asprintf(&from, "%s/@@0x%016jx", filesystem, (uintmax_t)synctid.tid);
417 if (from == NULL) {
418 err(2, "Couldn't generate string");
419 /* not reached */
422 int sz = strlen(softlink_fmt) + 50;
423 to = malloc(sz);
424 if (to == NULL) {
425 err(2, "Failed to allocate string");
426 /* not reached */
429 time_t t = time(NULL);
430 if (strftime(to, sz, softlink_fmt, localtime(&t)) == 0) {
431 err(2, "String buffer too small");
432 /* not reached */
435 asprintf(&from, "%s/@@0x%016jx", filesystem, (uintmax_t)synctid.tid);
437 snapshot_add(fd, from, to, note, synctid.tid);
439 close(fd);
440 printf("%s\n", to);
442 free(softlink_fmt);
443 free(from);
444 free(to);
447 static
448 void
449 snapshot_add(int fd, const char *fsym, const char *tsym, const char *label,
450 hammer_tid_t tid)
452 struct hammer_ioc_version version;
453 struct hammer_ioc_snapshot snapshot;
455 bzero(&version, sizeof(version));
456 bzero(&snapshot, sizeof(snapshot));
459 * For HAMMER filesystem v3+ the snapshot is recorded in meta-data.
461 if (ioctl(fd, HAMMERIOC_GET_VERSION, &version) == 0 &&
462 version.cur_version >= 3) {
463 HammerVersion = version.cur_version;
464 snapshot.index = 0;
465 snapshot.count = 1;
466 snapshot.snaps[0].tid = tid;
467 snapshot.snaps[0].ts = time(NULL) * 1000000ULL;
468 if (label) {
469 snprintf(snapshot.snaps[0].label,
470 sizeof(snapshot.snaps[0].label),
471 "%s",
472 label);
474 if (ioctl(fd, HAMMERIOC_ADD_SNAPSHOT, &snapshot) < 0) {
475 err(2, "Unable to create snapshot");
476 /* not reached */
477 } else if (snapshot.head.error &&
478 snapshot.head.error != EEXIST) {
479 errx(2, "Unable to create snapshot: %s",
480 strerror(snapshot.head.error));
481 /* not reached */
486 * Create a symlink for the snapshot. If a file exists with the same
487 * name the new symlink will replace it.
489 if (fsym && tsym) {
490 remove(tsym);
491 if (symlink(fsym, tsym) < 0) {
492 err(2, "Unable to create symlink %s", tsym);
493 /* not reached */
498 static
499 void
500 snapshot_ls(const char *path)
502 struct hammer_ioc_info info;
503 struct hammer_ioc_snapshot snapshot;
504 struct hammer_ioc_pseudofs_rw pfs;
505 struct hammer_pseudofs_data pfs_od;
506 hammer_snapshot_data_t snap;
507 struct tm *tp;
508 time_t t;
509 uint32_t i;
510 int fd;
511 char snapts[64];
513 fd = open(path, O_RDONLY);
514 if (fd < 0) {
515 err(2, "hammer snapls: cannot open %s", path);
516 /* not reached */
519 clrpfs(&pfs, &pfs_od, -1);
520 if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) < 0) {
521 err(2, "hammer snapls: cannot retrieve PFS info on %s", path);
522 /* not reached */
525 bzero(&info, sizeof(info));
526 if ((ioctl(fd, HAMMERIOC_GET_INFO, &info)) < 0) {
527 err(2, "hammer snapls: cannot retrieve HAMMER info");
528 /* not reached */
531 printf("Snapshots on %s\tPFS#%d\n", path, pfs.pfs_id);
532 printf("Transaction ID\t\tTimestamp\t\tNote\n");
534 bzero(&snapshot, sizeof(snapshot));
535 do {
536 if (ioctl(fd, HAMMERIOC_GET_SNAPSHOT, &snapshot) < 0) {
537 err(2, "hammer snapls: %s: not HAMMER fs or "
538 "version < 3", path);
539 /* not reached */
541 for (i = 0; i < snapshot.count; ++i) {
542 snap = &snapshot.snaps[i];
544 t = snap->ts / 1000000ULL;
545 tp = localtime(&t);
546 strftime(snapts, sizeof(snapts),
547 "%Y-%m-%d %H:%M:%S %Z", tp);
548 printf("0x%016jx\t%s\t%s\n",
549 (uintmax_t)snap->tid, snapts,
550 strlen(snap->label) ? snap->label : "-");
552 } while (snapshot.head.error == 0 && snapshot.count);
555 static
556 void
557 snapshot_del(int fsfd, hammer_tid_t tid)
559 struct hammer_ioc_snapshot snapshot;
560 struct hammer_ioc_version version;
562 bzero(&version, sizeof(version));
564 if (ioctl(fsfd, HAMMERIOC_GET_VERSION, &version) < 0) {
565 err(2, "hammer snaprm 0x%016jx", (uintmax_t)tid);
566 /* not reached */
568 HammerVersion = version.cur_version;
569 if (version.cur_version < 3) {
570 errx(2, "hammer snaprm 0x%016jx: You must upgrade to version "
571 " 3 to use this directive", (uintmax_t)tid);
572 /* not reached */
575 bzero(&snapshot, sizeof(snapshot));
576 snapshot.count = 1;
577 snapshot.snaps[0].tid = tid;
580 * Do not abort if we are unable to remove the meta-data.
582 if (ioctl(fsfd, HAMMERIOC_DEL_SNAPSHOT, &snapshot) < 0) {
583 err(2, "hammer snaprm 0x%016jx",
584 (uintmax_t)tid);
585 /* not reached */
586 } else if (snapshot.head.error == ENOENT) {
587 fprintf(stderr, "Warning: hammer snaprm 0x%016jx: "
588 "meta-data not found\n",
589 (uintmax_t)tid);
590 } else if (snapshot.head.error) {
591 fprintf(stderr, "Warning: hammer snaprm 0x%016jx: %s\n",
592 (uintmax_t)tid, strerror(snapshot.head.error));
596 static
597 void
598 snapshot_usage(int exit_code)
600 fprintf(stderr,
601 "hammer snap <path> [<note>]\t\tcreate snapshot & link, points to\n"
602 "\t\t\t\t\tbase of PFS mount\n"
603 "hammer snaplo <path> [<note>]\t\tcreate snapshot & link, points to\n"
604 "\t\t\t\t\ttarget dir\n"
605 "hammer snapq <dir> [<note>]\t\tcreate snapshot, output path to stdout\n"
606 "hammer snaprm <path> ...\t\tdelete snapshots; filesystem is CWD\n"
607 "hammer snaprm <transid> ...\t\tdelete snapshots\n"
608 "hammer snaprm <filesystem> <transid> ...\tdelete snapshots\n"
609 "hammer snapls [<path> ...]\t\tlist available snapshots\n"
610 "\n"
611 "NOTE: Snapshots are created in filesystem meta-data, any directory\n"
612 " in a HAMMER filesystem or PFS may be specified. If the path\n"
613 " specified does not exist this function will also create a\n"
614 " softlink.\n"
615 "\n"
616 " When deleting snapshots transaction ids may be directly specified\n"
617 " or file paths to snapshot softlinks may be specified. If a\n"
618 " softlink is specified the softlink will also be deleted.\n"
619 "\n"
620 "NOTE: The old 'hammer snapshot [<filesystem>] <snapshot-dir>' form\n"
621 " is still accepted but is a deprecated form. This form will\n"
622 " work for older hammer versions. The new forms only work for\n"
623 " HAMMER version 3 or later filesystems. HAMMER can be upgraded\n"
624 " to version 3 in-place.\n"
626 exit(exit_code);
629 static
630 char *
631 dirpart(const char *path)
633 const char *ptr;
634 char *res;
636 ptr = strrchr(path, '/');
637 if (ptr) {
638 while (ptr > path && ptr[-1] == '/')
639 --ptr;
640 if (ptr == path)
641 ptr = NULL;
643 if (ptr == NULL) {
644 path = ".";
645 ptr = path + 1;
647 res = malloc(ptr - path + 1);
648 bcopy(path, res, ptr - path);
649 res[ptr - path] = 0;
650 return(res);