| Commit | Line | Data |
|---|---|---|
| 6b669ab4 MN |
1 | /* |
| 2 | * Copyright (c) 2008 The DragonFly Project. All rights reserved. | |
| 84e57c2c | 3 | * |
| 6b669ab4 MN |
4 | * This code is derived from software contributed to The DragonFly Project |
| 5 | * by Matthew Dillon <dillon@backplane.com> | |
| 84e57c2c | 6 | * |
| 6b669ab4 MN |
7 | * Redistribution and use in source and binary forms, with or without |
| 8 | * modification, are permitted provided that the following conditions | |
| 9 | * are met: | |
| 84e57c2c | 10 | * |
| 6b669ab4 MN |
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. | |
| 84e57c2c | 20 | * |
| 6b669ab4 MN |
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. | |
| 84e57c2c | 33 | * |
| e0af86f0 | 34 | * $DragonFly: src/sbin/hammer/cmd_snapshot.c,v 1.7 2008/07/10 18:47:22 mneumann Exp $ |
| 6b669ab4 MN |
35 | */ |
| 36 | ||
| 37 | #include "hammer.h" | |
| 38 | #include <sys/param.h> | |
| 39 | #include <sys/mount.h> | |
| 96e6b862 MN |
40 | #include <sys/types.h> |
| 41 | #include <sys/stat.h> | |
| 6b669ab4 | 42 | #include <unistd.h> |
| f8052532 MN |
43 | #include <string.h> |
| 44 | #include <time.h> | |
| 6b669ab4 | 45 | |
| 2c1d3cef SK |
46 | extern char *find_pfs_mount(int pfsid, uuid_t parentuuid, int ismaster); |
| 47 | ||
| 96e6b862 MN |
48 | #define DEFAULT_SNAPSHOT_NAME "snap-%Y%m%d-%H%M" |
| 49 | ||
| 6b669ab4 | 50 | static void snapshot_usage(int exit_code); |
| 83f2a3aa MD |
51 | static void snapshot_add(int fd, const char *fsym, const char *tsym, |
| 52 | const char *label, hammer_tid_t tid); | |
| 53 | static void snapshot_ls(const char *path); | |
| 54 | static void snapshot_del(int fsfd, hammer_tid_t tid); | |
| 55 | static char *dirpart(const char *path); | |
| 56 | ||
| 57 | /* | |
| 16265794 | 58 | * hammer snap <path> [<note>] |
| 83f2a3aa | 59 | * |
| 16265794 | 60 | * Path may be a directory, softlink, or non-existent (a softlink will be |
| 83f2a3aa MD |
61 | * created). |
| 62 | */ | |
| 63 | void | |
| 64 | hammer_cmd_snap(char **av, int ac, int tostdout, int fsbase) | |
| 65 | { | |
| 66 | struct hammer_ioc_synctid synctid; | |
| 67 | struct hammer_ioc_version version; | |
| 68 | char *dirpath; | |
| 69 | char *fsym; | |
| 70 | char *tsym; | |
| 71 | struct stat st; | |
| 72 | char note[64]; | |
| 73 | int fsfd; | |
| 74 | ||
| 75 | if (ac == 0 || ac > 2) { | |
| 76 | snapshot_usage(1); | |
| 77 | /* not reached */ | |
| 78 | exit(1); | |
| 79 | } | |
| 80 | ||
| 81 | if (ac == 2) | |
| 82 | snprintf(note, sizeof(note), "%s", av[1]); | |
| 83 | else | |
| 84 | note[0] = 0; | |
| 85 | ||
| 86 | /* | |
| 87 | * Figure out the softlink path and directory path | |
| 88 | */ | |
| 89 | if (stat(av[0], &st) < 0) { | |
| 90 | dirpath = dirpart(av[0]); | |
| 91 | tsym = av[0]; | |
| 92 | } else if (S_ISLNK(st.st_mode)) { | |
| 93 | dirpath = dirpart(av[0]); | |
| 94 | tsym = av[0]; | |
| 95 | } else if (S_ISDIR(st.st_mode)) { | |
| 96 | time_t t = time(NULL); | |
| 97 | struct tm *tp; | |
| 98 | char extbuf[64]; | |
| 99 | ||
| 100 | tp = localtime(&t); | |
| 101 | strftime(extbuf, sizeof(extbuf), DEFAULT_SNAPSHOT_NAME, tp); | |
| 102 | ||
| 103 | dirpath = strdup(av[0]); | |
| 104 | asprintf(&tsym, "%s/%s", dirpath, extbuf); | |
| 105 | } else { | |
| 106 | err(2, "hammer snap: File %s exists and is not a softlink\n", | |
| 107 | av[0]); | |
| 108 | /* not reached */ | |
| 109 | } | |
| 110 | ||
| 111 | /* | |
| 112 | * Get a handle on some directory in the filesystem for the | |
| 113 | * ioctl (so it is stored in the correct PFS). | |
| 114 | */ | |
| 115 | fsfd = open(dirpath, O_RDONLY); | |
| 116 | if (fsfd < 0) { | |
| 16265794 | 117 | err(2, "hammer snap: Cannot open directory %s\n", dirpath); |
| 83f2a3aa MD |
118 | /* not reached */ |
| 119 | } | |
| 120 | ||
| 121 | /* | |
| 122 | * Must be at least version 3 to use this command. | |
| 123 | */ | |
| 124 | bzero(&version, sizeof(version)); | |
| 125 | ||
| 126 | if (ioctl(fsfd, HAMMERIOC_GET_VERSION, &version) < 0) { | |
| 127 | err(2, "Unable to create snapshot"); | |
| 128 | /* not reached */ | |
| 129 | } else if (version.cur_version < 3) { | |
| 130 | errx(2, "Unable to create snapshot: This directive requires " | |
| 16265794 TN |
131 | "you to upgrade\n" |
| 132 | "the filesystem to version 3. " | |
| 133 | "Use 'hammer snapshot' for legacy operation."); | |
| 83f2a3aa MD |
134 | /* not reached */ |
| 135 | } | |
| 136 | ||
| 137 | /* | |
| 138 | * Synctid to get a transaction id for the snapshot. | |
| 139 | */ | |
| 140 | bzero(&synctid, sizeof(synctid)); | |
| 141 | synctid.op = HAMMER_SYNCTID_SYNC2; | |
| 142 | if (ioctl(fsfd, HAMMERIOC_SYNCTID, &synctid) < 0) { | |
| 143 | err(2, "hammer snap: Synctid %s failed", | |
| 144 | dirpath); | |
| 145 | } | |
| 146 | if (tostdout) { | |
| b78f1a00 MD |
147 | if (strcmp(dirpath, ".") == 0 || strcmp(dirpath, "..") == 0) { |
| 148 | printf("%s/@@0x%016jx\n", | |
| 149 | dirpath, (uintmax_t)synctid.tid); | |
| 150 | } else { | |
| 151 | printf("%s@@0x%016jx\n", | |
| 152 | dirpath, (uintmax_t)synctid.tid); | |
| 153 | } | |
| 83f2a3aa MD |
154 | fsym = NULL; |
| 155 | tsym = NULL; | |
| 156 | } | |
| 157 | ||
| 158 | /* | |
| 159 | * Contents of the symlink being created. | |
| 160 | */ | |
| 161 | if (fsbase) { | |
| 162 | struct statfs buf; | |
| 163 | ||
| 164 | if (statfs(dirpath, &buf) < 0) { | |
| 16265794 | 165 | err(2, "hammer snap: Cannot determine mount for %s", |
| 83f2a3aa MD |
166 | dirpath); |
| 167 | } | |
| 168 | asprintf(&fsym, "%s/@@0x%016jx", | |
| 169 | buf.f_mntonname, (uintmax_t)synctid.tid); | |
| b78f1a00 | 170 | } else if (strcmp(dirpath, ".") == 0 || strcmp(dirpath, "..") == 0) { |
| 83f2a3aa MD |
171 | asprintf(&fsym, "%s/@@0x%016jx", |
| 172 | dirpath, (uintmax_t)synctid.tid); | |
| b78f1a00 MD |
173 | } else { |
| 174 | asprintf(&fsym, "%s@@0x%016jx", | |
| 175 | dirpath, (uintmax_t)synctid.tid); | |
| 83f2a3aa MD |
176 | } |
| 177 | ||
| 178 | /* | |
| 179 | * Create the snapshot. | |
| 180 | */ | |
| 181 | snapshot_add(fsfd, fsym, tsym, note, synctid.tid); | |
| 182 | free(dirpath); | |
| 183 | } | |
| 184 | ||
| 185 | /* | |
| 16265794 | 186 | * hammer snapls [<path> ...] |
| 83f2a3aa MD |
187 | * |
| 188 | * If no arguments are specified snapshots for the PFS containing the | |
| 189 | * current directory are listed. | |
| 190 | */ | |
| 191 | void | |
| 192 | hammer_cmd_snapls(char **av, int ac) | |
| 193 | { | |
| 194 | int i; | |
| 195 | ||
| 196 | for (i = 0; i < ac; ++i) | |
| 197 | snapshot_ls(av[i]); | |
| 198 | if (ac == 0) | |
| 199 | snapshot_ls("."); | |
| 200 | } | |
| 201 | ||
| 202 | /* | |
| aaf93065 TN |
203 | * hammer snaprm <path> ... |
| 204 | * hammer snaprm <transid> ... | |
| 205 | * hammer snaprm <filesystem> <transid> ... | |
| 83f2a3aa MD |
206 | */ |
| 207 | void | |
| 208 | hammer_cmd_snaprm(char **av, int ac) | |
| 209 | { | |
| 210 | struct stat st; | |
| 211 | char linkbuf[1024]; | |
| 212 | intmax_t tid; | |
| 213 | int fsfd = -1; | |
| 214 | int i; | |
| aa6862a4 TN |
215 | int delete; |
| 216 | enum snaprm_mode { none_m, path_m, tid_m } mode = none_m; | |
| 83f2a3aa | 217 | char *dirpath; |
| aa6862a4 TN |
218 | char *ptr, *ptr2; |
| 219 | ||
| 220 | if (ac == 0) { | |
| 221 | snapshot_usage(1); | |
| 222 | /* not reached */ | |
| 223 | } | |
| 83f2a3aa MD |
224 | |
| 225 | for (i = 0; i < ac; ++i) { | |
| 226 | if (lstat(av[i], &st) < 0) { | |
| 227 | tid = strtoull(av[i], &ptr, 16); | |
| 662838f8 | 228 | if (*ptr) { |
| 83f2a3aa MD |
229 | err(2, "hammer snaprm: not a file or tid: %s", |
| 230 | av[i]); | |
| 231 | /* not reached */ | |
| 232 | } | |
| aa6862a4 TN |
233 | if (mode == path_m) { |
| 234 | snapshot_usage(1); | |
| 235 | /* not reached */ | |
| 236 | } | |
| 237 | mode = tid_m; | |
| 92ed14a3 MD |
238 | if (fsfd < 0) |
| 239 | fsfd = open(".", O_RDONLY); | |
| 83f2a3aa MD |
240 | snapshot_del(fsfd, tid); |
| 241 | } else if (S_ISDIR(st.st_mode)) { | |
| aa6862a4 TN |
242 | if (i != 0 || ac < 2) { |
| 243 | snapshot_usage(1); | |
| 244 | /* not reached */ | |
| 245 | } | |
| 83f2a3aa MD |
246 | if (fsfd >= 0) |
| 247 | close(fsfd); | |
| 248 | fsfd = open(av[i], O_RDONLY); | |
| 249 | if (fsfd < 0) { | |
| 250 | err(2, "hammer snaprm: cannot open dir %s", | |
| 251 | av[i]); | |
| 252 | /* not reached */ | |
| 253 | } | |
| aa6862a4 | 254 | mode = tid_m; |
| 83f2a3aa MD |
255 | } else if (S_ISLNK(st.st_mode)) { |
| 256 | dirpath = dirpart(av[i]); | |
| bf2c6489 MD |
257 | bzero(linkbuf, sizeof(linkbuf)); |
| 258 | if (readlink(av[i], linkbuf, sizeof(linkbuf) - 1) < 0) { | |
| 259 | err(2, "hammer snaprm: cannot read softlink: " | |
| 260 | "%s", av[i]); | |
| 261 | /* not reached */ | |
| 262 | } | |
| 263 | if (linkbuf[0] == '/') { | |
| 264 | free(dirpath); | |
| 265 | dirpath = dirpart(linkbuf); | |
| 266 | } else { | |
| 267 | asprintf(&ptr, "%s/%s", dirpath, linkbuf); | |
| 268 | free(dirpath); | |
| 269 | dirpath = dirpart(ptr); | |
| 270 | } | |
| 271 | ||
| 83f2a3aa MD |
272 | if (fsfd >= 0) |
| 273 | close(fsfd); | |
| 274 | fsfd = open(dirpath, O_RDONLY); | |
| 275 | if (fsfd < 0) { | |
| 276 | err(2, "hammer snaprm: cannot open dir %s", | |
| 277 | dirpath); | |
| 278 | /* not reached */ | |
| 279 | } | |
| 280 | ||
| aa6862a4 TN |
281 | delete = 1; |
| 282 | if (i == 0 && ac > 1) { | |
| 283 | mode = path_m; | |
| 284 | if (lstat(av[1], &st) < 0) { | |
| 285 | tid = strtoull(av[1], &ptr, 16); | |
| 286 | if (*ptr == '\0') { | |
| 287 | delete = 0; | |
| 288 | mode = tid_m; | |
| 289 | } | |
| 290 | } | |
| 291 | } else { | |
| 292 | if (mode == tid_m) { | |
| 293 | snapshot_usage(1); | |
| 294 | /* not reached */ | |
| 295 | } | |
| 296 | mode = path_m; | |
| 297 | } | |
| 298 | if (delete && (ptr = strrchr(linkbuf, '@')) && | |
| a6d893d9 | 299 | ptr > linkbuf && ptr[-1] == '@' && ptr[1]) { |
| aa6862a4 TN |
300 | tid = strtoull(ptr + 1, &ptr2, 16); |
| 301 | if (*ptr2 == '\0') { | |
| 302 | snapshot_del(fsfd, tid); | |
| 303 | remove(av[i]); | |
| 304 | } | |
| 83f2a3aa | 305 | } |
| 83f2a3aa MD |
306 | free(dirpath); |
| 307 | } else { | |
| 308 | err(2, "hammer snaprm: not directory or snapshot " | |
| 309 | "softlink: %s", av[i]); | |
| 310 | /* not reached */ | |
| 311 | } | |
| 312 | } | |
| 313 | if (fsfd >= 0) | |
| 314 | close(fsfd); | |
| 315 | } | |
| 6b669ab4 MN |
316 | |
| 317 | /* | |
| 16265794 TN |
318 | * snapshot <softlink-dir> |
| 319 | * snapshot <filesystem> <softlink-dir> [<note>] | |
| 6b669ab4 MN |
320 | */ |
| 321 | void | |
| 322 | hammer_cmd_snapshot(char **av, int ac) | |
| 323 | { | |
| 6b669ab4 | 324 | const char *filesystem; |
| 96e6b862 MN |
325 | const char *softlink_dir; |
| 326 | char *softlink_fmt; | |
| 6b669ab4 | 327 | struct statfs buf; |
| 96e6b862 | 328 | struct stat st; |
| 6b669ab4 | 329 | struct hammer_ioc_synctid synctid; |
| 6b669ab4 MN |
330 | char *from; |
| 331 | char *to; | |
| b5ec5ad4 | 332 | char *note = NULL; |
| 6b669ab4 | 333 | |
| f8052532 MN |
334 | if (ac == 1) { |
| 335 | filesystem = NULL; | |
| 96e6b862 | 336 | softlink_dir = av[0]; |
| 3267eb8a | 337 | } else if (ac == 2) { |
| f8052532 | 338 | filesystem = av[0]; |
| 96e6b862 | 339 | softlink_dir = av[1]; |
| b5ec5ad4 MD |
340 | } else if (ac == 3) { |
| 341 | filesystem = av[0]; | |
| 342 | softlink_dir = av[1]; | |
| 343 | note = av[2]; | |
| 3267eb8a | 344 | } else { |
| 6b669ab4 | 345 | snapshot_usage(1); |
| 83f2a3aa MD |
346 | /* not reached */ |
| 347 | softlink_dir = NULL; | |
| 348 | filesystem = NULL; | |
| f8052532 MN |
349 | } |
| 350 | ||
| 96e6b862 | 351 | if (stat(softlink_dir, &st) == 0) { |
| 84e57c2c | 352 | if (!S_ISDIR(st.st_mode)) |
| 96e6b862 MN |
353 | err(2, "File %s already exists", softlink_dir); |
| 354 | ||
| 355 | if (filesystem == NULL) { | |
| 356 | if (statfs(softlink_dir, &buf) != 0) { | |
| 357 | err(2, "Unable to determine filesystem of %s", | |
| 358 | softlink_dir); | |
| 359 | } | |
| 360 | filesystem = buf.f_mntonname; | |
| 361 | } | |
| 362 | ||
| 363 | softlink_fmt = malloc(strlen(softlink_dir) + 1 + 1 + | |
| 364 | sizeof(DEFAULT_SNAPSHOT_NAME)); | |
| 365 | if (softlink_fmt == NULL) | |
| 366 | err(2, "Failed to allocate string"); | |
| 84e57c2c | 367 | |
| 96e6b862 MN |
368 | strcpy(softlink_fmt, softlink_dir); |
| 369 | if (softlink_fmt[strlen(softlink_fmt)-1] != '/') | |
| 370 | strcat(softlink_fmt, "/"); | |
| 84e57c2c | 371 | strcat(softlink_fmt, DEFAULT_SNAPSHOT_NAME); |
| 3267eb8a | 372 | } else { |
| 96e6b862 MN |
373 | softlink_fmt = strdup(softlink_dir); |
| 374 | ||
| 375 | if (filesystem == NULL) { | |
| 84e57c2c | 376 | /* |
| 96e6b862 MN |
377 | * strip-off last '/path' segment to get the softlink |
| 378 | * directory, which we need to determine the filesystem | |
| 379 | * we are on. | |
| 380 | */ | |
| 381 | char *pos = strrchr(softlink_fmt, '/'); | |
| a231693e MN |
382 | if (pos != NULL) |
| 383 | *pos = '\0'; | |
| 96e6b862 MN |
384 | |
| 385 | if (stat(softlink_fmt, &st) != 0 || | |
| 386 | !S_ISDIR(st.st_mode)) { | |
| a231693e | 387 | err(2, "Unable to determine softlink dir %s", |
| 96e6b862 | 388 | softlink_fmt); |
| a231693e | 389 | } |
| 96e6b862 MN |
390 | if (statfs(softlink_fmt, &buf) != 0) { |
| 391 | err(2, "Unable to determine filesystem of %s", | |
| 392 | softlink_fmt); | |
| 393 | } | |
| 394 | filesystem = buf.f_mntonname; | |
| 6b669ab4 | 395 | |
| e0af86f0 | 396 | /* restore '/' */ |
| 96e6b862 MN |
397 | if (pos != NULL) |
| 398 | *pos = '/'; | |
| f8052532 | 399 | } |
| f8052532 | 400 | } |
| 84e57c2c | 401 | |
| f8052532 | 402 | /* |
| 84e57c2c | 403 | * Synctid |
| f8052532 | 404 | */ |
| 6b669ab4 MN |
405 | bzero(&synctid, sizeof(synctid)); |
| 406 | synctid.op = HAMMER_SYNCTID_SYNC2; | |
| 83f2a3aa | 407 | |
| 96e6b862 | 408 | int fd = open(filesystem, O_RDONLY); |
| 6b669ab4 MN |
409 | if (fd < 0) |
| 410 | err(2, "Unable to open %s", filesystem); | |
| 6764f177 | 411 | if (ioctl(fd, HAMMERIOC_SYNCTID, &synctid) < 0) |
| 6b669ab4 | 412 | err(2, "Synctid %s failed", filesystem); |
| 6b669ab4 | 413 | |
| a276dc6b | 414 | asprintf(&from, "%s/@@0x%016jx", filesystem, (uintmax_t)synctid.tid); |
| 6764f177 MN |
415 | if (from == NULL) |
| 416 | err(2, "Couldn't generate string"); | |
| 84e57c2c | 417 | |
| f8052532 MN |
418 | int sz = strlen(softlink_fmt) + 50; |
| 419 | to = malloc(sz); | |
| 6764f177 | 420 | if (to == NULL) |
| f8052532 | 421 | err(2, "Failed to allocate string"); |
| 84e57c2c | 422 | |
| f8052532 MN |
423 | time_t t = time(NULL); |
| 424 | if (strftime(to, sz, softlink_fmt, localtime(&t)) == 0) | |
| 425 | err(2, "String buffer too small"); | |
| 6764f177 | 426 | |
| 83f2a3aa MD |
427 | asprintf(&from, "%s/@@0x%016jx", filesystem, (uintmax_t)synctid.tid); |
| 428 | ||
| b5ec5ad4 | 429 | snapshot_add(fd, from, to, note, synctid.tid); |
| 83f2a3aa MD |
430 | |
| 431 | close(fd); | |
| 6764f177 | 432 | printf("%s\n", to); |
| 6b669ab4 | 433 | |
| 96e6b862 | 434 | free(softlink_fmt); |
| 6764f177 MN |
435 | free(from); |
| 436 | free(to); | |
| 6b669ab4 MN |
437 | } |
| 438 | ||
| 439 | static | |
| 440 | void | |
| 83f2a3aa MD |
441 | snapshot_add(int fd, const char *fsym, const char *tsym, const char *label, |
| 442 | hammer_tid_t tid) | |
| 443 | { | |
| 444 | struct hammer_ioc_version version; | |
| 445 | struct hammer_ioc_snapshot snapshot; | |
| 446 | ||
| 447 | bzero(&version, sizeof(version)); | |
| 448 | bzero(&snapshot, sizeof(snapshot)); | |
| 449 | ||
| b5ec5ad4 MD |
450 | /* |
| 451 | * For HAMMER filesystem v3+ the snapshot is recorded in meta-data. | |
| 452 | */ | |
| 83f2a3aa MD |
453 | if (ioctl(fd, HAMMERIOC_GET_VERSION, &version) == 0 && |
| 454 | version.cur_version >= 3) { | |
| 455 | snapshot.index = 0; | |
| 456 | snapshot.count = 1; | |
| 457 | snapshot.snaps[0].tid = tid; | |
| 458 | snapshot.snaps[0].ts = time(NULL) * 1000000ULL; | |
| 459 | if (label) { | |
| 460 | snprintf(snapshot.snaps[0].label, | |
| 461 | sizeof(snapshot.snaps[0].label), | |
| 462 | "%s", | |
| 463 | label); | |
| 464 | } | |
| 465 | if (ioctl(fd, HAMMERIOC_ADD_SNAPSHOT, &snapshot) < 0) { | |
| 466 | err(2, "Unable to create snapshot"); | |
| 467 | } else if (snapshot.head.error && | |
| 468 | snapshot.head.error != EEXIST) { | |
| 469 | errx(2, "Unable to create snapshot: %s\n", | |
| 470 | strerror(snapshot.head.error)); | |
| 471 | } | |
| 472 | } | |
| b5ec5ad4 MD |
473 | |
| 474 | /* | |
| 475 | * Create a symlink for the snapshot. If a file exists with the same | |
| 476 | * name the new symlink will replace it. | |
| 477 | */ | |
| 83f2a3aa MD |
478 | if (fsym && tsym) { |
| 479 | remove(tsym); | |
| 480 | if (symlink(fsym, tsym) < 0) { | |
| 481 | err(2, "Unable to create symlink %s", tsym); | |
| 482 | } | |
| 483 | } | |
| 484 | } | |
| 485 | ||
| 486 | static | |
| 487 | void | |
| 488 | snapshot_ls(const char *path) | |
| 489 | { | |
| 490 | /*struct hammer_ioc_version version;*/ | |
| 2c1d3cef | 491 | struct hammer_ioc_info info; |
| 83f2a3aa MD |
492 | struct hammer_ioc_snapshot snapshot; |
| 493 | struct hammer_ioc_pseudofs_rw pfs; | |
| 2c1d3cef | 494 | struct hammer_pseudofs_data pfs_od; |
| 83f2a3aa MD |
495 | struct hammer_snapshot_data *snap; |
| 496 | struct tm *tp; | |
| 497 | time_t t; | |
| 498 | u_int32_t i; | |
| 2c1d3cef | 499 | int fd, ismaster; |
| 83f2a3aa | 500 | char snapts[64]; |
| 2c1d3cef | 501 | char *mntpoint; |
| 83f2a3aa MD |
502 | |
| 503 | fd = open(path, O_RDONLY); | |
| 504 | if (fd < 0) { | |
| 505 | err(2, "hammer snapls: cannot open %s", path); | |
| 506 | /* not reached */ | |
| 507 | } | |
| 508 | ||
| 509 | bzero(&pfs, sizeof(pfs)); | |
| 2c1d3cef | 510 | bzero(&pfs_od, sizeof(pfs_od)); |
| 83f2a3aa | 511 | pfs.pfs_id = -1; |
| 2c1d3cef | 512 | pfs.ondisk = &pfs_od; |
| 83f2a3aa MD |
513 | pfs.bytes = sizeof(struct hammer_pseudofs_data); |
| 514 | if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) < 0) { | |
| 515 | err(2, "hammer snapls: cannot retrieve PFS info on %s", path); | |
| 516 | /* not reached */ | |
| 517 | } | |
| 518 | ||
| 2c1d3cef SK |
519 | bzero(&info, sizeof(info)); |
| 520 | if ((ioctl(fd, HAMMERIOC_GET_INFO, &info)) < 0) { | |
| 521 | err(2, "hammer snapls: cannot retrieve HAMMER info"); | |
| 522 | /* not reached */ | |
| 523 | } | |
| 524 | ||
| 525 | ismaster = (pfs_od.mirror_flags & HAMMER_PFSD_SLAVE) ? 0 : 1; | |
| 4f09feab | 526 | mntpoint = libhammer_find_pfs_mount(pfs.pfs_id, info.vol_fsid, ismaster); |
| 2c1d3cef | 527 | |
| f2b2e6b4 SK |
528 | /* Note the degenerate case of PFS #0 */ |
| 529 | printf("Snapshots on %s\tPFS #%d\n", | |
| 530 | pfs.pfs_id == 0 ? path : mntpoint, pfs.pfs_id); | |
| 2c1d3cef | 531 | printf("Transaction ID\t\tTimestamp\t\tNote\n"); |
| 83f2a3aa | 532 | |
| f2b2e6b4 SK |
533 | if (mntpoint) |
| 534 | free(mntpoint); | |
| 535 | ||
| 83f2a3aa MD |
536 | bzero(&snapshot, sizeof(snapshot)); |
| 537 | do { | |
| 538 | if (ioctl(fd, HAMMERIOC_GET_SNAPSHOT, &snapshot) < 0) { | |
| 539 | err(2, "hammer snapls: %s: not HAMMER fs or " | |
| 540 | "version < 3", path); | |
| 541 | /* not reached */ | |
| 542 | } | |
| 543 | for (i = 0; i < snapshot.count; ++i) { | |
| 544 | snap = &snapshot.snaps[i]; | |
| 545 | ||
| 546 | t = snap->ts / 1000000ULL; | |
| 547 | tp = localtime(&t); | |
| 548 | strftime(snapts, sizeof(snapts), | |
| 549 | "%Y-%m-%d %H:%M:%S %Z", tp); | |
| 550 | printf("0x%016jx\t%s\t%s\n", | |
| 2c1d3cef SK |
551 | (uintmax_t)snap->tid, snapts, |
| 552 | strlen(snap->label) ? snap->label : "-"); | |
| 83f2a3aa MD |
553 | } |
| 554 | } while (snapshot.head.error == 0 && snapshot.count); | |
| 555 | } | |
| 556 | ||
| 557 | static | |
| 558 | void | |
| 559 | snapshot_del(int fsfd, hammer_tid_t tid) | |
| 560 | { | |
| 561 | struct hammer_ioc_snapshot snapshot; | |
| 562 | struct hammer_ioc_version version; | |
| 563 | ||
| 564 | bzero(&version, sizeof(version)); | |
| 565 | ||
| 566 | if (ioctl(fsfd, HAMMERIOC_GET_VERSION, &version) < 0) { | |
| 567 | err(2, "hammer snaprm 0x%016jx", (uintmax_t)tid); | |
| 568 | } | |
| 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 | } | |
| 573 | ||
| 574 | bzero(&snapshot, sizeof(snapshot)); | |
| 575 | snapshot.count = 1; | |
| 576 | snapshot.snaps[0].tid = tid; | |
| 577 | ||
| 92ed14a3 MD |
578 | /* |
| 579 | * Do not abort if we are unable to remove the meta-data. | |
| 580 | */ | |
| 83f2a3aa | 581 | if (ioctl(fsfd, HAMMERIOC_DEL_SNAPSHOT, &snapshot) < 0) { |
| 92ed14a3 MD |
582 | err(2, "hammer snaprm 0x%016jx", |
| 583 | (uintmax_t)tid); | |
| 584 | } else if (snapshot.head.error == ENOENT) { | |
| 585 | fprintf(stderr, "Warning: hammer snaprm 0x%016jx: " | |
| 586 | "meta-data not found\n", | |
| 587 | (uintmax_t)tid); | |
| 83f2a3aa | 588 | } else if (snapshot.head.error) { |
| 92ed14a3 | 589 | fprintf(stderr, "Warning: hammer snaprm 0x%016jx: %s\n", |
| 83f2a3aa MD |
590 | (uintmax_t)tid, strerror(snapshot.head.error)); |
| 591 | } | |
| 592 | } | |
| 593 | ||
| 594 | static | |
| 595 | void | |
| 6b669ab4 MN |
596 | snapshot_usage(int exit_code) |
| 597 | { | |
| 83f2a3aa | 598 | fprintf(stderr, |
| aaf93065 | 599 | "hammer snap <path> [<note>]\t\tcreate snapshot & link, points to\n" |
| 16265794 | 600 | "\t\t\t\t\tbase of PFS mount\n" |
| aaf93065 | 601 | "hammer snaplo <path> [<note>]\t\tcreate snapshot & link, points to\n" |
| 16265794 TN |
602 | "\t\t\t\t\ttarget dir\n" |
| 603 | "hammer snapq <dir> [<note>]\t\tcreate snapshot, output path to stdout\n" | |
| aaf93065 TN |
604 | "hammer snaprm <path> ...\t\tdelete snapshots; filesystem is CWD\n" |
| 605 | "hammer snaprm <transid> ...\t\tdelete snapshots\n" | |
| 606 | "hammer snaprm <filesystem> <transid> ...\tdelete snapshots\n" | |
| 16265794 | 607 | "hammer snapls [<path> ...]\t\tlist available snapshots\n" |
| 83f2a3aa MD |
608 | "\n" |
| 609 | "NOTE: Snapshots are created in filesystem meta-data, any directory\n" | |
| 610 | " in a HAMMER filesystem or PFS may be specified. If the path\n" | |
| 16265794 | 611 | " specified does not exist this function will also create a\n" |
| 83f2a3aa MD |
612 | " softlink.\n" |
| 613 | "\n" | |
| 614 | " When deleting snapshots transaction ids may be directly specified\n" | |
| 16265794 TN |
615 | " or file paths to snapshot softlinks may be specified. If a\n" |
| 616 | " softlink is specified the softlink will also be deleted.\n" | |
| 83f2a3aa MD |
617 | "\n" |
| 618 | "NOTE: The old 'hammer snapshot [<filesystem>] <snapshot-dir>' form\n" | |
| 619 | " is still accepted but is a deprecated form. This form will\n" | |
| 620 | " work for older hammer versions. The new forms only work for\n" | |
| 16265794 TN |
621 | " HAMMER version 3 or later filesystems. HAMMER can be upgraded\n" |
| 622 | " to version 3 in-place.\n" | |
| 83f2a3aa | 623 | ); |
| 6b669ab4 MN |
624 | exit(exit_code); |
| 625 | } | |
| 83f2a3aa MD |
626 | |
| 627 | static | |
| 628 | char * | |
| 629 | dirpart(const char *path) | |
| 630 | { | |
| 631 | const char *ptr; | |
| 632 | char *res; | |
| 633 | ||
| 634 | ptr = strrchr(path, '/'); | |
| 635 | if (ptr) { | |
| 636 | while (ptr > path && ptr[-1] == '/') | |
| 637 | --ptr; | |
| 638 | if (ptr == path) | |
| 639 | ptr = NULL; | |
| 640 | } | |
| 641 | if (ptr == NULL) { | |
| 642 | path = "."; | |
| 643 | ptr = path + 1; | |
| 644 | } | |
| 645 | res = malloc(ptr - path + 1); | |
| 646 | bcopy(path, res, ptr - path); | |
| 647 | res[ptr - path] = 0; | |
| 648 | return(res); | |
| 649 | } |