16a6e350fSMatthew Dillon /* 26a6e350fSMatthew Dillon * Copyright (c) 2008 The DragonFly Project. All rights reserved. 36a6e350fSMatthew Dillon * 46a6e350fSMatthew Dillon * This code is derived from software contributed to The DragonFly Project 56a6e350fSMatthew Dillon * by Matthew Dillon <dillon@backplane.com> 66a6e350fSMatthew Dillon * 76a6e350fSMatthew Dillon * Redistribution and use in source and binary forms, with or without 86a6e350fSMatthew Dillon * modification, are permitted provided that the following conditions 96a6e350fSMatthew Dillon * are met: 106a6e350fSMatthew Dillon * 116a6e350fSMatthew Dillon * 1. Redistributions of source code must retain the above copyright 126a6e350fSMatthew Dillon * notice, this list of conditions and the following disclaimer. 136a6e350fSMatthew Dillon * 2. Redistributions in binary form must reproduce the above copyright 146a6e350fSMatthew Dillon * notice, this list of conditions and the following disclaimer in 156a6e350fSMatthew Dillon * the documentation and/or other materials provided with the 166a6e350fSMatthew Dillon * distribution. 176a6e350fSMatthew Dillon * 3. Neither the name of The DragonFly Project nor the names of its 186a6e350fSMatthew Dillon * contributors may be used to endorse or promote products derived 196a6e350fSMatthew Dillon * from this software without specific, prior written permission. 206a6e350fSMatthew Dillon * 216a6e350fSMatthew Dillon * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 226a6e350fSMatthew Dillon * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 236a6e350fSMatthew Dillon * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 246a6e350fSMatthew Dillon * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 256a6e350fSMatthew Dillon * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 266a6e350fSMatthew Dillon * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 276a6e350fSMatthew Dillon * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 286a6e350fSMatthew Dillon * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 296a6e350fSMatthew Dillon * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 306a6e350fSMatthew Dillon * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 316a6e350fSMatthew Dillon * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 326a6e350fSMatthew Dillon * SUCH DAMAGE. 336a6e350fSMatthew Dillon * 34bb8e52c0SThomas Nikolajsen * $DragonFly: src/sbin/hammer/cmd_cleanup.c,v 1.6 2008/10/07 22:28:41 thomas Exp $ 356a6e350fSMatthew Dillon */ 366a6e350fSMatthew Dillon /* 3716265794SThomas Nikolajsen * Clean up specific HAMMER filesystems or all HAMMER filesystems. 386a6e350fSMatthew Dillon * 39f6532f03SThomas Nikolajsen * If no filesystems are specified any HAMMER- or null-mounted hammer PFS's 4016265794SThomas Nikolajsen * are cleaned. 416a6e350fSMatthew Dillon * 4216265794SThomas Nikolajsen * Each HAMMER filesystem may contain a configuration file. If no 4316265794SThomas Nikolajsen * configuration file is present one will be created with the following 446a6e350fSMatthew Dillon * defaults: 456a6e350fSMatthew Dillon * 465e435c92SMatthew Dillon * snapshots 1d 60d (0d 0d for /tmp, /var/tmp, /usr/obj) 476a6e350fSMatthew Dillon * prune 1d 5m 48f13fea8bSSascha Wildner * rebalance 1d 5m 496a6e350fSMatthew Dillon * reblock 1d 5m 50f6532f03SThomas Nikolajsen * recopy 30d 10m 516a6e350fSMatthew Dillon * 526a6e350fSMatthew Dillon * All hammer commands create and maintain cycle files in the snapshots 536a6e350fSMatthew Dillon * directory. 5416265794SThomas Nikolajsen * 5516265794SThomas Nikolajsen * For HAMMER version 2- the configuration file is a named 'config' in 5616265794SThomas Nikolajsen * the snapshots directory, which defaults to <pfs>/snapshots. 5716265794SThomas Nikolajsen * For HAMMER version 3+ the configuration file is saved in filesystem 5816265794SThomas Nikolajsen * meta-data. The snapshots directory defaults to /var/hammer/<pfs> 5916265794SThomas Nikolajsen * (/var/hammer/root for root mount). 606a6e350fSMatthew Dillon */ 616a6e350fSMatthew Dillon 626a6e350fSMatthew Dillon #include "hammer.h" 636a6e350fSMatthew Dillon 64c453712aSMatthew Dillon struct didpfs { 65c453712aSMatthew Dillon struct didpfs *next; 66c453712aSMatthew Dillon uuid_t uuid; 67c453712aSMatthew Dillon }; 68c453712aSMatthew Dillon 696a6e350fSMatthew Dillon static void do_cleanup(const char *path); 7083f2a3aaSMatthew Dillon static void config_init(const char *path, struct hammer_ioc_config *config); 7183f2a3aaSMatthew Dillon static void migrate_config(FILE *fp, struct hammer_ioc_config *config); 7283f2a3aaSMatthew Dillon static void migrate_snapshots(int fd, const char *snapshots_path); 7383f2a3aaSMatthew Dillon static void migrate_one_snapshot(int fd, const char *fpath, 7483f2a3aaSMatthew Dillon struct hammer_ioc_snapshot *snapshot); 756a6e350fSMatthew Dillon static int strtosecs(char *ptr); 766a6e350fSMatthew Dillon static const char *dividing_slash(const char *path); 776a6e350fSMatthew Dillon static int check_period(const char *snapshots_path, const char *cmd, int arg1, 786a6e350fSMatthew Dillon time_t *savep); 796a6e350fSMatthew Dillon static void save_period(const char *snapshots_path, const char *cmd, 806a6e350fSMatthew Dillon time_t savet); 8183f2a3aaSMatthew Dillon static int check_softlinks(int fd, int new_config, const char *snapshots_path); 8283f2a3aaSMatthew Dillon static void cleanup_softlinks(int fd, int new_config, 8383f2a3aaSMatthew Dillon const char *snapshots_path, int arg2, char *arg3); 84ff1c9800SMatthew Dillon static int check_expired(const char *fpath, int arg2); 856a6e350fSMatthew Dillon 86b5ec5ad4SMatthew Dillon static int create_snapshot(const char *path, const char *snapshots_path); 870b8bd7daSMatthew Dillon static int cleanup_rebalance(const char *path, const char *snapshots_path, 880b8bd7daSMatthew Dillon int arg1, int arg2); 896a6e350fSMatthew Dillon static int cleanup_prune(const char *path, const char *snapshots_path, 90c6c298a7SMatthew Dillon int arg1, int arg2, int snapshots_disabled); 916a6e350fSMatthew Dillon static int cleanup_reblock(const char *path, const char *snapshots_path, 926a6e350fSMatthew Dillon int arg1, int arg2); 936a6e350fSMatthew Dillon static int cleanup_recopy(const char *path, const char *snapshots_path, 946a6e350fSMatthew Dillon int arg1, int arg2); 956a6e350fSMatthew Dillon 966a6e350fSMatthew Dillon static void runcmd(int *resp, const char *ctl, ...); 976a6e350fSMatthew Dillon 98b5ec5ad4SMatthew Dillon /* 99b5ec5ad4SMatthew Dillon * WARNING: Do not make the SNAPSHOTS_BASE "/var/snapshots" because 100b5ec5ad4SMatthew Dillon * it will interfere with the older HAMMER VERS < 3 snapshots directory 101b5ec5ad4SMatthew Dillon * for the /var PFS. 102b5ec5ad4SMatthew Dillon */ 103b5ec5ad4SMatthew Dillon #define SNAPSHOTS_BASE "/var/hammer" /* HAMMER VERS >= 3 */ 1046a6e350fSMatthew Dillon #define WS " \t\r\n" 1056a6e350fSMatthew Dillon 106c453712aSMatthew Dillon struct didpfs *FirstPFS; 1076a6e350fSMatthew Dillon 1086a6e350fSMatthew Dillon void 1096a6e350fSMatthew Dillon hammer_cmd_cleanup(char **av, int ac) 1106a6e350fSMatthew Dillon { 111eac446c5SMatthew Dillon char *fstype, *fs, *path; 112eac446c5SMatthew Dillon struct statfs *stfsbuf; 113eac446c5SMatthew Dillon int mntsize, i; 1146a6e350fSMatthew Dillon 1156a6e350fSMatthew Dillon tzset(); 1166a6e350fSMatthew Dillon if (ac == 0) { 117eac446c5SMatthew Dillon mntsize = getmntinfo(&stfsbuf, MNT_NOWAIT); 118eac446c5SMatthew Dillon if (mntsize > 0) { 119eac446c5SMatthew Dillon for (i=0; i < mntsize; i++) { 120eac446c5SMatthew Dillon /* 121eac446c5SMatthew Dillon * We will cleanup in the case fstype is hammer. 122eac446c5SMatthew Dillon * If we have null-mounted PFS, we check the 123eac446c5SMatthew Dillon * mount source. If it looks like a PFS, we 124eac446c5SMatthew Dillon * proceed to cleanup also. 125eac446c5SMatthew Dillon */ 126eac446c5SMatthew Dillon fstype = stfsbuf[i].f_fstypename; 127eac446c5SMatthew Dillon fs = stfsbuf[i].f_mntfromname; 128eac446c5SMatthew Dillon if ((strcmp(fstype, "hammer") == 0) || 129eac446c5SMatthew Dillon ((strcmp(fstype, "null") == 0) && 130cb3c760cSMatthew Dillon (strstr(fs, "/@@0x") != NULL || 131cb3c760cSMatthew Dillon strstr(fs, "/@@-1") != NULL))) { 132eac446c5SMatthew Dillon path = stfsbuf[i].f_mntonname; 1336a6e350fSMatthew Dillon do_cleanup(path); 1346a6e350fSMatthew Dillon } 135574066d3SMatthew Dillon } 136eac446c5SMatthew Dillon } 137eac446c5SMatthew Dillon 1386a6e350fSMatthew Dillon } else { 1396a6e350fSMatthew Dillon while (ac) { 1406a6e350fSMatthew Dillon do_cleanup(*av); 1416a6e350fSMatthew Dillon --ac; 1426a6e350fSMatthew Dillon ++av; 1436a6e350fSMatthew Dillon } 1446a6e350fSMatthew Dillon } 1456a6e350fSMatthew Dillon } 1466a6e350fSMatthew Dillon 1476a6e350fSMatthew Dillon static 1486a6e350fSMatthew Dillon void 1496a6e350fSMatthew Dillon do_cleanup(const char *path) 1506a6e350fSMatthew Dillon { 1516a6e350fSMatthew Dillon struct hammer_ioc_pseudofs_rw pfs; 15283f2a3aaSMatthew Dillon struct hammer_ioc_config config; 15383f2a3aaSMatthew Dillon struct hammer_ioc_version version; 1546a6e350fSMatthew Dillon union hammer_ioc_mrecord_any mrec_tmp; 155*82893617SThomas Nikolajsen char *snapshots_path = NULL; 1566a6e350fSMatthew Dillon char *config_path; 1576a6e350fSMatthew Dillon struct stat st; 1586a6e350fSMatthew Dillon char *cmd; 1596a6e350fSMatthew Dillon char *ptr; 1606a6e350fSMatthew Dillon int arg1; 1616a6e350fSMatthew Dillon int arg2; 1625e435c92SMatthew Dillon char *arg3; 1636a6e350fSMatthew Dillon time_t savet; 1646a6e350fSMatthew Dillon char buf[256]; 16583f2a3aaSMatthew Dillon char *cbase; 16683f2a3aaSMatthew Dillon char *cptr; 167*82893617SThomas Nikolajsen FILE *fp = NULL; 168c453712aSMatthew Dillon struct didpfs *didpfs; 169c6c298a7SMatthew Dillon int snapshots_disabled = 0; 170c6c298a7SMatthew Dillon int prune_warning = 0; 17183f2a3aaSMatthew Dillon int new_config = 0; 172b5ec5ad4SMatthew Dillon int snapshots_from_pfs = 0; 1736a6e350fSMatthew Dillon int fd; 1746a6e350fSMatthew Dillon int r; 1750b8bd7daSMatthew Dillon int found_rebal = 0; 1766a6e350fSMatthew Dillon 1776a6e350fSMatthew Dillon bzero(&pfs, sizeof(pfs)); 1786a6e350fSMatthew Dillon bzero(&mrec_tmp, sizeof(mrec_tmp)); 1796a6e350fSMatthew Dillon pfs.ondisk = &mrec_tmp.pfs.pfsd; 1806a6e350fSMatthew Dillon pfs.bytes = sizeof(mrec_tmp.pfs.pfsd); 1816a6e350fSMatthew Dillon pfs.pfs_id = -1; 1826a6e350fSMatthew Dillon 1836a6e350fSMatthew Dillon printf("cleanup %-20s -", path); 1846a6e350fSMatthew Dillon fd = open(path, O_RDONLY); 1856a6e350fSMatthew Dillon if (fd < 0) { 1866a6e350fSMatthew Dillon printf(" unable to access directory: %s\n", strerror(errno)); 1876a6e350fSMatthew Dillon return; 1886a6e350fSMatthew Dillon } 18983f2a3aaSMatthew Dillon if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) < 0) { 1906a6e350fSMatthew Dillon printf(" not a HAMMER filesystem: %s\n", strerror(errno)); 19183f2a3aaSMatthew Dillon close(fd); 1926a6e350fSMatthew Dillon return; 1936a6e350fSMatthew Dillon } 1946a6e350fSMatthew Dillon if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) { 1956a6e350fSMatthew Dillon printf(" unrecognized HAMMER version\n"); 19683f2a3aaSMatthew Dillon close(fd); 1976a6e350fSMatthew Dillon return; 1986a6e350fSMatthew Dillon } 19983f2a3aaSMatthew Dillon bzero(&version, sizeof(version)); 20083f2a3aaSMatthew Dillon if (ioctl(fd, HAMMERIOC_GET_VERSION, &version) < 0) { 20183f2a3aaSMatthew Dillon printf(" HAMMER filesystem but couldn't retrieve version!\n"); 20283f2a3aaSMatthew Dillon close(fd); 20383f2a3aaSMatthew Dillon return; 20483f2a3aaSMatthew Dillon } 20583f2a3aaSMatthew Dillon 20683f2a3aaSMatthew Dillon bzero(&config, sizeof(config)); 20783f2a3aaSMatthew Dillon if (version.cur_version >= 3) { 20883f2a3aaSMatthew Dillon if (ioctl(fd, HAMMERIOC_GET_CONFIG, &config) == 0 && 20983f2a3aaSMatthew Dillon config.head.error == 0) { 21083f2a3aaSMatthew Dillon new_config = 1; 21183f2a3aaSMatthew Dillon } 21283f2a3aaSMatthew Dillon } 2136a6e350fSMatthew Dillon 2146a6e350fSMatthew Dillon /* 2156a6e350fSMatthew Dillon * Make sure we have not already handled this PFS. Several nullfs 2166a6e350fSMatthew Dillon * mounts might alias the same PFS. 2176a6e350fSMatthew Dillon */ 218c453712aSMatthew Dillon for (didpfs = FirstPFS; didpfs; didpfs = didpfs->next) { 219c453712aSMatthew Dillon if (bcmp(&didpfs->uuid, &mrec_tmp.pfs.pfsd.unique_uuid, sizeof(uuid_t)) == 0) { 220bb8e52c0SThomas Nikolajsen printf(" PFS #%d already handled\n", pfs.pfs_id); 22183f2a3aaSMatthew Dillon close(fd); 2226a6e350fSMatthew Dillon return; 2236a6e350fSMatthew Dillon } 224c453712aSMatthew Dillon } 225c453712aSMatthew Dillon didpfs = malloc(sizeof(*didpfs)); 226c453712aSMatthew Dillon didpfs->next = FirstPFS; 227c453712aSMatthew Dillon FirstPFS = didpfs; 228c453712aSMatthew Dillon didpfs->uuid = mrec_tmp.pfs.pfsd.unique_uuid; 2296a6e350fSMatthew Dillon 2306a6e350fSMatthew Dillon /* 231b5ec5ad4SMatthew Dillon * Calculate the old snapshots directory for HAMMER VERSION < 3 232b5ec5ad4SMatthew Dillon * 233b5ec5ad4SMatthew Dillon * If the directory is explicitly specified in the PFS config 234b5ec5ad4SMatthew Dillon * we flag it and will not migrate it later. 235ff1c9800SMatthew Dillon */ 236ff1c9800SMatthew Dillon if (mrec_tmp.pfs.pfsd.snapshots[0] == '/') { 237ff1c9800SMatthew Dillon asprintf(&snapshots_path, "%s", mrec_tmp.pfs.pfsd.snapshots); 238b5ec5ad4SMatthew Dillon snapshots_from_pfs = 1; 239ff1c9800SMatthew Dillon } else if (mrec_tmp.pfs.pfsd.snapshots[0]) { 240ff1c9800SMatthew Dillon printf(" WARNING: pfs-slave's snapshots dir is not absolute\n"); 24183f2a3aaSMatthew Dillon close(fd); 242ff1c9800SMatthew Dillon return; 243ff1c9800SMatthew Dillon } else if (mrec_tmp.pfs.pfsd.mirror_flags & HAMMER_PFSD_SLAVE) { 244*82893617SThomas Nikolajsen if (version.cur_version < 3) { 245ff1c9800SMatthew Dillon printf(" WARNING: must configure snapshot dir for PFS slave\n"); 246ff1c9800SMatthew Dillon printf("\tWe suggest <fs>/var/slaves/<name> where " 247ff1c9800SMatthew Dillon "<fs> is the base HAMMER fs\n"); 248bb8e52c0SThomas Nikolajsen printf("\tcontaining the slave\n"); 24983f2a3aaSMatthew Dillon close(fd); 250ff1c9800SMatthew Dillon return; 251*82893617SThomas Nikolajsen } 252ff1c9800SMatthew Dillon } else { 253ff1c9800SMatthew Dillon asprintf(&snapshots_path, 254ff1c9800SMatthew Dillon "%s%ssnapshots", path, dividing_slash(path)); 255ff1c9800SMatthew Dillon } 256ff1c9800SMatthew Dillon 257ff1c9800SMatthew Dillon /* 258b5ec5ad4SMatthew Dillon * Check for old-style config file 2596a6e350fSMatthew Dillon */ 260*82893617SThomas Nikolajsen if (snapshots_path) { 2616a6e350fSMatthew Dillon asprintf(&config_path, "%s/config", snapshots_path); 2626a6e350fSMatthew Dillon fp = fopen(config_path, "r"); 263*82893617SThomas Nikolajsen } 26483f2a3aaSMatthew Dillon 26583f2a3aaSMatthew Dillon /* 26683f2a3aaSMatthew Dillon * Handle upgrades to hammer version 3, move the config 26783f2a3aaSMatthew Dillon * file into meta-data. 26883f2a3aaSMatthew Dillon * 26983f2a3aaSMatthew Dillon * For the old config read the file into the config structure, 27083f2a3aaSMatthew Dillon * we will parse it out of the config structure regardless. 27183f2a3aaSMatthew Dillon */ 27283f2a3aaSMatthew Dillon if (version.cur_version >= 3) { 27383f2a3aaSMatthew Dillon if (fp) { 27483f2a3aaSMatthew Dillon printf("(migrating) "); 27583f2a3aaSMatthew Dillon fflush(stdout); 27683f2a3aaSMatthew Dillon migrate_config(fp, &config); 27783f2a3aaSMatthew Dillon migrate_snapshots(fd, snapshots_path); 27883f2a3aaSMatthew Dillon fclose(fp); 27983f2a3aaSMatthew Dillon if (ioctl(fd, HAMMERIOC_SET_CONFIG, &config) < 0) { 28083f2a3aaSMatthew Dillon printf(" cannot init meta-data config!\n"); 28183f2a3aaSMatthew Dillon close(fd); 2826a6e350fSMatthew Dillon return; 2836a6e350fSMatthew Dillon } 28483f2a3aaSMatthew Dillon remove(config_path); 28583f2a3aaSMatthew Dillon } else if (new_config == 0) { 28683f2a3aaSMatthew Dillon config_init(path, &config); 28783f2a3aaSMatthew Dillon if (ioctl(fd, HAMMERIOC_SET_CONFIG, &config) < 0) { 28883f2a3aaSMatthew Dillon printf(" cannot init meta-data config!\n"); 28983f2a3aaSMatthew Dillon close(fd); 29083f2a3aaSMatthew Dillon return; 29183f2a3aaSMatthew Dillon } 29283f2a3aaSMatthew Dillon } 29383f2a3aaSMatthew Dillon new_config = 1; 29483f2a3aaSMatthew Dillon } else { 295b5ec5ad4SMatthew Dillon /* 296b5ec5ad4SMatthew Dillon * Create missing snapshots directory for HAMMER VERSION < 3 297b5ec5ad4SMatthew Dillon */ 298b5ec5ad4SMatthew Dillon if (stat(snapshots_path, &st) < 0) { 299b5ec5ad4SMatthew Dillon if (mkdir(snapshots_path, 0755) != 0) { 300b5ec5ad4SMatthew Dillon free(snapshots_path); 301b5ec5ad4SMatthew Dillon printf(" unable to create snapshot dir \"%s\": %s\n", 302b5ec5ad4SMatthew Dillon snapshots_path, strerror(errno)); 303b5ec5ad4SMatthew Dillon close(fd); 304b5ec5ad4SMatthew Dillon return; 305b5ec5ad4SMatthew Dillon } 306b5ec5ad4SMatthew Dillon } 307b5ec5ad4SMatthew Dillon 308b5ec5ad4SMatthew Dillon /* 309b5ec5ad4SMatthew Dillon * Create missing config file for HAMMER VERSION < 3 310b5ec5ad4SMatthew Dillon */ 31183f2a3aaSMatthew Dillon if (fp == NULL) { 31283f2a3aaSMatthew Dillon config_init(path, &config); 31383f2a3aaSMatthew Dillon fp = fopen(config_path, "w"); 31483f2a3aaSMatthew Dillon if (fp) { 31583f2a3aaSMatthew Dillon fwrite(config.config.text, 1, 31683f2a3aaSMatthew Dillon strlen(config.config.text), fp); 31783f2a3aaSMatthew Dillon fclose(fp); 31883f2a3aaSMatthew Dillon } 31983f2a3aaSMatthew Dillon } else { 32083f2a3aaSMatthew Dillon migrate_config(fp, &config); 32183f2a3aaSMatthew Dillon fclose(fp); 32283f2a3aaSMatthew Dillon } 32383f2a3aaSMatthew Dillon } 3246a6e350fSMatthew Dillon 325b5ec5ad4SMatthew Dillon /* 326b5ec5ad4SMatthew Dillon * If snapshots_from_pfs is not set we calculate the new snapshots 327b5ec5ad4SMatthew Dillon * directory default (in /var) for HAMMER VERSION >= 3 and migrate 328b5ec5ad4SMatthew Dillon * the old snapshots directory over. 329b5ec5ad4SMatthew Dillon * 330b5ec5ad4SMatthew Dillon * People who have set an explicit snapshots directory will have 331b5ec5ad4SMatthew Dillon * to migrate the data manually into /var/hammer, or not bother at 332b5ec5ad4SMatthew Dillon * all. People running slaves may wish to migrate it and then 333b5ec5ad4SMatthew Dillon * clear the snapshots specification in the PFS config for the 334b5ec5ad4SMatthew Dillon * slave. 335b5ec5ad4SMatthew Dillon */ 336b5ec5ad4SMatthew Dillon if (new_config && snapshots_from_pfs == 0) { 337b5ec5ad4SMatthew Dillon char *npath; 338b5ec5ad4SMatthew Dillon 339b5ec5ad4SMatthew Dillon assert(path[0] == '/'); 340b5ec5ad4SMatthew Dillon if (strcmp(path, "/") == 0) 341b5ec5ad4SMatthew Dillon asprintf(&npath, "%s/root", SNAPSHOTS_BASE); 342b5ec5ad4SMatthew Dillon else 343b5ec5ad4SMatthew Dillon asprintf(&npath, "%s/%s", SNAPSHOTS_BASE, path + 1); 344*82893617SThomas Nikolajsen if (snapshots_path) { 345b5ec5ad4SMatthew Dillon if (stat(npath, &st) < 0 && errno == ENOENT) { 346b5ec5ad4SMatthew Dillon if (stat(snapshots_path, &st) < 0 && errno == ENOENT) { 347b5ec5ad4SMatthew Dillon printf(" HAMMER UPGRADE: Creating snapshots\n" 348b5ec5ad4SMatthew Dillon "\tCreating snapshots in %s\n", 349b5ec5ad4SMatthew Dillon npath); 350b5ec5ad4SMatthew Dillon runcmd(&r, "mkdir -p %s", npath); 351b5ec5ad4SMatthew Dillon } else { 352b5ec5ad4SMatthew Dillon printf(" HAMMER UPGRADE: Moving snapshots\n" 353b5ec5ad4SMatthew Dillon "\tMoving snapshots from %s to %s\n", 354b5ec5ad4SMatthew Dillon snapshots_path, npath); 355b5ec5ad4SMatthew Dillon runcmd(&r, "mkdir -p %s", npath); 356b5ec5ad4SMatthew Dillon runcmd(&r, "cpdup %s %s", snapshots_path, npath); 357b5ec5ad4SMatthew Dillon if (r != 0) { 358b5ec5ad4SMatthew Dillon printf("Unable to move snapshots directory!\n"); 359b5ec5ad4SMatthew Dillon printf("Please fix this critical error.\n"); 360b5ec5ad4SMatthew Dillon printf("Aborting cleanup of %s\n", path); 361b5ec5ad4SMatthew Dillon close(fd); 362b5ec5ad4SMatthew Dillon return; 363b5ec5ad4SMatthew Dillon } 364b5ec5ad4SMatthew Dillon runcmd(&r, "rm -rf %s", snapshots_path); 365b5ec5ad4SMatthew Dillon } 366b5ec5ad4SMatthew Dillon } 367b5ec5ad4SMatthew Dillon free(snapshots_path); 368*82893617SThomas Nikolajsen } else if (stat(npath, &st) < 0 && errno == ENOENT) { 369*82893617SThomas Nikolajsen runcmd(&r, "mkdir -p %s", npath); 370*82893617SThomas Nikolajsen } 371b5ec5ad4SMatthew Dillon snapshots_path = npath; 372b5ec5ad4SMatthew Dillon } 373b5ec5ad4SMatthew Dillon 374b5ec5ad4SMatthew Dillon /* 375b5ec5ad4SMatthew Dillon * Lock the PFS. fd is the base directory of the mounted PFS. 376b5ec5ad4SMatthew Dillon */ 37783f2a3aaSMatthew Dillon if (flock(fd, LOCK_EX|LOCK_NB) == -1) { 3785bd5f172SSimon Schubert if (errno == EWOULDBLOCK) 3795bd5f172SSimon Schubert printf(" PFS #%d locked by other process\n", pfs.pfs_id); 3805bd5f172SSimon Schubert else 3815bd5f172SSimon Schubert printf(" can not lock %s: %s\n", config_path, strerror(errno)); 38283f2a3aaSMatthew Dillon close(fd); 3835bd5f172SSimon Schubert return; 3845bd5f172SSimon Schubert } 3855bd5f172SSimon Schubert 386ff1c9800SMatthew Dillon printf(" handle PFS #%d using %s\n", pfs.pfs_id, snapshots_path); 3876a6e350fSMatthew Dillon 3886a6e350fSMatthew Dillon /* 3896a6e350fSMatthew Dillon * Process the config file 3906a6e350fSMatthew Dillon */ 39183f2a3aaSMatthew Dillon cbase = config.config.text; 39283f2a3aaSMatthew Dillon 39383f2a3aaSMatthew Dillon while ((cptr = strchr(cbase, '\n')) != NULL) { 39483f2a3aaSMatthew Dillon bcopy(cbase, buf, cptr - cbase); 39583f2a3aaSMatthew Dillon buf[cptr - cbase] = 0; 39683f2a3aaSMatthew Dillon cbase = cptr + 1; 39783f2a3aaSMatthew Dillon 3986a6e350fSMatthew Dillon cmd = strtok(buf, WS); 399743332abSMatthew Dillon if (cmd == NULL || cmd[0] == '#') 400743332abSMatthew Dillon continue; 401743332abSMatthew Dillon 4026a6e350fSMatthew Dillon arg1 = 0; 4036a6e350fSMatthew Dillon arg2 = 0; 4045e435c92SMatthew Dillon arg3 = NULL; 4056a6e350fSMatthew Dillon if ((ptr = strtok(NULL, WS)) != NULL) { 4066a6e350fSMatthew Dillon arg1 = strtosecs(ptr); 4075e435c92SMatthew Dillon if ((ptr = strtok(NULL, WS)) != NULL) { 4086a6e350fSMatthew Dillon arg2 = strtosecs(ptr); 4095e435c92SMatthew Dillon arg3 = strtok(NULL, WS); 4105e435c92SMatthew Dillon } 4116a6e350fSMatthew Dillon } 4126a6e350fSMatthew Dillon 4136a6e350fSMatthew Dillon printf("%20s - ", cmd); 4146a6e350fSMatthew Dillon fflush(stdout); 4156a6e350fSMatthew Dillon 4166a6e350fSMatthew Dillon r = 1; 4176a6e350fSMatthew Dillon if (strcmp(cmd, "snapshots") == 0) { 4185e435c92SMatthew Dillon if (arg1 == 0) { 41983f2a3aaSMatthew Dillon if (arg2 && 42083f2a3aaSMatthew Dillon check_softlinks(fd, new_config, 42183f2a3aaSMatthew Dillon snapshots_path)) { 4225e435c92SMatthew Dillon printf("only removing old snapshots\n"); 4235e435c92SMatthew Dillon prune_warning = 1; 42483f2a3aaSMatthew Dillon cleanup_softlinks(fd, new_config, 42583f2a3aaSMatthew Dillon snapshots_path, 4265e435c92SMatthew Dillon arg2, arg3); 4275e435c92SMatthew Dillon } else { 4285e435c92SMatthew Dillon printf("disabled\n"); 4295e435c92SMatthew Dillon snapshots_disabled = 1; 4305e435c92SMatthew Dillon } 4315e435c92SMatthew Dillon } else 4326a6e350fSMatthew Dillon if (check_period(snapshots_path, cmd, arg1, &savet)) { 4336a6e350fSMatthew Dillon printf("run\n"); 43483f2a3aaSMatthew Dillon cleanup_softlinks(fd, new_config, 43583f2a3aaSMatthew Dillon snapshots_path, 4365e435c92SMatthew Dillon arg2, arg3); 437b5ec5ad4SMatthew Dillon r = create_snapshot(path, snapshots_path); 4386a6e350fSMatthew Dillon } else { 4396a6e350fSMatthew Dillon printf("skip\n"); 4406a6e350fSMatthew Dillon } 4415e435c92SMatthew Dillon } else if (arg1 == 0) { 4425e435c92SMatthew Dillon /* 4435e435c92SMatthew Dillon * The commands following this check can't handle 4445e435c92SMatthew Dillon * a period of 0, so call the feature disabled and 4455e435c92SMatthew Dillon * ignore the directive. 4465e435c92SMatthew Dillon */ 4475e435c92SMatthew Dillon printf("disabled\n"); 4486a6e350fSMatthew Dillon } else if (strcmp(cmd, "prune") == 0) { 4496a6e350fSMatthew Dillon if (check_period(snapshots_path, cmd, arg1, &savet)) { 4505e435c92SMatthew Dillon if (prune_warning) { 4515e435c92SMatthew Dillon printf("run - WARNING snapshot " 4525e435c92SMatthew Dillon "softlinks present " 453226f3799SThomas Nikolajsen "but snapshots disabled\n"); 4545e435c92SMatthew Dillon } else { 455c6c298a7SMatthew Dillon printf("run\n"); 4565e435c92SMatthew Dillon } 4576a6e350fSMatthew Dillon r = cleanup_prune(path, snapshots_path, 458c6c298a7SMatthew Dillon arg1, arg2, snapshots_disabled); 4596a6e350fSMatthew Dillon } else { 4606a6e350fSMatthew Dillon printf("skip\n"); 4616a6e350fSMatthew Dillon } 4620b8bd7daSMatthew Dillon } else if (strcmp(cmd, "rebalance") == 0) { 4630b8bd7daSMatthew Dillon found_rebal = 1; 4640b8bd7daSMatthew Dillon if (check_period(snapshots_path, cmd, arg1, &savet)) { 4650b8bd7daSMatthew Dillon printf("run"); 4660b8bd7daSMatthew Dillon fflush(stdout); 4670b8bd7daSMatthew Dillon if (VerboseOpt) 4680b8bd7daSMatthew Dillon printf("\n"); 4690b8bd7daSMatthew Dillon r = cleanup_rebalance(path, snapshots_path, 4700b8bd7daSMatthew Dillon arg1, arg2); 4710b8bd7daSMatthew Dillon } else { 4720b8bd7daSMatthew Dillon printf("skip\n"); 4730b8bd7daSMatthew Dillon } 4746a6e350fSMatthew Dillon } else if (strcmp(cmd, "reblock") == 0) { 4756a6e350fSMatthew Dillon if (check_period(snapshots_path, cmd, arg1, &savet)) { 4766a6e350fSMatthew Dillon printf("run"); 4776a6e350fSMatthew Dillon fflush(stdout); 4786a6e350fSMatthew Dillon if (VerboseOpt) 4796a6e350fSMatthew Dillon printf("\n"); 4806a6e350fSMatthew Dillon r = cleanup_reblock(path, snapshots_path, 4816a6e350fSMatthew Dillon arg1, arg2); 4826a6e350fSMatthew Dillon } else { 4836a6e350fSMatthew Dillon printf("skip\n"); 4846a6e350fSMatthew Dillon } 4856a6e350fSMatthew Dillon } else if (strcmp(cmd, "recopy") == 0) { 4866a6e350fSMatthew Dillon if (check_period(snapshots_path, cmd, arg1, &savet)) { 4876a6e350fSMatthew Dillon printf("run"); 4886a6e350fSMatthew Dillon fflush(stdout); 4896a6e350fSMatthew Dillon if (VerboseOpt) 4906a6e350fSMatthew Dillon printf("\n"); 4916a6e350fSMatthew Dillon r = cleanup_recopy(path, snapshots_path, 4926a6e350fSMatthew Dillon arg1, arg2); 4936a6e350fSMatthew Dillon } else { 4946a6e350fSMatthew Dillon printf("skip\n"); 4956a6e350fSMatthew Dillon } 4966a6e350fSMatthew Dillon } else { 4976a6e350fSMatthew Dillon printf("unknown directive\n"); 4986a6e350fSMatthew Dillon r = 1; 4996a6e350fSMatthew Dillon } 5006a6e350fSMatthew Dillon if (r == 0) 5016a6e350fSMatthew Dillon save_period(snapshots_path, cmd, savet); 5026a6e350fSMatthew Dillon } 5030b8bd7daSMatthew Dillon 5040b8bd7daSMatthew Dillon /* 50583f2a3aaSMatthew Dillon * Add new rebalance feature if the config doesn't have it. 506b5ec5ad4SMatthew Dillon * (old style config only). 5070b8bd7daSMatthew Dillon */ 50883f2a3aaSMatthew Dillon if (new_config == 0 && found_rebal == 0) { 5090b8bd7daSMatthew Dillon if ((fp = fopen(config_path, "r+")) != NULL) { 5100b8bd7daSMatthew Dillon fseek(fp, 0L, 2); 5110b8bd7daSMatthew Dillon fprintf(fp, "rebalance 1d 5m\n"); 5120b8bd7daSMatthew Dillon fclose(fp); 5130b8bd7daSMatthew Dillon } 5140b8bd7daSMatthew Dillon } 51583f2a3aaSMatthew Dillon 51683f2a3aaSMatthew Dillon /* 51783f2a3aaSMatthew Dillon * Cleanup, and delay a little 51883f2a3aaSMatthew Dillon */ 51983f2a3aaSMatthew Dillon close(fd); 5206a6e350fSMatthew Dillon usleep(1000); 5216a6e350fSMatthew Dillon } 5226a6e350fSMatthew Dillon 52383f2a3aaSMatthew Dillon /* 52483f2a3aaSMatthew Dillon * Initialize new config data (new or old style) 52583f2a3aaSMatthew Dillon */ 52683f2a3aaSMatthew Dillon static void 52783f2a3aaSMatthew Dillon config_init(const char *path, struct hammer_ioc_config *config) 52883f2a3aaSMatthew Dillon { 52983f2a3aaSMatthew Dillon const char *snapshots; 53083f2a3aaSMatthew Dillon 53183f2a3aaSMatthew Dillon if (strcmp(path, "/tmp") == 0 || 53283f2a3aaSMatthew Dillon strcmp(path, "/var/tmp") == 0 || 53383f2a3aaSMatthew Dillon strcmp(path, "/usr/obj") == 0) { 53483f2a3aaSMatthew Dillon snapshots = "snapshots 0d 0d\n"; 53583f2a3aaSMatthew Dillon } else { 53683f2a3aaSMatthew Dillon snapshots = "snapshots 1d 60d\n"; 53783f2a3aaSMatthew Dillon } 53883f2a3aaSMatthew Dillon bzero(config->config.text, sizeof(config->config.text)); 53983f2a3aaSMatthew Dillon snprintf(config->config.text, sizeof(config->config.text) - 1, "%s%s", 54083f2a3aaSMatthew Dillon snapshots, 54183f2a3aaSMatthew Dillon "prune 1d 5m\n" 54283f2a3aaSMatthew Dillon "rebalance 1d 5m\n" 54383f2a3aaSMatthew Dillon "reblock 1d 5m\n" 544f6532f03SThomas Nikolajsen "recopy 30d 10m\n"); 54583f2a3aaSMatthew Dillon } 54683f2a3aaSMatthew Dillon 54783f2a3aaSMatthew Dillon /* 54883f2a3aaSMatthew Dillon * Migrate configuration data from the old snapshots/config 54916265794SThomas Nikolajsen * file to the new meta-data format. 55083f2a3aaSMatthew Dillon */ 55183f2a3aaSMatthew Dillon static void 55283f2a3aaSMatthew Dillon migrate_config(FILE *fp, struct hammer_ioc_config *config) 55383f2a3aaSMatthew Dillon { 55483f2a3aaSMatthew Dillon int n; 55583f2a3aaSMatthew Dillon 55683f2a3aaSMatthew Dillon n = fread(config->config.text, 1, sizeof(config->config.text) - 1, fp); 55783f2a3aaSMatthew Dillon if (n >= 0) 55883f2a3aaSMatthew Dillon bzero(config->config.text + n, sizeof(config->config.text) - n); 55983f2a3aaSMatthew Dillon } 56083f2a3aaSMatthew Dillon 56183f2a3aaSMatthew Dillon /* 56283f2a3aaSMatthew Dillon * Migrate snapshot softlinks in the snapshots directory to the 56383f2a3aaSMatthew Dillon * new meta-data format. The softlinks are left intact, but 56483f2a3aaSMatthew Dillon * this way the pruning code won't lose track of them if you 56583f2a3aaSMatthew Dillon * happen to blow away the snapshots directory. 56683f2a3aaSMatthew Dillon */ 56783f2a3aaSMatthew Dillon static void 56883f2a3aaSMatthew Dillon migrate_snapshots(int fd, const char *snapshots_path) 56983f2a3aaSMatthew Dillon { 57083f2a3aaSMatthew Dillon struct hammer_ioc_snapshot snapshot; 57183f2a3aaSMatthew Dillon struct dirent *den; 57283f2a3aaSMatthew Dillon struct stat st; 57383f2a3aaSMatthew Dillon DIR *dir; 57483f2a3aaSMatthew Dillon char *fpath; 57583f2a3aaSMatthew Dillon 57683f2a3aaSMatthew Dillon bzero(&snapshot, sizeof(snapshot)); 57783f2a3aaSMatthew Dillon 57883f2a3aaSMatthew Dillon if ((dir = opendir(snapshots_path)) != NULL) { 57983f2a3aaSMatthew Dillon while ((den = readdir(dir)) != NULL) { 58083f2a3aaSMatthew Dillon if (den->d_name[0] == '.') 58183f2a3aaSMatthew Dillon continue; 58283f2a3aaSMatthew Dillon asprintf(&fpath, "%s/%s", snapshots_path, den->d_name); 58383f2a3aaSMatthew Dillon if (lstat(fpath, &st) == 0 && S_ISLNK(st.st_mode)) { 58483f2a3aaSMatthew Dillon migrate_one_snapshot(fd, fpath, &snapshot); 58583f2a3aaSMatthew Dillon } 58683f2a3aaSMatthew Dillon free(fpath); 58783f2a3aaSMatthew Dillon } 58883f2a3aaSMatthew Dillon closedir(dir); 58983f2a3aaSMatthew Dillon } 59083f2a3aaSMatthew Dillon migrate_one_snapshot(fd, NULL, &snapshot); 59183f2a3aaSMatthew Dillon 59283f2a3aaSMatthew Dillon } 59383f2a3aaSMatthew Dillon 59483f2a3aaSMatthew Dillon /* 59583f2a3aaSMatthew Dillon * Migrate a single snapshot. If fpath is NULL the ioctl is flushed, 59683f2a3aaSMatthew Dillon * otherwise it is flushed when it fills up. 59783f2a3aaSMatthew Dillon */ 59883f2a3aaSMatthew Dillon static void 59983f2a3aaSMatthew Dillon migrate_one_snapshot(int fd, const char *fpath, 60083f2a3aaSMatthew Dillon struct hammer_ioc_snapshot *snapshot) 60183f2a3aaSMatthew Dillon { 60283f2a3aaSMatthew Dillon if (fpath) { 60383f2a3aaSMatthew Dillon struct hammer_snapshot_data *snap; 60483f2a3aaSMatthew Dillon struct tm tm; 60583f2a3aaSMatthew Dillon time_t t; 60683f2a3aaSMatthew Dillon int year; 60783f2a3aaSMatthew Dillon int month; 60883f2a3aaSMatthew Dillon int day = 0; 60983f2a3aaSMatthew Dillon int hour = 0; 61083f2a3aaSMatthew Dillon int minute = 0; 61183f2a3aaSMatthew Dillon int r; 61283f2a3aaSMatthew Dillon char linkbuf[1024]; 61383f2a3aaSMatthew Dillon const char *ptr; 61483f2a3aaSMatthew Dillon hammer_tid_t tid; 61583f2a3aaSMatthew Dillon 61683f2a3aaSMatthew Dillon t = (time_t)-1; 61783f2a3aaSMatthew Dillon tid = (hammer_tid_t)(int64_t)-1; 61883f2a3aaSMatthew Dillon 61983f2a3aaSMatthew Dillon ptr = fpath; 62083f2a3aaSMatthew Dillon while (*ptr && *ptr != '-' && *ptr != '.') 62183f2a3aaSMatthew Dillon ++ptr; 62283f2a3aaSMatthew Dillon if (*ptr) 62383f2a3aaSMatthew Dillon ++ptr; 62483f2a3aaSMatthew Dillon r = sscanf(ptr, "%4d%2d%2d-%2d%2d", 62583f2a3aaSMatthew Dillon &year, &month, &day, &hour, &minute); 62683f2a3aaSMatthew Dillon 62783f2a3aaSMatthew Dillon if (r >= 3) { 62883f2a3aaSMatthew Dillon bzero(&tm, sizeof(tm)); 62983f2a3aaSMatthew Dillon tm.tm_isdst = -1; 63083f2a3aaSMatthew Dillon tm.tm_min = minute; 63183f2a3aaSMatthew Dillon tm.tm_hour = hour; 63283f2a3aaSMatthew Dillon tm.tm_mday = day; 63383f2a3aaSMatthew Dillon tm.tm_mon = month - 1; 63483f2a3aaSMatthew Dillon tm.tm_year = year - 1900; 63583f2a3aaSMatthew Dillon t = mktime(&tm); 63683f2a3aaSMatthew Dillon } 63783f2a3aaSMatthew Dillon bzero(linkbuf, sizeof(linkbuf)); 63883f2a3aaSMatthew Dillon if (readlink(fpath, linkbuf, sizeof(linkbuf) - 1) > 0 && 63983f2a3aaSMatthew Dillon (ptr = strrchr(linkbuf, '@')) != NULL && 64083f2a3aaSMatthew Dillon ptr > linkbuf && ptr[-1] == '@') { 64183f2a3aaSMatthew Dillon tid = strtoull(ptr + 1, NULL, 16); 64283f2a3aaSMatthew Dillon } 64383f2a3aaSMatthew Dillon if (t != (time_t)-1 && tid != (hammer_tid_t)(int64_t)-1) { 64483f2a3aaSMatthew Dillon snap = &snapshot->snaps[snapshot->count]; 64583f2a3aaSMatthew Dillon bzero(snap, sizeof(*snap)); 64683f2a3aaSMatthew Dillon snap->tid = tid; 64783f2a3aaSMatthew Dillon snap->ts = (u_int64_t)t * 1000000ULL; 64883f2a3aaSMatthew Dillon snprintf(snap->label, sizeof(snap->label), 64983f2a3aaSMatthew Dillon "migrated"); 65083f2a3aaSMatthew Dillon ++snapshot->count; 65183f2a3aaSMatthew Dillon } 65283f2a3aaSMatthew Dillon } 65383f2a3aaSMatthew Dillon 65483f2a3aaSMatthew Dillon if ((fpath == NULL && snapshot->count) || 65583f2a3aaSMatthew Dillon snapshot->count == HAMMER_SNAPS_PER_IOCTL) { 65683f2a3aaSMatthew Dillon printf(" (%d snapshots)", snapshot->count); 65783f2a3aaSMatthew Dillon again: 65883f2a3aaSMatthew Dillon if (ioctl(fd, HAMMERIOC_ADD_SNAPSHOT, snapshot) < 0) { 65983f2a3aaSMatthew Dillon printf(" Ioctl to migrate snapshots failed: %s\n", 66083f2a3aaSMatthew Dillon strerror(errno)); 66183f2a3aaSMatthew Dillon } else if (snapshot->head.error == EALREADY) { 66283f2a3aaSMatthew Dillon ++snapshot->index; 66383f2a3aaSMatthew Dillon goto again; 66483f2a3aaSMatthew Dillon } else if (snapshot->head.error) { 665068da2c2SMatthew Dillon printf(" Ioctl to migrate snapshots failed: %s\n", 66683f2a3aaSMatthew Dillon strerror(snapshot->head.error)); 66783f2a3aaSMatthew Dillon } 66883f2a3aaSMatthew Dillon printf("index %d\n", snapshot->index); 66983f2a3aaSMatthew Dillon snapshot->index = 0; 67083f2a3aaSMatthew Dillon snapshot->count = 0; 67183f2a3aaSMatthew Dillon snapshot->head.error = 0; 67283f2a3aaSMatthew Dillon } 67383f2a3aaSMatthew Dillon } 67483f2a3aaSMatthew Dillon 6756a6e350fSMatthew Dillon static 6766a6e350fSMatthew Dillon int 6776a6e350fSMatthew Dillon strtosecs(char *ptr) 6786a6e350fSMatthew Dillon { 6796a6e350fSMatthew Dillon int val; 6806a6e350fSMatthew Dillon 6816a6e350fSMatthew Dillon val = strtol(ptr, &ptr, 0); 6826a6e350fSMatthew Dillon switch(*ptr) { 6836a6e350fSMatthew Dillon case 'd': 6846a6e350fSMatthew Dillon val *= 24; 6856a6e350fSMatthew Dillon /* fall through */ 6866a6e350fSMatthew Dillon case 'h': 6876a6e350fSMatthew Dillon val *= 60; 6886a6e350fSMatthew Dillon /* fall through */ 6896a6e350fSMatthew Dillon case 'm': 6906a6e350fSMatthew Dillon val *= 60; 6916a6e350fSMatthew Dillon /* fall through */ 6926a6e350fSMatthew Dillon case 's': 6936a6e350fSMatthew Dillon break; 6946a6e350fSMatthew Dillon default: 6956a6e350fSMatthew Dillon errx(1, "illegal suffix converting %s\n", ptr); 6966a6e350fSMatthew Dillon break; 6976a6e350fSMatthew Dillon } 6986a6e350fSMatthew Dillon return(val); 6996a6e350fSMatthew Dillon } 7006a6e350fSMatthew Dillon 7016a6e350fSMatthew Dillon static const char * 7026a6e350fSMatthew Dillon dividing_slash(const char *path) 7036a6e350fSMatthew Dillon { 7046a6e350fSMatthew Dillon int len = strlen(path); 7056a6e350fSMatthew Dillon if (len && path[len-1] == '/') 7066a6e350fSMatthew Dillon return(""); 7076a6e350fSMatthew Dillon else 7086a6e350fSMatthew Dillon return("/"); 7096a6e350fSMatthew Dillon } 7106a6e350fSMatthew Dillon 7116a6e350fSMatthew Dillon /* 7126a6e350fSMatthew Dillon * Check whether the desired period has elapsed since the last successful 7136a6e350fSMatthew Dillon * run. The run may take a while and cross a boundary so we remember the 7146a6e350fSMatthew Dillon * current time_t so we can save it later on. 7156a6e350fSMatthew Dillon * 7166a6e350fSMatthew Dillon * Periods in minutes, hours, or days are assumed to have been crossed 7176a6e350fSMatthew Dillon * if the local time crosses a minute, hour, or day boundary regardless 7186a6e350fSMatthew Dillon * of how close the last operation actually was. 7196a6e350fSMatthew Dillon */ 7206a6e350fSMatthew Dillon static int 7216a6e350fSMatthew Dillon check_period(const char *snapshots_path, const char *cmd, int arg1, 7226a6e350fSMatthew Dillon time_t *savep) 7236a6e350fSMatthew Dillon { 7246a6e350fSMatthew Dillon char *check_path; 7256a6e350fSMatthew Dillon struct tm tp1; 7266a6e350fSMatthew Dillon struct tm tp2; 7276a6e350fSMatthew Dillon FILE *fp; 7286a6e350fSMatthew Dillon time_t baset, lastt; 7296a6e350fSMatthew Dillon char buf[256]; 7306a6e350fSMatthew Dillon 7316a6e350fSMatthew Dillon time(savep); 7326a6e350fSMatthew Dillon localtime_r(savep, &tp1); 7336a6e350fSMatthew Dillon 7346a6e350fSMatthew Dillon /* 7356a6e350fSMatthew Dillon * Retrieve the start time of the last successful operation. 7366a6e350fSMatthew Dillon */ 7376a6e350fSMatthew Dillon asprintf(&check_path, "%s/.%s.period", snapshots_path, cmd); 7386a6e350fSMatthew Dillon fp = fopen(check_path, "r"); 7396a6e350fSMatthew Dillon free(check_path); 7406a6e350fSMatthew Dillon if (fp == NULL) 7416a6e350fSMatthew Dillon return(1); 7426a6e350fSMatthew Dillon if (fgets(buf, sizeof(buf), fp) == NULL) { 7436a6e350fSMatthew Dillon fclose(fp); 7446a6e350fSMatthew Dillon return(1); 7456a6e350fSMatthew Dillon } 7466a6e350fSMatthew Dillon fclose(fp); 7476a6e350fSMatthew Dillon 7486a6e350fSMatthew Dillon lastt = strtol(buf, NULL, 0); 7496a6e350fSMatthew Dillon localtime_r(&lastt, &tp2); 7506a6e350fSMatthew Dillon 7516a6e350fSMatthew Dillon /* 7526a6e350fSMatthew Dillon * Normalize the times. e.g. if asked to do something on a 1-day 7536a6e350fSMatthew Dillon * interval the operation will be performed as soon as the day 7546a6e350fSMatthew Dillon * turns over relative to the previous operation, even if the previous 7556a6e350fSMatthew Dillon * operation ran a few seconds ago just before midnight. 7566a6e350fSMatthew Dillon */ 7576a6e350fSMatthew Dillon if (arg1 % 60 == 0) { 7586a6e350fSMatthew Dillon tp1.tm_sec = 0; 7596a6e350fSMatthew Dillon tp2.tm_sec = 0; 7606a6e350fSMatthew Dillon } 7616a6e350fSMatthew Dillon if (arg1 % (60 * 60) == 0) { 7626a6e350fSMatthew Dillon tp1.tm_min = 0; 7636a6e350fSMatthew Dillon tp2.tm_min = 0; 7646a6e350fSMatthew Dillon } 7656a6e350fSMatthew Dillon if (arg1 % (24 * 60 * 60) == 0) { 7666a6e350fSMatthew Dillon tp1.tm_hour = 0; 7676a6e350fSMatthew Dillon tp2.tm_hour = 0; 7686a6e350fSMatthew Dillon } 7696a6e350fSMatthew Dillon 7706a6e350fSMatthew Dillon baset = mktime(&tp1); 7716a6e350fSMatthew Dillon lastt = mktime(&tp2); 7726a6e350fSMatthew Dillon 7736a6e350fSMatthew Dillon #if 0 7746a6e350fSMatthew Dillon printf("%lld vs %lld\n", (long long)(baset - lastt), (long long)arg1); 7756a6e350fSMatthew Dillon #endif 7766a6e350fSMatthew Dillon 7776a6e350fSMatthew Dillon if ((int)(baset - lastt) >= arg1) 7786a6e350fSMatthew Dillon return(1); 7796a6e350fSMatthew Dillon return(0); 7806a6e350fSMatthew Dillon } 7816a6e350fSMatthew Dillon 7826a6e350fSMatthew Dillon /* 7836a6e350fSMatthew Dillon * Store the start time of the last successful operation. 7846a6e350fSMatthew Dillon */ 7856a6e350fSMatthew Dillon static void 7866a6e350fSMatthew Dillon save_period(const char *snapshots_path, const char *cmd, 7876a6e350fSMatthew Dillon time_t savet) 7886a6e350fSMatthew Dillon { 7896a6e350fSMatthew Dillon char *ocheck_path; 7906a6e350fSMatthew Dillon char *ncheck_path; 7916a6e350fSMatthew Dillon FILE *fp; 7926a6e350fSMatthew Dillon 7936a6e350fSMatthew Dillon asprintf(&ocheck_path, "%s/.%s.period", snapshots_path, cmd); 7946a6e350fSMatthew Dillon asprintf(&ncheck_path, "%s/.%s.period.new", snapshots_path, cmd); 7956a6e350fSMatthew Dillon fp = fopen(ncheck_path, "w"); 7963b9fbdeaSMatthew Dillon if (fp) { 7976a6e350fSMatthew Dillon fprintf(fp, "0x%08llx\n", (long long)savet); 7986a6e350fSMatthew Dillon if (fclose(fp) == 0) 7996a6e350fSMatthew Dillon rename(ncheck_path, ocheck_path); 8006a6e350fSMatthew Dillon remove(ncheck_path); 8013b9fbdeaSMatthew Dillon } else { 8023b9fbdeaSMatthew Dillon fprintf(stderr, "hammer: Unable to create period-file %s: %s\n", 8033b9fbdeaSMatthew Dillon ncheck_path, strerror(errno)); 8043b9fbdeaSMatthew Dillon } 8056a6e350fSMatthew Dillon } 8066a6e350fSMatthew Dillon 807ff1c9800SMatthew Dillon /* 808ff1c9800SMatthew Dillon * Simply count the number of softlinks in the snapshots dir 809ff1c9800SMatthew Dillon */ 810c6c298a7SMatthew Dillon static int 81183f2a3aaSMatthew Dillon check_softlinks(int fd, int new_config, const char *snapshots_path) 812c6c298a7SMatthew Dillon { 813c6c298a7SMatthew Dillon struct dirent *den; 814c6c298a7SMatthew Dillon struct stat st; 815c6c298a7SMatthew Dillon DIR *dir; 816c6c298a7SMatthew Dillon char *fpath; 817c6c298a7SMatthew Dillon int res = 0; 818c6c298a7SMatthew Dillon 81983f2a3aaSMatthew Dillon /* 82083f2a3aaSMatthew Dillon * Old-style softlink-based snapshots 82183f2a3aaSMatthew Dillon */ 822c6c298a7SMatthew Dillon if ((dir = opendir(snapshots_path)) != NULL) { 823c6c298a7SMatthew Dillon while ((den = readdir(dir)) != NULL) { 824c6c298a7SMatthew Dillon if (den->d_name[0] == '.') 825c6c298a7SMatthew Dillon continue; 826c6c298a7SMatthew Dillon asprintf(&fpath, "%s/%s", snapshots_path, den->d_name); 827c6c298a7SMatthew Dillon if (lstat(fpath, &st) == 0 && S_ISLNK(st.st_mode)) 828c6c298a7SMatthew Dillon ++res; 829c6c298a7SMatthew Dillon free(fpath); 830c6c298a7SMatthew Dillon } 831c6c298a7SMatthew Dillon closedir(dir); 832c6c298a7SMatthew Dillon } 83383f2a3aaSMatthew Dillon 83483f2a3aaSMatthew Dillon /* 83583f2a3aaSMatthew Dillon * New-style snapshots are stored as filesystem meta-data, 83683f2a3aaSMatthew Dillon * count those too. 83783f2a3aaSMatthew Dillon */ 83883f2a3aaSMatthew Dillon if (new_config) { 83983f2a3aaSMatthew Dillon struct hammer_ioc_snapshot snapshot; 84083f2a3aaSMatthew Dillon 84183f2a3aaSMatthew Dillon bzero(&snapshot, sizeof(snapshot)); 84283f2a3aaSMatthew Dillon do { 84383f2a3aaSMatthew Dillon if (ioctl(fd, HAMMERIOC_GET_SNAPSHOT, &snapshot) < 0) { 84483f2a3aaSMatthew Dillon err(2, "hammer cleanup: check_softlink " 84583f2a3aaSMatthew Dillon "snapshot error"); 84683f2a3aaSMatthew Dillon /* not reached */ 84783f2a3aaSMatthew Dillon } 84883f2a3aaSMatthew Dillon res += snapshot.count; 84983f2a3aaSMatthew Dillon } while (snapshot.head.error == 0 && snapshot.count); 85083f2a3aaSMatthew Dillon } 851c6c298a7SMatthew Dillon return (res); 852c6c298a7SMatthew Dillon } 853c6c298a7SMatthew Dillon 8546a6e350fSMatthew Dillon /* 855ff1c9800SMatthew Dillon * Clean up expired softlinks in the snapshots dir 856ff1c9800SMatthew Dillon */ 857ff1c9800SMatthew Dillon static void 85883f2a3aaSMatthew Dillon cleanup_softlinks(int fd, int new_config, 85983f2a3aaSMatthew Dillon const char *snapshots_path, int arg2, char *arg3) 860ff1c9800SMatthew Dillon { 861ff1c9800SMatthew Dillon struct dirent *den; 862ff1c9800SMatthew Dillon struct stat st; 863ff1c9800SMatthew Dillon DIR *dir; 864ff1c9800SMatthew Dillon char *fpath; 8655e435c92SMatthew Dillon int anylink = 0; 8665e435c92SMatthew Dillon 86709e1b0d6SMatthew Dillon if (arg3 != NULL && strstr(arg3, "any") != NULL) 8685e435c92SMatthew Dillon anylink = 1; 869ff1c9800SMatthew Dillon 870ff1c9800SMatthew Dillon if ((dir = opendir(snapshots_path)) != NULL) { 871ff1c9800SMatthew Dillon while ((den = readdir(dir)) != NULL) { 872ff1c9800SMatthew Dillon if (den->d_name[0] == '.') 873ff1c9800SMatthew Dillon continue; 874ff1c9800SMatthew Dillon asprintf(&fpath, "%s/%s", snapshots_path, den->d_name); 875ff1c9800SMatthew Dillon if (lstat(fpath, &st) == 0 && S_ISLNK(st.st_mode) && 8765e435c92SMatthew Dillon (anylink || strncmp(den->d_name, "snap-", 5) == 0) 8775e435c92SMatthew Dillon ) { 878ff1c9800SMatthew Dillon if (check_expired(den->d_name, arg2)) { 879ff1c9800SMatthew Dillon if (VerboseOpt) { 880ff1c9800SMatthew Dillon printf(" expire %s\n", 881ff1c9800SMatthew Dillon fpath); 882ff1c9800SMatthew Dillon } 883ff1c9800SMatthew Dillon remove(fpath); 884ff1c9800SMatthew Dillon } 885ff1c9800SMatthew Dillon } 886ff1c9800SMatthew Dillon free(fpath); 887ff1c9800SMatthew Dillon } 888ff1c9800SMatthew Dillon closedir(dir); 889ff1c9800SMatthew Dillon } 89083f2a3aaSMatthew Dillon 89183f2a3aaSMatthew Dillon /* 89283f2a3aaSMatthew Dillon * New-style snapshots are stored as filesystem meta-data, 89383f2a3aaSMatthew Dillon * count those too. 89483f2a3aaSMatthew Dillon */ 89583f2a3aaSMatthew Dillon if (new_config) { 89683f2a3aaSMatthew Dillon struct hammer_ioc_snapshot snapshot; 89783f2a3aaSMatthew Dillon struct hammer_ioc_snapshot dsnapshot; 89883f2a3aaSMatthew Dillon struct hammer_snapshot_data *snap; 89983f2a3aaSMatthew Dillon struct tm *tp; 90083f2a3aaSMatthew Dillon time_t t; 90183f2a3aaSMatthew Dillon char snapts[32]; 90283f2a3aaSMatthew Dillon u_int32_t i; 90383f2a3aaSMatthew Dillon 90483f2a3aaSMatthew Dillon bzero(&snapshot, sizeof(snapshot)); 90583f2a3aaSMatthew Dillon bzero(&dsnapshot, sizeof(dsnapshot)); 90683f2a3aaSMatthew Dillon do { 90783f2a3aaSMatthew Dillon if (ioctl(fd, HAMMERIOC_GET_SNAPSHOT, &snapshot) < 0) { 90883f2a3aaSMatthew Dillon err(2, "hammer cleanup: check_softlink " 90983f2a3aaSMatthew Dillon "snapshot error"); 91083f2a3aaSMatthew Dillon /* not reached */ 91183f2a3aaSMatthew Dillon } 91283f2a3aaSMatthew Dillon for (i = 0; i < snapshot.count; ++i) { 91383f2a3aaSMatthew Dillon snap = &snapshot.snaps[i]; 91483f2a3aaSMatthew Dillon t = time(NULL) - snap->ts / 1000000ULL; 915068da2c2SMatthew Dillon if ((int)t > arg2 && snap->tid != 0) { 91683f2a3aaSMatthew Dillon dsnapshot.snaps[dsnapshot.count++] = 91783f2a3aaSMatthew Dillon *snap; 91883f2a3aaSMatthew Dillon } 91983f2a3aaSMatthew Dillon if ((int)t > arg2 && VerboseOpt) { 92083f2a3aaSMatthew Dillon tp = localtime(&t); 92183f2a3aaSMatthew Dillon strftime(snapts, sizeof(snapts), 92283f2a3aaSMatthew Dillon "%Y-%m-%d %H:%M:%S %Z", tp); 92383f2a3aaSMatthew Dillon printf(" expire 0x%016jx %s %s\n", 92483f2a3aaSMatthew Dillon (uintmax_t)snap->tid, 92583f2a3aaSMatthew Dillon snapts, 92683f2a3aaSMatthew Dillon snap->label); 92783f2a3aaSMatthew Dillon } 92883f2a3aaSMatthew Dillon if (dsnapshot.count == HAMMER_SNAPS_PER_IOCTL) { 92983f2a3aaSMatthew Dillon if (ioctl(fd, HAMMERIOC_DEL_SNAPSHOT, &dsnapshot) < 0) { 930068da2c2SMatthew Dillon printf(" Ioctl to delete snapshots failed: %s index %d\n", strerror(errno), dsnapshot.index); 93183f2a3aaSMatthew Dillon } else if (dsnapshot.head.error) { 93283f2a3aaSMatthew Dillon printf(" Ioctl to delete snapshots failed: %s\n", strerror(dsnapshot.head.error)); 933068da2c2SMatthew Dillon exit(1); 93483f2a3aaSMatthew Dillon } 935068da2c2SMatthew Dillon dsnapshot.index = 0; 93683f2a3aaSMatthew Dillon dsnapshot.count = 0; 93783f2a3aaSMatthew Dillon dsnapshot.head.error = 0; 93883f2a3aaSMatthew Dillon } 93983f2a3aaSMatthew Dillon } 94083f2a3aaSMatthew Dillon } while (snapshot.head.error == 0 && snapshot.count); 94183f2a3aaSMatthew Dillon 94283f2a3aaSMatthew Dillon if (dsnapshot.count) { 94383f2a3aaSMatthew Dillon if (ioctl(fd, HAMMERIOC_DEL_SNAPSHOT, &dsnapshot) < 0) { 94483f2a3aaSMatthew Dillon printf(" Ioctl to delete snapshots failed: %s\n", strerror(errno)); 94583f2a3aaSMatthew Dillon } else if (dsnapshot.head.error) { 94683f2a3aaSMatthew Dillon printf(" Ioctl to delete snapshots failed: %s\n", strerror(dsnapshot.head.error)); 94783f2a3aaSMatthew Dillon } 94883f2a3aaSMatthew Dillon dsnapshot.count = 0; 949068da2c2SMatthew Dillon dsnapshot.index = 0; 95083f2a3aaSMatthew Dillon dsnapshot.head.error = 0; 95183f2a3aaSMatthew Dillon } 95283f2a3aaSMatthew Dillon } 953ff1c9800SMatthew Dillon } 954ff1c9800SMatthew Dillon 955ff1c9800SMatthew Dillon /* 956ff1c9800SMatthew Dillon * Take a softlink path in the form snap-yyyymmdd-hhmm and the 957ff1c9800SMatthew Dillon * expiration in seconds (arg2) and return non-zero if the softlink 958ff1c9800SMatthew Dillon * has expired. 959ff1c9800SMatthew Dillon */ 960ff1c9800SMatthew Dillon static int 961ff1c9800SMatthew Dillon check_expired(const char *fpath, int arg2) 962ff1c9800SMatthew Dillon { 963ff1c9800SMatthew Dillon struct tm tm; 964ff1c9800SMatthew Dillon time_t t; 965ff1c9800SMatthew Dillon int year; 966ff1c9800SMatthew Dillon int month; 9675e435c92SMatthew Dillon int day = 0; 9685e435c92SMatthew Dillon int hour = 0; 9695e435c92SMatthew Dillon int minute = 0; 970ff1c9800SMatthew Dillon int r; 971ff1c9800SMatthew Dillon 9725e435c92SMatthew Dillon while (*fpath && *fpath != '-' && *fpath != '.') 9735e435c92SMatthew Dillon ++fpath; 9745e435c92SMatthew Dillon if (*fpath) 9755e435c92SMatthew Dillon ++fpath; 9765e435c92SMatthew Dillon 9775e435c92SMatthew Dillon r = sscanf(fpath, "%4d%2d%2d-%2d%2d", 978ff1c9800SMatthew Dillon &year, &month, &day, &hour, &minute); 9795e435c92SMatthew Dillon 9805e435c92SMatthew Dillon if (r >= 3) { 981ff1c9800SMatthew Dillon bzero(&tm, sizeof(tm)); 982ff1c9800SMatthew Dillon tm.tm_isdst = -1; 983ff1c9800SMatthew Dillon tm.tm_min = minute; 984ff1c9800SMatthew Dillon tm.tm_hour = hour; 985ff1c9800SMatthew Dillon tm.tm_mday = day; 986ff1c9800SMatthew Dillon tm.tm_mon = month - 1; 987ff1c9800SMatthew Dillon tm.tm_year = year - 1900; 9885e435c92SMatthew Dillon t = mktime(&tm); 9895e435c92SMatthew Dillon if (t == (time_t)-1) 9905e435c92SMatthew Dillon return(0); 9915e435c92SMatthew Dillon t = time(NULL) - t; 992ff1c9800SMatthew Dillon if ((int)t > arg2) 993ff1c9800SMatthew Dillon return(1); 994ff1c9800SMatthew Dillon } 995ff1c9800SMatthew Dillon return(0); 996ff1c9800SMatthew Dillon } 997ff1c9800SMatthew Dillon 998ff1c9800SMatthew Dillon /* 9996a6e350fSMatthew Dillon * Issue a snapshot. 10006a6e350fSMatthew Dillon */ 10016a6e350fSMatthew Dillon static int 1002b5ec5ad4SMatthew Dillon create_snapshot(const char *path, const char *snapshots_path) 10036a6e350fSMatthew Dillon { 10046a6e350fSMatthew Dillon int r; 10056a6e350fSMatthew Dillon 1006ff1c9800SMatthew Dillon runcmd(&r, "hammer snapshot %s %s", path, snapshots_path); 10076a6e350fSMatthew Dillon return(r); 10086a6e350fSMatthew Dillon } 10096a6e350fSMatthew Dillon 10106a6e350fSMatthew Dillon static int 10116a6e350fSMatthew Dillon cleanup_prune(const char *path __unused, const char *snapshots_path, 1012c6c298a7SMatthew Dillon int arg1 __unused, int arg2, int snapshots_disabled) 10136a6e350fSMatthew Dillon { 1014c6c298a7SMatthew Dillon /* 1015c6c298a7SMatthew Dillon * If snapshots have been disabled run prune-everything instead 1016c6c298a7SMatthew Dillon * of prune. 1017c6c298a7SMatthew Dillon */ 1018c6c298a7SMatthew Dillon if (snapshots_disabled && arg2) { 1019c6c298a7SMatthew Dillon runcmd(NULL, "hammer -c %s/.prune.cycle -t %d prune-everything %s", 1020c6c298a7SMatthew Dillon snapshots_path, arg2, path); 1021c6c298a7SMatthew Dillon } else if (snapshots_disabled) { 1022c6c298a7SMatthew Dillon runcmd(NULL, "hammer prune-everything %s", path); 1023c6c298a7SMatthew Dillon } else if (arg2) { 10246a6e350fSMatthew Dillon runcmd(NULL, "hammer -c %s/.prune.cycle -t %d prune %s", 10256a6e350fSMatthew Dillon snapshots_path, arg2, snapshots_path); 10266a6e350fSMatthew Dillon } else { 10276a6e350fSMatthew Dillon runcmd(NULL, "hammer prune %s", snapshots_path); 10286a6e350fSMatthew Dillon } 10296a6e350fSMatthew Dillon return(0); 10306a6e350fSMatthew Dillon } 10316a6e350fSMatthew Dillon 10326a6e350fSMatthew Dillon static int 10330b8bd7daSMatthew Dillon cleanup_rebalance(const char *path, const char *snapshots_path, 10340b8bd7daSMatthew Dillon int arg1 __unused, int arg2) 10350b8bd7daSMatthew Dillon { 10360b8bd7daSMatthew Dillon if (VerboseOpt == 0) { 10370b8bd7daSMatthew Dillon printf("."); 10380b8bd7daSMatthew Dillon fflush(stdout); 10390b8bd7daSMatthew Dillon } 10400b8bd7daSMatthew Dillon 10410b8bd7daSMatthew Dillon runcmd(NULL, 10420b8bd7daSMatthew Dillon "hammer -c %s/.rebalance.cycle -t %d rebalance %s", 10430b8bd7daSMatthew Dillon snapshots_path, arg2, path); 10440b8bd7daSMatthew Dillon if (VerboseOpt == 0) { 10450b8bd7daSMatthew Dillon printf("."); 10460b8bd7daSMatthew Dillon fflush(stdout); 10470b8bd7daSMatthew Dillon } 10480b8bd7daSMatthew Dillon if (VerboseOpt == 0) 10490b8bd7daSMatthew Dillon printf("\n"); 10500b8bd7daSMatthew Dillon return(0); 10510b8bd7daSMatthew Dillon } 10520b8bd7daSMatthew Dillon 10530b8bd7daSMatthew Dillon static int 10546a6e350fSMatthew Dillon cleanup_reblock(const char *path, const char *snapshots_path, 10556a6e350fSMatthew Dillon int arg1 __unused, int arg2) 10566a6e350fSMatthew Dillon { 10576a6e350fSMatthew Dillon if (VerboseOpt == 0) { 10586a6e350fSMatthew Dillon printf("."); 10596a6e350fSMatthew Dillon fflush(stdout); 10606a6e350fSMatthew Dillon } 1061797a0b63SMatthew Dillon 1062797a0b63SMatthew Dillon /* 1063797a0b63SMatthew Dillon * When reblocking the B-Tree always reblock everything in normal 1064797a0b63SMatthew Dillon * mode. 1065797a0b63SMatthew Dillon */ 10666a6e350fSMatthew Dillon runcmd(NULL, 1067797a0b63SMatthew Dillon "hammer -c %s/.reblock-1.cycle -t %d reblock-btree %s", 10686a6e350fSMatthew Dillon snapshots_path, arg2, path); 10696a6e350fSMatthew Dillon if (VerboseOpt == 0) { 10706a6e350fSMatthew Dillon printf("."); 10716a6e350fSMatthew Dillon fflush(stdout); 10726a6e350fSMatthew Dillon } 1073797a0b63SMatthew Dillon 1074797a0b63SMatthew Dillon /* 1075797a0b63SMatthew Dillon * When reblocking the inodes always reblock everything in normal 1076797a0b63SMatthew Dillon * mode. 1077797a0b63SMatthew Dillon */ 10786a6e350fSMatthew Dillon runcmd(NULL, 1079797a0b63SMatthew Dillon "hammer -c %s/.reblock-2.cycle -t %d reblock-inodes %s", 10806a6e350fSMatthew Dillon snapshots_path, arg2, path); 10816a6e350fSMatthew Dillon if (VerboseOpt == 0) { 10826a6e350fSMatthew Dillon printf("."); 10836a6e350fSMatthew Dillon fflush(stdout); 10846a6e350fSMatthew Dillon } 1085797a0b63SMatthew Dillon 1086797a0b63SMatthew Dillon /* 1087797a0b63SMatthew Dillon * When reblocking the directories always reblock everything in normal 1088797a0b63SMatthew Dillon * mode. 1089797a0b63SMatthew Dillon */ 1090797a0b63SMatthew Dillon runcmd(NULL, 1091797a0b63SMatthew Dillon "hammer -c %s/.reblock-4.cycle -t %d reblock-dirs %s", 1092797a0b63SMatthew Dillon snapshots_path, arg2, path); 1093797a0b63SMatthew Dillon if (VerboseOpt == 0) { 1094797a0b63SMatthew Dillon printf("."); 1095797a0b63SMatthew Dillon fflush(stdout); 1096797a0b63SMatthew Dillon } 1097797a0b63SMatthew Dillon 1098797a0b63SMatthew Dillon /* 1099797a0b63SMatthew Dillon * Do not reblock all the data in normal mode. 1100797a0b63SMatthew Dillon */ 11016a6e350fSMatthew Dillon runcmd(NULL, 11026a6e350fSMatthew Dillon "hammer -c %s/.reblock-3.cycle -t %d reblock-data %s 95", 11036a6e350fSMatthew Dillon snapshots_path, arg2, path); 11046a6e350fSMatthew Dillon if (VerboseOpt == 0) 11056a6e350fSMatthew Dillon printf("\n"); 11066a6e350fSMatthew Dillon return(0); 11076a6e350fSMatthew Dillon } 11086a6e350fSMatthew Dillon 11096a6e350fSMatthew Dillon static int 11106a6e350fSMatthew Dillon cleanup_recopy(const char *path, const char *snapshots_path, 11116a6e350fSMatthew Dillon int arg1 __unused, int arg2) 11126a6e350fSMatthew Dillon { 11136a6e350fSMatthew Dillon if (VerboseOpt == 0) { 11146a6e350fSMatthew Dillon printf("."); 11156a6e350fSMatthew Dillon fflush(stdout); 11166a6e350fSMatthew Dillon } 11176a6e350fSMatthew Dillon runcmd(NULL, 11186a6e350fSMatthew Dillon "hammer -c %s/.recopy-1.cycle -t %d reblock-btree %s", 11196a6e350fSMatthew Dillon snapshots_path, arg2, path); 11206a6e350fSMatthew Dillon if (VerboseOpt == 0) { 11216a6e350fSMatthew Dillon printf("."); 11226a6e350fSMatthew Dillon fflush(stdout); 11236a6e350fSMatthew Dillon } 11246a6e350fSMatthew Dillon runcmd(NULL, 11256a6e350fSMatthew Dillon "hammer -c %s/.recopy-2.cycle -t %d reblock-inodes %s", 11266a6e350fSMatthew Dillon snapshots_path, arg2, path); 11276a6e350fSMatthew Dillon if (VerboseOpt == 0) { 11286a6e350fSMatthew Dillon printf("."); 11296a6e350fSMatthew Dillon fflush(stdout); 11306a6e350fSMatthew Dillon } 11316a6e350fSMatthew Dillon runcmd(NULL, 1132797a0b63SMatthew Dillon "hammer -c %s/.recopy-4.cycle -t %d reblock-dirs %s", 1133797a0b63SMatthew Dillon snapshots_path, arg2, path); 1134797a0b63SMatthew Dillon if (VerboseOpt == 0) { 1135797a0b63SMatthew Dillon printf("."); 1136797a0b63SMatthew Dillon fflush(stdout); 1137797a0b63SMatthew Dillon } 1138797a0b63SMatthew Dillon runcmd(NULL, 11396a6e350fSMatthew Dillon "hammer -c %s/.recopy-3.cycle -t %d reblock-data %s", 11406a6e350fSMatthew Dillon snapshots_path, arg2, path); 11416a6e350fSMatthew Dillon if (VerboseOpt == 0) 11426a6e350fSMatthew Dillon printf("\n"); 11436a6e350fSMatthew Dillon return(0); 11446a6e350fSMatthew Dillon } 11456a6e350fSMatthew Dillon 11466a6e350fSMatthew Dillon static 11476a6e350fSMatthew Dillon void 11486a6e350fSMatthew Dillon runcmd(int *resp, const char *ctl, ...) 11496a6e350fSMatthew Dillon { 11506a6e350fSMatthew Dillon va_list va; 11516a6e350fSMatthew Dillon char *cmd; 11526a6e350fSMatthew Dillon char *arg; 11536a6e350fSMatthew Dillon char **av; 11546a6e350fSMatthew Dillon int n; 11556a6e350fSMatthew Dillon int nmax; 11566a6e350fSMatthew Dillon int res; 11576a6e350fSMatthew Dillon pid_t pid; 11586a6e350fSMatthew Dillon 11596a6e350fSMatthew Dillon /* 11606a6e350fSMatthew Dillon * Generate the command 11616a6e350fSMatthew Dillon */ 11626a6e350fSMatthew Dillon va_start(va, ctl); 11636a6e350fSMatthew Dillon vasprintf(&cmd, ctl, va); 11646a6e350fSMatthew Dillon va_end(va); 11656a6e350fSMatthew Dillon if (VerboseOpt) 11666a6e350fSMatthew Dillon printf(" %s\n", cmd); 11676a6e350fSMatthew Dillon 11686a6e350fSMatthew Dillon /* 11696a6e350fSMatthew Dillon * Break us down into arguments. We do not just use system() here 11706a6e350fSMatthew Dillon * because it blocks SIGINT and friends. 11716a6e350fSMatthew Dillon */ 11726a6e350fSMatthew Dillon n = 0; 11736a6e350fSMatthew Dillon nmax = 16; 11746a6e350fSMatthew Dillon av = malloc(sizeof(char *) * nmax); 11756a6e350fSMatthew Dillon 11766a6e350fSMatthew Dillon for (arg = strtok(cmd, WS); arg; arg = strtok(NULL, WS)) { 1177c453712aSMatthew Dillon if (n == nmax - 1) { 11786a6e350fSMatthew Dillon nmax += 16; 11796a6e350fSMatthew Dillon av = realloc(av, sizeof(char *) * nmax); 11806a6e350fSMatthew Dillon } 11816a6e350fSMatthew Dillon av[n++] = arg; 11826a6e350fSMatthew Dillon } 1183c453712aSMatthew Dillon av[n++] = NULL; 11846a6e350fSMatthew Dillon 11856a6e350fSMatthew Dillon /* 11866a6e350fSMatthew Dillon * Run the command. 11876a6e350fSMatthew Dillon */ 1188445faa69SMatthew Dillon RunningIoctl = 1; 11896a6e350fSMatthew Dillon if ((pid = fork()) == 0) { 11906a6e350fSMatthew Dillon if (VerboseOpt < 2) { 11916a6e350fSMatthew Dillon int fd = open("/dev/null", O_RDWR); 11926a6e350fSMatthew Dillon dup2(fd, 1); 11936a6e350fSMatthew Dillon close(fd); 11946a6e350fSMatthew Dillon } 11956a6e350fSMatthew Dillon execvp(av[0], av); 11966a6e350fSMatthew Dillon _exit(127); 11976a6e350fSMatthew Dillon } else if (pid < 0) { 11986a6e350fSMatthew Dillon res = 127; 11996a6e350fSMatthew Dillon } else { 12006a6e350fSMatthew Dillon int status; 1201445faa69SMatthew Dillon 12026a6e350fSMatthew Dillon while (waitpid(pid, &status, 0) != pid) 12036a6e350fSMatthew Dillon ; 12046a6e350fSMatthew Dillon res = WEXITSTATUS(status); 12056a6e350fSMatthew Dillon } 1206445faa69SMatthew Dillon RunningIoctl = 0; 1207445faa69SMatthew Dillon if (DidInterrupt) 1208445faa69SMatthew Dillon _exit(1); 12096a6e350fSMatthew Dillon 12106a6e350fSMatthew Dillon free(cmd); 12116a6e350fSMatthew Dillon free(av); 12126a6e350fSMatthew Dillon if (resp) 12136a6e350fSMatthew Dillon *resp = res; 12146a6e350fSMatthew Dillon } 1215