16b669ab4SMichael Neumann /* 26b669ab4SMichael Neumann * Copyright (c) 2008 The DragonFly Project. All rights reserved. 36b669ab4SMichael Neumann * 46b669ab4SMichael Neumann * This code is derived from software contributed to The DragonFly Project 56b669ab4SMichael Neumann * by Matthew Dillon <dillon@backplane.com> 66b669ab4SMichael Neumann * 76b669ab4SMichael Neumann * Redistribution and use in source and binary forms, with or without 86b669ab4SMichael Neumann * modification, are permitted provided that the following conditions 96b669ab4SMichael Neumann * are met: 106b669ab4SMichael Neumann * 116b669ab4SMichael Neumann * 1. Redistributions of source code must retain the above copyright 126b669ab4SMichael Neumann * notice, this list of conditions and the following disclaimer. 136b669ab4SMichael Neumann * 2. Redistributions in binary form must reproduce the above copyright 146b669ab4SMichael Neumann * notice, this list of conditions and the following disclaimer in 156b669ab4SMichael Neumann * the documentation and/or other materials provided with the 166b669ab4SMichael Neumann * distribution. 176b669ab4SMichael Neumann * 3. Neither the name of The DragonFly Project nor the names of its 186b669ab4SMichael Neumann * contributors may be used to endorse or promote products derived 196b669ab4SMichael Neumann * from this software without specific, prior written permission. 206b669ab4SMichael Neumann * 216b669ab4SMichael Neumann * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 226b669ab4SMichael Neumann * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 236b669ab4SMichael Neumann * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 246b669ab4SMichael Neumann * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 256b669ab4SMichael Neumann * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 266b669ab4SMichael Neumann * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 276b669ab4SMichael Neumann * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 286b669ab4SMichael Neumann * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 296b669ab4SMichael Neumann * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 306b669ab4SMichael Neumann * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 316b669ab4SMichael Neumann * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 326b669ab4SMichael Neumann * SUCH DAMAGE. 336b669ab4SMichael Neumann * 34e0af86f0SMichael Neumann * $DragonFly: src/sbin/hammer/cmd_snapshot.c,v 1.7 2008/07/10 18:47:22 mneumann Exp $ 356b669ab4SMichael Neumann */ 366b669ab4SMichael Neumann 37f8052532SMichael Neumann #include <time.h> 386b669ab4SMichael Neumann 39*b45803e3STomohiro Kusumi #include "hammer.h" 40*b45803e3STomohiro Kusumi 4196e6b862SMichael Neumann #define DEFAULT_SNAPSHOT_NAME "snap-%Y%m%d-%H%M" 4296e6b862SMichael Neumann 436b669ab4SMichael Neumann static void snapshot_usage(int exit_code); 4483f2a3aaSMatthew Dillon static void snapshot_add(int fd, const char *fsym, const char *tsym, 4583f2a3aaSMatthew Dillon const char *label, hammer_tid_t tid); 4683f2a3aaSMatthew Dillon static void snapshot_ls(const char *path); 4783f2a3aaSMatthew Dillon static void snapshot_del(int fsfd, hammer_tid_t tid); 4883f2a3aaSMatthew Dillon static char *dirpart(const char *path); 4983f2a3aaSMatthew Dillon 5083f2a3aaSMatthew Dillon /* 5116265794SThomas Nikolajsen * hammer snap <path> [<note>] 5283f2a3aaSMatthew Dillon * 5316265794SThomas Nikolajsen * Path may be a directory, softlink, or non-existent (a softlink will be 5483f2a3aaSMatthew Dillon * created). 5583f2a3aaSMatthew Dillon */ 5683f2a3aaSMatthew Dillon void 5783f2a3aaSMatthew Dillon hammer_cmd_snap(char **av, int ac, int tostdout, int fsbase) 5883f2a3aaSMatthew Dillon { 5983f2a3aaSMatthew Dillon struct hammer_ioc_synctid synctid; 6083f2a3aaSMatthew Dillon struct hammer_ioc_version version; 6183f2a3aaSMatthew Dillon char *dirpath; 62f111635eSTomohiro Kusumi char *fsym = NULL; 63f111635eSTomohiro Kusumi char *tsym = NULL; 6483f2a3aaSMatthew Dillon struct stat st; 6583f2a3aaSMatthew Dillon char note[64]; 6683f2a3aaSMatthew Dillon int fsfd; 6783f2a3aaSMatthew Dillon 6883f2a3aaSMatthew Dillon if (ac == 0 || ac > 2) { 6983f2a3aaSMatthew Dillon snapshot_usage(1); 7083f2a3aaSMatthew Dillon /* not reached */ 7183f2a3aaSMatthew Dillon } 7283f2a3aaSMatthew Dillon 7383f2a3aaSMatthew Dillon if (ac == 2) 7483f2a3aaSMatthew Dillon snprintf(note, sizeof(note), "%s", av[1]); 7583f2a3aaSMatthew Dillon else 7683f2a3aaSMatthew Dillon note[0] = 0; 7783f2a3aaSMatthew Dillon 7883f2a3aaSMatthew Dillon /* 7983f2a3aaSMatthew Dillon * Figure out the softlink path and directory path 8083f2a3aaSMatthew Dillon */ 8183f2a3aaSMatthew Dillon if (stat(av[0], &st) < 0) { 8283f2a3aaSMatthew Dillon dirpath = dirpart(av[0]); 83f111635eSTomohiro Kusumi tsym = strdup(av[0]); 8483f2a3aaSMatthew Dillon } else if (S_ISDIR(st.st_mode)) { 8583f2a3aaSMatthew Dillon time_t t = time(NULL); 8683f2a3aaSMatthew Dillon struct tm *tp; 8783f2a3aaSMatthew Dillon char extbuf[64]; 8883f2a3aaSMatthew Dillon 8983f2a3aaSMatthew Dillon tp = localtime(&t); 9083f2a3aaSMatthew Dillon strftime(extbuf, sizeof(extbuf), DEFAULT_SNAPSHOT_NAME, tp); 9183f2a3aaSMatthew Dillon 9283f2a3aaSMatthew Dillon dirpath = strdup(av[0]); 9383f2a3aaSMatthew Dillon asprintf(&tsym, "%s/%s", dirpath, extbuf); 9483f2a3aaSMatthew Dillon } else { 9564df1e74STomohiro Kusumi err(2, "hammer snap: File %s exists and is not a directory\n", 9683f2a3aaSMatthew Dillon av[0]); 9783f2a3aaSMatthew Dillon /* not reached */ 9883f2a3aaSMatthew Dillon } 9983f2a3aaSMatthew Dillon 10083f2a3aaSMatthew Dillon /* 10183f2a3aaSMatthew Dillon * Get a handle on some directory in the filesystem for the 10283f2a3aaSMatthew Dillon * ioctl (so it is stored in the correct PFS). 10383f2a3aaSMatthew Dillon */ 10483f2a3aaSMatthew Dillon fsfd = open(dirpath, O_RDONLY); 10583f2a3aaSMatthew Dillon if (fsfd < 0) { 10616265794SThomas Nikolajsen err(2, "hammer snap: Cannot open directory %s\n", dirpath); 10783f2a3aaSMatthew Dillon /* not reached */ 10883f2a3aaSMatthew Dillon } 10983f2a3aaSMatthew Dillon 11083f2a3aaSMatthew Dillon /* 11183f2a3aaSMatthew Dillon * Must be at least version 3 to use this command. 11283f2a3aaSMatthew Dillon */ 11383f2a3aaSMatthew Dillon bzero(&version, sizeof(version)); 11483f2a3aaSMatthew Dillon 11583f2a3aaSMatthew Dillon if (ioctl(fsfd, HAMMERIOC_GET_VERSION, &version) < 0) { 11683f2a3aaSMatthew Dillon err(2, "Unable to create snapshot"); 11783f2a3aaSMatthew Dillon /* not reached */ 11883f2a3aaSMatthew Dillon } else if (version.cur_version < 3) { 11983f2a3aaSMatthew Dillon errx(2, "Unable to create snapshot: This directive requires " 12016265794SThomas Nikolajsen "you to upgrade\n" 12116265794SThomas Nikolajsen "the filesystem to version 3. " 12216265794SThomas Nikolajsen "Use 'hammer snapshot' for legacy operation."); 12383f2a3aaSMatthew Dillon /* not reached */ 12483f2a3aaSMatthew Dillon } 12583f2a3aaSMatthew Dillon 12683f2a3aaSMatthew Dillon /* 12783f2a3aaSMatthew Dillon * Synctid to get a transaction id for the snapshot. 12883f2a3aaSMatthew Dillon */ 12983f2a3aaSMatthew Dillon bzero(&synctid, sizeof(synctid)); 13083f2a3aaSMatthew Dillon synctid.op = HAMMER_SYNCTID_SYNC2; 13183f2a3aaSMatthew Dillon if (ioctl(fsfd, HAMMERIOC_SYNCTID, &synctid) < 0) { 13283f2a3aaSMatthew Dillon err(2, "hammer snap: Synctid %s failed", 13383f2a3aaSMatthew Dillon dirpath); 13483f2a3aaSMatthew Dillon } 13583f2a3aaSMatthew Dillon if (tostdout) { 136b78f1a00SMatthew Dillon if (strcmp(dirpath, ".") == 0 || strcmp(dirpath, "..") == 0) { 137b78f1a00SMatthew Dillon printf("%s/@@0x%016jx\n", 138b78f1a00SMatthew Dillon dirpath, (uintmax_t)synctid.tid); 139b78f1a00SMatthew Dillon } else { 140b78f1a00SMatthew Dillon printf("%s@@0x%016jx\n", 141b78f1a00SMatthew Dillon dirpath, (uintmax_t)synctid.tid); 142b78f1a00SMatthew Dillon } 14383f2a3aaSMatthew Dillon fsym = NULL; 14483f2a3aaSMatthew Dillon tsym = NULL; 14583f2a3aaSMatthew Dillon } 14683f2a3aaSMatthew Dillon 14783f2a3aaSMatthew Dillon /* 14883f2a3aaSMatthew Dillon * Contents of the symlink being created. 14983f2a3aaSMatthew Dillon */ 15083f2a3aaSMatthew Dillon if (fsbase) { 15183f2a3aaSMatthew Dillon struct statfs buf; 15283f2a3aaSMatthew Dillon 15383f2a3aaSMatthew Dillon if (statfs(dirpath, &buf) < 0) { 15416265794SThomas Nikolajsen err(2, "hammer snap: Cannot determine mount for %s", 15583f2a3aaSMatthew Dillon dirpath); 15683f2a3aaSMatthew Dillon } 15783f2a3aaSMatthew Dillon asprintf(&fsym, "%s/@@0x%016jx", 15883f2a3aaSMatthew Dillon buf.f_mntonname, (uintmax_t)synctid.tid); 159b78f1a00SMatthew Dillon } else if (strcmp(dirpath, ".") == 0 || strcmp(dirpath, "..") == 0) { 16083f2a3aaSMatthew Dillon asprintf(&fsym, "%s/@@0x%016jx", 16183f2a3aaSMatthew Dillon dirpath, (uintmax_t)synctid.tid); 162b78f1a00SMatthew Dillon } else { 163b78f1a00SMatthew Dillon asprintf(&fsym, "%s@@0x%016jx", 164b78f1a00SMatthew Dillon dirpath, (uintmax_t)synctid.tid); 16583f2a3aaSMatthew Dillon } 16683f2a3aaSMatthew Dillon 16783f2a3aaSMatthew Dillon /* 16883f2a3aaSMatthew Dillon * Create the snapshot. 16983f2a3aaSMatthew Dillon */ 17083f2a3aaSMatthew Dillon snapshot_add(fsfd, fsym, tsym, note, synctid.tid); 17183f2a3aaSMatthew Dillon free(dirpath); 172f111635eSTomohiro Kusumi free(fsym); 173f111635eSTomohiro Kusumi free(tsym); 17483f2a3aaSMatthew Dillon } 17583f2a3aaSMatthew Dillon 17683f2a3aaSMatthew Dillon /* 17716265794SThomas Nikolajsen * hammer snapls [<path> ...] 17883f2a3aaSMatthew Dillon * 17983f2a3aaSMatthew Dillon * If no arguments are specified snapshots for the PFS containing the 18083f2a3aaSMatthew Dillon * current directory are listed. 18183f2a3aaSMatthew Dillon */ 18283f2a3aaSMatthew Dillon void 18383f2a3aaSMatthew Dillon hammer_cmd_snapls(char **av, int ac) 18483f2a3aaSMatthew Dillon { 18583f2a3aaSMatthew Dillon int i; 18683f2a3aaSMatthew Dillon 18783f2a3aaSMatthew Dillon for (i = 0; i < ac; ++i) 18883f2a3aaSMatthew Dillon snapshot_ls(av[i]); 18983f2a3aaSMatthew Dillon if (ac == 0) 19083f2a3aaSMatthew Dillon snapshot_ls("."); 19183f2a3aaSMatthew Dillon } 19283f2a3aaSMatthew Dillon 19383f2a3aaSMatthew Dillon /* 194aaf93065SThomas Nikolajsen * hammer snaprm <path> ... 195aaf93065SThomas Nikolajsen * hammer snaprm <transid> ... 196aaf93065SThomas Nikolajsen * hammer snaprm <filesystem> <transid> ... 19783f2a3aaSMatthew Dillon */ 19883f2a3aaSMatthew Dillon void 19983f2a3aaSMatthew Dillon hammer_cmd_snaprm(char **av, int ac) 20083f2a3aaSMatthew Dillon { 20183f2a3aaSMatthew Dillon struct stat st; 20283f2a3aaSMatthew Dillon char linkbuf[1024]; 20383f2a3aaSMatthew Dillon intmax_t tid; 20483f2a3aaSMatthew Dillon int fsfd = -1; 20583f2a3aaSMatthew Dillon int i; 206aa6862a4SThomas Nikolajsen int delete; 207aa6862a4SThomas Nikolajsen enum snaprm_mode { none_m, path_m, tid_m } mode = none_m; 20883f2a3aaSMatthew Dillon char *dirpath; 209aa6862a4SThomas Nikolajsen char *ptr, *ptr2; 210aa6862a4SThomas Nikolajsen 211aa6862a4SThomas Nikolajsen if (ac == 0) { 212aa6862a4SThomas Nikolajsen snapshot_usage(1); 213aa6862a4SThomas Nikolajsen /* not reached */ 214aa6862a4SThomas Nikolajsen } 21583f2a3aaSMatthew Dillon 21683f2a3aaSMatthew Dillon for (i = 0; i < ac; ++i) { 21783f2a3aaSMatthew Dillon if (lstat(av[i], &st) < 0) { 21883f2a3aaSMatthew Dillon tid = strtoull(av[i], &ptr, 16); 219662838f8SMatthew Dillon if (*ptr) { 22083f2a3aaSMatthew Dillon err(2, "hammer snaprm: not a file or tid: %s", 22183f2a3aaSMatthew Dillon av[i]); 22283f2a3aaSMatthew Dillon /* not reached */ 22383f2a3aaSMatthew Dillon } 224aa6862a4SThomas Nikolajsen if (mode == path_m) { 225aa6862a4SThomas Nikolajsen snapshot_usage(1); 226aa6862a4SThomas Nikolajsen /* not reached */ 227aa6862a4SThomas Nikolajsen } 228aa6862a4SThomas Nikolajsen mode = tid_m; 22992ed14a3SMatthew Dillon if (fsfd < 0) 23092ed14a3SMatthew Dillon fsfd = open(".", O_RDONLY); 23183f2a3aaSMatthew Dillon snapshot_del(fsfd, tid); 23283f2a3aaSMatthew Dillon } else if (S_ISDIR(st.st_mode)) { 233aa6862a4SThomas Nikolajsen if (i != 0 || ac < 2) { 234aa6862a4SThomas Nikolajsen snapshot_usage(1); 235aa6862a4SThomas Nikolajsen /* not reached */ 236aa6862a4SThomas Nikolajsen } 23783f2a3aaSMatthew Dillon if (fsfd >= 0) 23883f2a3aaSMatthew Dillon close(fsfd); 23983f2a3aaSMatthew Dillon fsfd = open(av[i], O_RDONLY); 24083f2a3aaSMatthew Dillon if (fsfd < 0) { 24183f2a3aaSMatthew Dillon err(2, "hammer snaprm: cannot open dir %s", 24283f2a3aaSMatthew Dillon av[i]); 24383f2a3aaSMatthew Dillon /* not reached */ 24483f2a3aaSMatthew Dillon } 245aa6862a4SThomas Nikolajsen mode = tid_m; 24683f2a3aaSMatthew Dillon } else if (S_ISLNK(st.st_mode)) { 24783f2a3aaSMatthew Dillon dirpath = dirpart(av[i]); 248bf2c6489SMatthew Dillon bzero(linkbuf, sizeof(linkbuf)); 249bf2c6489SMatthew Dillon if (readlink(av[i], linkbuf, sizeof(linkbuf) - 1) < 0) { 250bf2c6489SMatthew Dillon err(2, "hammer snaprm: cannot read softlink: " 251bf2c6489SMatthew Dillon "%s", av[i]); 252bf2c6489SMatthew Dillon /* not reached */ 253bf2c6489SMatthew Dillon } 254bf2c6489SMatthew Dillon if (linkbuf[0] == '/') { 255bf2c6489SMatthew Dillon free(dirpath); 256bf2c6489SMatthew Dillon dirpath = dirpart(linkbuf); 257bf2c6489SMatthew Dillon } else { 258bf2c6489SMatthew Dillon asprintf(&ptr, "%s/%s", dirpath, linkbuf); 259bf2c6489SMatthew Dillon free(dirpath); 260bf2c6489SMatthew Dillon dirpath = dirpart(ptr); 261f111635eSTomohiro Kusumi free(ptr); 262bf2c6489SMatthew Dillon } 263bf2c6489SMatthew Dillon 26483f2a3aaSMatthew Dillon if (fsfd >= 0) 26583f2a3aaSMatthew Dillon close(fsfd); 26683f2a3aaSMatthew Dillon fsfd = open(dirpath, O_RDONLY); 26783f2a3aaSMatthew Dillon if (fsfd < 0) { 26883f2a3aaSMatthew Dillon err(2, "hammer snaprm: cannot open dir %s", 26983f2a3aaSMatthew Dillon dirpath); 27083f2a3aaSMatthew Dillon /* not reached */ 27183f2a3aaSMatthew Dillon } 27283f2a3aaSMatthew Dillon 273aa6862a4SThomas Nikolajsen delete = 1; 274aa6862a4SThomas Nikolajsen if (i == 0 && ac > 1) { 275aa6862a4SThomas Nikolajsen mode = path_m; 276aa6862a4SThomas Nikolajsen if (lstat(av[1], &st) < 0) { 277aa6862a4SThomas Nikolajsen tid = strtoull(av[1], &ptr, 16); 278aa6862a4SThomas Nikolajsen if (*ptr == '\0') { 279aa6862a4SThomas Nikolajsen delete = 0; 280aa6862a4SThomas Nikolajsen mode = tid_m; 28183f2a3aaSMatthew Dillon } 282aa6862a4SThomas Nikolajsen } 283aa6862a4SThomas Nikolajsen } else { 284aa6862a4SThomas Nikolajsen if (mode == tid_m) { 285aa6862a4SThomas Nikolajsen snapshot_usage(1); 286aa6862a4SThomas Nikolajsen /* not reached */ 287aa6862a4SThomas Nikolajsen } 288aa6862a4SThomas Nikolajsen mode = path_m; 289aa6862a4SThomas Nikolajsen } 290aa6862a4SThomas Nikolajsen if (delete && (ptr = strrchr(linkbuf, '@')) && 291a6d893d9SThomas Nikolajsen ptr > linkbuf && ptr[-1] == '@' && ptr[1]) { 292aa6862a4SThomas Nikolajsen tid = strtoull(ptr + 1, &ptr2, 16); 293aa6862a4SThomas Nikolajsen if (*ptr2 == '\0') { 294aa6862a4SThomas Nikolajsen snapshot_del(fsfd, tid); 29583f2a3aaSMatthew Dillon remove(av[i]); 296aa6862a4SThomas Nikolajsen } 297aa6862a4SThomas Nikolajsen } 29883f2a3aaSMatthew Dillon free(dirpath); 29983f2a3aaSMatthew Dillon } else { 30083f2a3aaSMatthew Dillon err(2, "hammer snaprm: not directory or snapshot " 30183f2a3aaSMatthew Dillon "softlink: %s", av[i]); 30283f2a3aaSMatthew Dillon /* not reached */ 30383f2a3aaSMatthew Dillon } 30483f2a3aaSMatthew Dillon } 30583f2a3aaSMatthew Dillon if (fsfd >= 0) 30683f2a3aaSMatthew Dillon close(fsfd); 30783f2a3aaSMatthew Dillon } 3086b669ab4SMichael Neumann 3096b669ab4SMichael Neumann /* 31016265794SThomas Nikolajsen * snapshot <softlink-dir> 31116265794SThomas Nikolajsen * snapshot <filesystem> <softlink-dir> [<note>] 3126b669ab4SMichael Neumann */ 3136b669ab4SMichael Neumann void 3146b669ab4SMichael Neumann hammer_cmd_snapshot(char **av, int ac) 3156b669ab4SMichael Neumann { 3166b669ab4SMichael Neumann const char *filesystem; 31796e6b862SMichael Neumann const char *softlink_dir; 31896e6b862SMichael Neumann char *softlink_fmt; 3196b669ab4SMichael Neumann struct statfs buf; 32096e6b862SMichael Neumann struct stat st; 3216b669ab4SMichael Neumann struct hammer_ioc_synctid synctid; 3226b669ab4SMichael Neumann char *from; 3236b669ab4SMichael Neumann char *to; 324b5ec5ad4SMatthew Dillon char *note = NULL; 3256b669ab4SMichael Neumann 326f8052532SMichael Neumann if (ac == 1) { 327f8052532SMichael Neumann filesystem = NULL; 32896e6b862SMichael Neumann softlink_dir = av[0]; 3293267eb8aSSascha Wildner } else if (ac == 2) { 330f8052532SMichael Neumann filesystem = av[0]; 33196e6b862SMichael Neumann softlink_dir = av[1]; 332b5ec5ad4SMatthew Dillon } else if (ac == 3) { 333b5ec5ad4SMatthew Dillon filesystem = av[0]; 334b5ec5ad4SMatthew Dillon softlink_dir = av[1]; 335b5ec5ad4SMatthew Dillon note = av[2]; 3363267eb8aSSascha Wildner } else { 3376b669ab4SMichael Neumann snapshot_usage(1); 33883f2a3aaSMatthew Dillon /* not reached */ 33983f2a3aaSMatthew Dillon softlink_dir = NULL; 34083f2a3aaSMatthew Dillon filesystem = NULL; 341f8052532SMichael Neumann } 3426b669ab4SMichael Neumann 34396e6b862SMichael Neumann if (stat(softlink_dir, &st) == 0) { 34496e6b862SMichael Neumann if (!S_ISDIR(st.st_mode)) 34596e6b862SMichael Neumann err(2, "File %s already exists", softlink_dir); 3466b669ab4SMichael Neumann 34796e6b862SMichael Neumann if (filesystem == NULL) { 348f8052532SMichael Neumann if (statfs(softlink_dir, &buf) != 0) { 349f8052532SMichael Neumann err(2, "Unable to determine filesystem of %s", 350f8052532SMichael Neumann softlink_dir); 351f8052532SMichael Neumann } 3526b669ab4SMichael Neumann filesystem = buf.f_mntonname; 35396e6b862SMichael Neumann } 35496e6b862SMichael Neumann 35596e6b862SMichael Neumann softlink_fmt = malloc(strlen(softlink_dir) + 1 + 1 + 35696e6b862SMichael Neumann sizeof(DEFAULT_SNAPSHOT_NAME)); 35796e6b862SMichael Neumann if (softlink_fmt == NULL) 35896e6b862SMichael Neumann err(2, "Failed to allocate string"); 35996e6b862SMichael Neumann 36096e6b862SMichael Neumann strcpy(softlink_fmt, softlink_dir); 36196e6b862SMichael Neumann if (softlink_fmt[strlen(softlink_fmt)-1] != '/') 36296e6b862SMichael Neumann strcat(softlink_fmt, "/"); 36396e6b862SMichael Neumann strcat(softlink_fmt, DEFAULT_SNAPSHOT_NAME); 3643267eb8aSSascha Wildner } else { 36596e6b862SMichael Neumann softlink_fmt = strdup(softlink_dir); 36696e6b862SMichael Neumann 36796e6b862SMichael Neumann if (filesystem == NULL) { 36896e6b862SMichael Neumann /* 36996e6b862SMichael Neumann * strip-off last '/path' segment to get the softlink 37096e6b862SMichael Neumann * directory, which we need to determine the filesystem 37196e6b862SMichael Neumann * we are on. 37296e6b862SMichael Neumann */ 37396e6b862SMichael Neumann char *pos = strrchr(softlink_fmt, '/'); 37496e6b862SMichael Neumann if (pos != NULL) 37596e6b862SMichael Neumann *pos = '\0'; 37696e6b862SMichael Neumann 37796e6b862SMichael Neumann if (stat(softlink_fmt, &st) != 0 || 37896e6b862SMichael Neumann !S_ISDIR(st.st_mode)) { 37996e6b862SMichael Neumann err(2, "Unable to determine softlink dir %s", 38096e6b862SMichael Neumann softlink_fmt); 38196e6b862SMichael Neumann } 38296e6b862SMichael Neumann if (statfs(softlink_fmt, &buf) != 0) { 38396e6b862SMichael Neumann err(2, "Unable to determine filesystem of %s", 38496e6b862SMichael Neumann softlink_fmt); 38596e6b862SMichael Neumann } 38696e6b862SMichael Neumann filesystem = buf.f_mntonname; 38796e6b862SMichael Neumann 388e0af86f0SMichael Neumann /* restore '/' */ 38996e6b862SMichael Neumann if (pos != NULL) 39096e6b862SMichael Neumann *pos = '/'; 39196e6b862SMichael Neumann } 392a231693eSMichael Neumann } 393f8052532SMichael Neumann 394f8052532SMichael Neumann /* 395f8052532SMichael Neumann * Synctid 396f8052532SMichael Neumann */ 3976b669ab4SMichael Neumann bzero(&synctid, sizeof(synctid)); 3986b669ab4SMichael Neumann synctid.op = HAMMER_SYNCTID_SYNC2; 39983f2a3aaSMatthew Dillon 40096e6b862SMichael Neumann int fd = open(filesystem, O_RDONLY); 4016b669ab4SMichael Neumann if (fd < 0) 4026b669ab4SMichael Neumann err(2, "Unable to open %s", filesystem); 4036764f177SMichael Neumann if (ioctl(fd, HAMMERIOC_SYNCTID, &synctid) < 0) 4046b669ab4SMichael Neumann err(2, "Synctid %s failed", filesystem); 4056764f177SMichael Neumann 406a276dc6bSMatthew Dillon asprintf(&from, "%s/@@0x%016jx", filesystem, (uintmax_t)synctid.tid); 4076b669ab4SMichael Neumann if (from == NULL) 4086b669ab4SMichael Neumann err(2, "Couldn't generate string"); 4096b669ab4SMichael Neumann 410f8052532SMichael Neumann int sz = strlen(softlink_fmt) + 50; 411f8052532SMichael Neumann to = malloc(sz); 4126b669ab4SMichael Neumann if (to == NULL) 413f8052532SMichael Neumann err(2, "Failed to allocate string"); 414f8052532SMichael Neumann 415f8052532SMichael Neumann time_t t = time(NULL); 416f8052532SMichael Neumann if (strftime(to, sz, softlink_fmt, localtime(&t)) == 0) 417f8052532SMichael Neumann err(2, "String buffer too small"); 4186b669ab4SMichael Neumann 41983f2a3aaSMatthew Dillon asprintf(&from, "%s/@@0x%016jx", filesystem, (uintmax_t)synctid.tid); 4206b669ab4SMichael Neumann 421b5ec5ad4SMatthew Dillon snapshot_add(fd, from, to, note, synctid.tid); 42283f2a3aaSMatthew Dillon 42383f2a3aaSMatthew Dillon close(fd); 4246b669ab4SMichael Neumann printf("%s\n", to); 4256764f177SMichael Neumann 42696e6b862SMichael Neumann free(softlink_fmt); 4276764f177SMichael Neumann free(from); 4286764f177SMichael Neumann free(to); 4296b669ab4SMichael Neumann } 4306b669ab4SMichael Neumann 4316b669ab4SMichael Neumann static 4326b669ab4SMichael Neumann void 43383f2a3aaSMatthew Dillon snapshot_add(int fd, const char *fsym, const char *tsym, const char *label, 43483f2a3aaSMatthew Dillon hammer_tid_t tid) 43583f2a3aaSMatthew Dillon { 43683f2a3aaSMatthew Dillon struct hammer_ioc_version version; 43783f2a3aaSMatthew Dillon struct hammer_ioc_snapshot snapshot; 43883f2a3aaSMatthew Dillon 43983f2a3aaSMatthew Dillon bzero(&version, sizeof(version)); 44083f2a3aaSMatthew Dillon bzero(&snapshot, sizeof(snapshot)); 44183f2a3aaSMatthew Dillon 442b5ec5ad4SMatthew Dillon /* 443b5ec5ad4SMatthew Dillon * For HAMMER filesystem v3+ the snapshot is recorded in meta-data. 444b5ec5ad4SMatthew Dillon */ 44583f2a3aaSMatthew Dillon if (ioctl(fd, HAMMERIOC_GET_VERSION, &version) == 0 && 44683f2a3aaSMatthew Dillon version.cur_version >= 3) { 44783f2a3aaSMatthew Dillon snapshot.index = 0; 44883f2a3aaSMatthew Dillon snapshot.count = 1; 44983f2a3aaSMatthew Dillon snapshot.snaps[0].tid = tid; 45083f2a3aaSMatthew Dillon snapshot.snaps[0].ts = time(NULL) * 1000000ULL; 45183f2a3aaSMatthew Dillon if (label) { 45283f2a3aaSMatthew Dillon snprintf(snapshot.snaps[0].label, 45383f2a3aaSMatthew Dillon sizeof(snapshot.snaps[0].label), 45483f2a3aaSMatthew Dillon "%s", 45583f2a3aaSMatthew Dillon label); 45683f2a3aaSMatthew Dillon } 45783f2a3aaSMatthew Dillon if (ioctl(fd, HAMMERIOC_ADD_SNAPSHOT, &snapshot) < 0) { 45883f2a3aaSMatthew Dillon err(2, "Unable to create snapshot"); 45983f2a3aaSMatthew Dillon } else if (snapshot.head.error && 46083f2a3aaSMatthew Dillon snapshot.head.error != EEXIST) { 46183f2a3aaSMatthew Dillon errx(2, "Unable to create snapshot: %s\n", 46283f2a3aaSMatthew Dillon strerror(snapshot.head.error)); 46383f2a3aaSMatthew Dillon } 46483f2a3aaSMatthew Dillon } 465b5ec5ad4SMatthew Dillon 466b5ec5ad4SMatthew Dillon /* 467b5ec5ad4SMatthew Dillon * Create a symlink for the snapshot. If a file exists with the same 468b5ec5ad4SMatthew Dillon * name the new symlink will replace it. 469b5ec5ad4SMatthew Dillon */ 47083f2a3aaSMatthew Dillon if (fsym && tsym) { 47183f2a3aaSMatthew Dillon remove(tsym); 47283f2a3aaSMatthew Dillon if (symlink(fsym, tsym) < 0) { 47383f2a3aaSMatthew Dillon err(2, "Unable to create symlink %s", tsym); 47483f2a3aaSMatthew Dillon } 47583f2a3aaSMatthew Dillon } 47683f2a3aaSMatthew Dillon } 47783f2a3aaSMatthew Dillon 47883f2a3aaSMatthew Dillon static 47983f2a3aaSMatthew Dillon void 48083f2a3aaSMatthew Dillon snapshot_ls(const char *path) 48183f2a3aaSMatthew Dillon { 48283f2a3aaSMatthew Dillon /*struct hammer_ioc_version version;*/ 4832c1d3cefSStathis Kamperis struct hammer_ioc_info info; 48483f2a3aaSMatthew Dillon struct hammer_ioc_snapshot snapshot; 48583f2a3aaSMatthew Dillon struct hammer_ioc_pseudofs_rw pfs; 4862c1d3cefSStathis Kamperis struct hammer_pseudofs_data pfs_od; 48783f2a3aaSMatthew Dillon struct hammer_snapshot_data *snap; 48883f2a3aaSMatthew Dillon struct tm *tp; 48983f2a3aaSMatthew Dillon time_t t; 49083f2a3aaSMatthew Dillon u_int32_t i; 491d428efb7SAntonio Huete Jimenez int fd; 49283f2a3aaSMatthew Dillon char snapts[64]; 4932c1d3cefSStathis Kamperis char *mntpoint; 49483f2a3aaSMatthew Dillon 49583f2a3aaSMatthew Dillon fd = open(path, O_RDONLY); 49683f2a3aaSMatthew Dillon if (fd < 0) { 49783f2a3aaSMatthew Dillon err(2, "hammer snapls: cannot open %s", path); 49883f2a3aaSMatthew Dillon /* not reached */ 49983f2a3aaSMatthew Dillon } 50083f2a3aaSMatthew Dillon 50183f2a3aaSMatthew Dillon bzero(&pfs, sizeof(pfs)); 5022c1d3cefSStathis Kamperis bzero(&pfs_od, sizeof(pfs_od)); 50383f2a3aaSMatthew Dillon pfs.pfs_id = -1; 5042c1d3cefSStathis Kamperis pfs.ondisk = &pfs_od; 50583f2a3aaSMatthew Dillon pfs.bytes = sizeof(struct hammer_pseudofs_data); 50683f2a3aaSMatthew Dillon if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) < 0) { 50783f2a3aaSMatthew Dillon err(2, "hammer snapls: cannot retrieve PFS info on %s", path); 50883f2a3aaSMatthew Dillon /* not reached */ 50983f2a3aaSMatthew Dillon } 51083f2a3aaSMatthew Dillon 5112c1d3cefSStathis Kamperis bzero(&info, sizeof(info)); 5122c1d3cefSStathis Kamperis if ((ioctl(fd, HAMMERIOC_GET_INFO, &info)) < 0) { 5132c1d3cefSStathis Kamperis err(2, "hammer snapls: cannot retrieve HAMMER info"); 5142c1d3cefSStathis Kamperis /* not reached */ 5152c1d3cefSStathis Kamperis } 5162c1d3cefSStathis Kamperis 517d428efb7SAntonio Huete Jimenez mntpoint = libhammer_find_pfs_mount(&pfs.ondisk->unique_uuid); 5182c1d3cefSStathis Kamperis 519f2b2e6b4SStathis Kamperis printf("Snapshots on %s\tPFS #%d\n", 520d428efb7SAntonio Huete Jimenez mntpoint ? mntpoint : path, pfs.pfs_id); 5212c1d3cefSStathis Kamperis printf("Transaction ID\t\tTimestamp\t\tNote\n"); 52283f2a3aaSMatthew Dillon 523f2b2e6b4SStathis Kamperis if (mntpoint) 524f2b2e6b4SStathis Kamperis free(mntpoint); 525f2b2e6b4SStathis Kamperis 52683f2a3aaSMatthew Dillon bzero(&snapshot, sizeof(snapshot)); 52783f2a3aaSMatthew Dillon do { 52883f2a3aaSMatthew Dillon if (ioctl(fd, HAMMERIOC_GET_SNAPSHOT, &snapshot) < 0) { 52983f2a3aaSMatthew Dillon err(2, "hammer snapls: %s: not HAMMER fs or " 53083f2a3aaSMatthew Dillon "version < 3", path); 53183f2a3aaSMatthew Dillon /* not reached */ 53283f2a3aaSMatthew Dillon } 53383f2a3aaSMatthew Dillon for (i = 0; i < snapshot.count; ++i) { 53483f2a3aaSMatthew Dillon snap = &snapshot.snaps[i]; 53583f2a3aaSMatthew Dillon 53683f2a3aaSMatthew Dillon t = snap->ts / 1000000ULL; 53783f2a3aaSMatthew Dillon tp = localtime(&t); 53883f2a3aaSMatthew Dillon strftime(snapts, sizeof(snapts), 53983f2a3aaSMatthew Dillon "%Y-%m-%d %H:%M:%S %Z", tp); 54083f2a3aaSMatthew Dillon printf("0x%016jx\t%s\t%s\n", 5412c1d3cefSStathis Kamperis (uintmax_t)snap->tid, snapts, 5422c1d3cefSStathis Kamperis strlen(snap->label) ? snap->label : "-"); 54383f2a3aaSMatthew Dillon } 54483f2a3aaSMatthew Dillon } while (snapshot.head.error == 0 && snapshot.count); 54583f2a3aaSMatthew Dillon } 54683f2a3aaSMatthew Dillon 54783f2a3aaSMatthew Dillon static 54883f2a3aaSMatthew Dillon void 54983f2a3aaSMatthew Dillon snapshot_del(int fsfd, hammer_tid_t tid) 55083f2a3aaSMatthew Dillon { 55183f2a3aaSMatthew Dillon struct hammer_ioc_snapshot snapshot; 55283f2a3aaSMatthew Dillon struct hammer_ioc_version version; 55383f2a3aaSMatthew Dillon 55483f2a3aaSMatthew Dillon bzero(&version, sizeof(version)); 55583f2a3aaSMatthew Dillon 55683f2a3aaSMatthew Dillon if (ioctl(fsfd, HAMMERIOC_GET_VERSION, &version) < 0) { 55783f2a3aaSMatthew Dillon err(2, "hammer snaprm 0x%016jx", (uintmax_t)tid); 55883f2a3aaSMatthew Dillon } 55983f2a3aaSMatthew Dillon if (version.cur_version < 3) { 56083f2a3aaSMatthew Dillon errx(2, "hammer snaprm 0x%016jx: You must upgrade to version " 56183f2a3aaSMatthew Dillon " 3 to use this directive", (uintmax_t)tid); 56283f2a3aaSMatthew Dillon } 56383f2a3aaSMatthew Dillon 56483f2a3aaSMatthew Dillon bzero(&snapshot, sizeof(snapshot)); 56583f2a3aaSMatthew Dillon snapshot.count = 1; 56683f2a3aaSMatthew Dillon snapshot.snaps[0].tid = tid; 56783f2a3aaSMatthew Dillon 56892ed14a3SMatthew Dillon /* 56992ed14a3SMatthew Dillon * Do not abort if we are unable to remove the meta-data. 57092ed14a3SMatthew Dillon */ 57183f2a3aaSMatthew Dillon if (ioctl(fsfd, HAMMERIOC_DEL_SNAPSHOT, &snapshot) < 0) { 57292ed14a3SMatthew Dillon err(2, "hammer snaprm 0x%016jx", 57392ed14a3SMatthew Dillon (uintmax_t)tid); 57492ed14a3SMatthew Dillon } else if (snapshot.head.error == ENOENT) { 57592ed14a3SMatthew Dillon fprintf(stderr, "Warning: hammer snaprm 0x%016jx: " 57692ed14a3SMatthew Dillon "meta-data not found\n", 57792ed14a3SMatthew Dillon (uintmax_t)tid); 57883f2a3aaSMatthew Dillon } else if (snapshot.head.error) { 57992ed14a3SMatthew Dillon fprintf(stderr, "Warning: hammer snaprm 0x%016jx: %s\n", 58083f2a3aaSMatthew Dillon (uintmax_t)tid, strerror(snapshot.head.error)); 58183f2a3aaSMatthew Dillon } 58283f2a3aaSMatthew Dillon } 58383f2a3aaSMatthew Dillon 58483f2a3aaSMatthew Dillon static 58583f2a3aaSMatthew Dillon void 5866b669ab4SMichael Neumann snapshot_usage(int exit_code) 5876b669ab4SMichael Neumann { 58883f2a3aaSMatthew Dillon fprintf(stderr, 58916265794SThomas Nikolajsen "hammer snap <path> [<note>]\t\tcreate snapshot & link, points to\n" 59016265794SThomas Nikolajsen "\t\t\t\t\tbase of PFS mount\n" 59116265794SThomas Nikolajsen "hammer snaplo <path> [<note>]\t\tcreate snapshot & link, points to\n" 59216265794SThomas Nikolajsen "\t\t\t\t\ttarget dir\n" 59316265794SThomas Nikolajsen "hammer snapq <dir> [<note>]\t\tcreate snapshot, output path to stdout\n" 594aaf93065SThomas Nikolajsen "hammer snaprm <path> ...\t\tdelete snapshots; filesystem is CWD\n" 595aaf93065SThomas Nikolajsen "hammer snaprm <transid> ...\t\tdelete snapshots\n" 596aaf93065SThomas Nikolajsen "hammer snaprm <filesystem> <transid> ...\tdelete snapshots\n" 59716265794SThomas Nikolajsen "hammer snapls [<path> ...]\t\tlist available snapshots\n" 59883f2a3aaSMatthew Dillon "\n" 59983f2a3aaSMatthew Dillon "NOTE: Snapshots are created in filesystem meta-data, any directory\n" 60083f2a3aaSMatthew Dillon " in a HAMMER filesystem or PFS may be specified. If the path\n" 60183f2a3aaSMatthew Dillon " specified does not exist this function will also create a\n" 60283f2a3aaSMatthew Dillon " softlink.\n" 60383f2a3aaSMatthew Dillon "\n" 60483f2a3aaSMatthew Dillon " When deleting snapshots transaction ids may be directly specified\n" 60516265794SThomas Nikolajsen " or file paths to snapshot softlinks may be specified. If a\n" 60616265794SThomas Nikolajsen " softlink is specified the softlink will also be deleted.\n" 60783f2a3aaSMatthew Dillon "\n" 60883f2a3aaSMatthew Dillon "NOTE: The old 'hammer snapshot [<filesystem>] <snapshot-dir>' form\n" 60983f2a3aaSMatthew Dillon " is still accepted but is a deprecated form. This form will\n" 61083f2a3aaSMatthew Dillon " work for older hammer versions. The new forms only work for\n" 61183f2a3aaSMatthew Dillon " HAMMER version 3 or later filesystems. HAMMER can be upgraded\n" 61216265794SThomas Nikolajsen " to version 3 in-place.\n" 61383f2a3aaSMatthew Dillon ); 6146b669ab4SMichael Neumann exit(exit_code); 6156b669ab4SMichael Neumann } 61683f2a3aaSMatthew Dillon 61783f2a3aaSMatthew Dillon static 61883f2a3aaSMatthew Dillon char * 61983f2a3aaSMatthew Dillon dirpart(const char *path) 62083f2a3aaSMatthew Dillon { 62183f2a3aaSMatthew Dillon const char *ptr; 62283f2a3aaSMatthew Dillon char *res; 62383f2a3aaSMatthew Dillon 62483f2a3aaSMatthew Dillon ptr = strrchr(path, '/'); 62583f2a3aaSMatthew Dillon if (ptr) { 62683f2a3aaSMatthew Dillon while (ptr > path && ptr[-1] == '/') 62783f2a3aaSMatthew Dillon --ptr; 62883f2a3aaSMatthew Dillon if (ptr == path) 62983f2a3aaSMatthew Dillon ptr = NULL; 63083f2a3aaSMatthew Dillon } 63183f2a3aaSMatthew Dillon if (ptr == NULL) { 63283f2a3aaSMatthew Dillon path = "."; 63383f2a3aaSMatthew Dillon ptr = path + 1; 63483f2a3aaSMatthew Dillon } 63583f2a3aaSMatthew Dillon res = malloc(ptr - path + 1); 63683f2a3aaSMatthew Dillon bcopy(path, res, ptr - path); 63783f2a3aaSMatthew Dillon res[ptr - path] = 0; 63883f2a3aaSMatthew Dillon return(res); 63983f2a3aaSMatthew Dillon } 640