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 */ 346a6e350fSMatthew Dillon /* 3516265794SThomas Nikolajsen * Clean up specific HAMMER filesystems or all HAMMER filesystems. 366a6e350fSMatthew Dillon * 37f6532f03SThomas Nikolajsen * If no filesystems are specified any HAMMER- or null-mounted hammer PFS's 3816265794SThomas Nikolajsen * are cleaned. 396a6e350fSMatthew Dillon * 4016265794SThomas Nikolajsen * Each HAMMER filesystem may contain a configuration file. If no 4116265794SThomas Nikolajsen * configuration file is present one will be created with the following 426a6e350fSMatthew Dillon * defaults: 436a6e350fSMatthew Dillon * 445e435c92SMatthew Dillon * snapshots 1d 60d (0d 0d for /tmp, /var/tmp, /usr/obj) 456a6e350fSMatthew Dillon * prune 1d 5m 46f13fea8bSSascha Wildner * rebalance 1d 5m 47bb29b5d8SMatthew Dillon * #dedup 1d 5m (not enabled by default) 486a6e350fSMatthew Dillon * reblock 1d 5m 49f6532f03SThomas Nikolajsen * recopy 30d 10m 506a6e350fSMatthew Dillon * 516a6e350fSMatthew Dillon * All hammer commands create and maintain cycle files in the snapshots 526a6e350fSMatthew Dillon * directory. 5316265794SThomas Nikolajsen * 5416265794SThomas Nikolajsen * For HAMMER version 2- the configuration file is a named 'config' in 5516265794SThomas Nikolajsen * the snapshots directory, which defaults to <pfs>/snapshots. 5616265794SThomas Nikolajsen * For HAMMER version 3+ the configuration file is saved in filesystem 5716265794SThomas Nikolajsen * meta-data. The snapshots directory defaults to /var/hammer/<pfs> 5816265794SThomas Nikolajsen * (/var/hammer/root for root mount). 596a6e350fSMatthew Dillon */ 606a6e350fSMatthew Dillon 61*a360fddeSJohn Marino #include <libutil.h> 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); 84ec2aa11dSMatthew Dillon static void delete_snapshots(int fd, struct hammer_ioc_snapshot *dsnapshot); 85ff1c9800SMatthew Dillon static int check_expired(const char *fpath, int arg2); 866a6e350fSMatthew Dillon 87b5ec5ad4SMatthew Dillon static int create_snapshot(const char *path, const char *snapshots_path); 880b8bd7daSMatthew Dillon static int cleanup_rebalance(const char *path, const char *snapshots_path, 890b8bd7daSMatthew Dillon int arg1, int arg2); 906a6e350fSMatthew Dillon static int cleanup_prune(const char *path, const char *snapshots_path, 91c6c298a7SMatthew Dillon int arg1, int arg2, int snapshots_disabled); 926a6e350fSMatthew Dillon static int cleanup_reblock(const char *path, const char *snapshots_path, 936a6e350fSMatthew Dillon int arg1, int arg2); 946a6e350fSMatthew Dillon static int cleanup_recopy(const char *path, const char *snapshots_path, 956a6e350fSMatthew Dillon int arg1, int arg2); 96bb29b5d8SMatthew Dillon static int cleanup_dedup(const char *path, const char *snapshots_path, 97bb29b5d8SMatthew Dillon int arg1, int arg2); 986a6e350fSMatthew Dillon 99b58f1e66SSascha Wildner static void runcmd(int *resp, const char *ctl, ...) __printflike(2, 3); 1006a6e350fSMatthew Dillon 101b5ec5ad4SMatthew Dillon /* 102b5ec5ad4SMatthew Dillon * WARNING: Do not make the SNAPSHOTS_BASE "/var/snapshots" because 103b5ec5ad4SMatthew Dillon * it will interfere with the older HAMMER VERS < 3 snapshots directory 104b5ec5ad4SMatthew Dillon * for the /var PFS. 105b5ec5ad4SMatthew Dillon */ 106b5ec5ad4SMatthew Dillon #define SNAPSHOTS_BASE "/var/hammer" /* HAMMER VERS >= 3 */ 1076a6e350fSMatthew Dillon #define WS " \t\r\n" 1086a6e350fSMatthew Dillon 109c453712aSMatthew Dillon struct didpfs *FirstPFS; 1106a6e350fSMatthew Dillon 1116a6e350fSMatthew Dillon void 1126a6e350fSMatthew Dillon hammer_cmd_cleanup(char **av, int ac) 1136a6e350fSMatthew Dillon { 114eac446c5SMatthew Dillon char *fstype, *fs, *path; 115eac446c5SMatthew Dillon struct statfs *stfsbuf; 116eac446c5SMatthew Dillon int mntsize, i; 1176a6e350fSMatthew Dillon 1186a6e350fSMatthew Dillon tzset(); 1196a6e350fSMatthew Dillon if (ac == 0) { 120eac446c5SMatthew Dillon mntsize = getmntinfo(&stfsbuf, MNT_NOWAIT); 121eac446c5SMatthew Dillon if (mntsize > 0) { 122eac446c5SMatthew Dillon for (i=0; i < mntsize; i++) { 123eac446c5SMatthew Dillon /* 124eac446c5SMatthew Dillon * We will cleanup in the case fstype is hammer. 125eac446c5SMatthew Dillon * If we have null-mounted PFS, we check the 126eac446c5SMatthew Dillon * mount source. If it looks like a PFS, we 127eac446c5SMatthew Dillon * proceed to cleanup also. 128eac446c5SMatthew Dillon */ 129eac446c5SMatthew Dillon fstype = stfsbuf[i].f_fstypename; 130eac446c5SMatthew Dillon fs = stfsbuf[i].f_mntfromname; 131eac446c5SMatthew Dillon if ((strcmp(fstype, "hammer") == 0) || 132eac446c5SMatthew Dillon ((strcmp(fstype, "null") == 0) && 133cb3c760cSMatthew Dillon (strstr(fs, "/@@0x") != NULL || 134cb3c760cSMatthew Dillon strstr(fs, "/@@-1") != NULL))) { 135eac446c5SMatthew Dillon path = stfsbuf[i].f_mntonname; 1366a6e350fSMatthew Dillon do_cleanup(path); 1376a6e350fSMatthew Dillon } 138574066d3SMatthew Dillon } 139eac446c5SMatthew Dillon } 140eac446c5SMatthew Dillon 1416a6e350fSMatthew Dillon } else { 1426a6e350fSMatthew Dillon while (ac) { 1436a6e350fSMatthew Dillon do_cleanup(*av); 1446a6e350fSMatthew Dillon --ac; 1456a6e350fSMatthew Dillon ++av; 1466a6e350fSMatthew Dillon } 1476a6e350fSMatthew Dillon } 1486a6e350fSMatthew Dillon } 1496a6e350fSMatthew Dillon 1506a6e350fSMatthew Dillon static 1516a6e350fSMatthew Dillon void 1526a6e350fSMatthew Dillon do_cleanup(const char *path) 1536a6e350fSMatthew Dillon { 1546a6e350fSMatthew Dillon struct hammer_ioc_pseudofs_rw pfs; 15583f2a3aaSMatthew Dillon struct hammer_ioc_config config; 15683f2a3aaSMatthew Dillon struct hammer_ioc_version version; 1576a6e350fSMatthew Dillon union hammer_ioc_mrecord_any mrec_tmp; 15882893617SThomas Nikolajsen char *snapshots_path = NULL; 1596a6e350fSMatthew Dillon char *config_path; 1606a6e350fSMatthew Dillon struct stat st; 1616a6e350fSMatthew Dillon char *cmd; 1626a6e350fSMatthew Dillon char *ptr; 1636a6e350fSMatthew Dillon int arg1; 1646a6e350fSMatthew Dillon int arg2; 1655e435c92SMatthew Dillon char *arg3; 1666a6e350fSMatthew Dillon time_t savet; 1676a6e350fSMatthew Dillon char buf[256]; 16883f2a3aaSMatthew Dillon char *cbase; 16983f2a3aaSMatthew Dillon char *cptr; 17082893617SThomas Nikolajsen FILE *fp = NULL; 171c453712aSMatthew Dillon struct didpfs *didpfs; 172c6c298a7SMatthew Dillon int snapshots_disabled = 0; 173c6c298a7SMatthew Dillon int prune_warning = 0; 17483f2a3aaSMatthew Dillon int new_config = 0; 175b5ec5ad4SMatthew Dillon int snapshots_from_pfs = 0; 1766a6e350fSMatthew Dillon int fd; 1776a6e350fSMatthew Dillon int r; 1780b8bd7daSMatthew Dillon int found_rebal = 0; 1796a6e350fSMatthew Dillon 1806a6e350fSMatthew Dillon bzero(&pfs, sizeof(pfs)); 1816a6e350fSMatthew Dillon bzero(&mrec_tmp, sizeof(mrec_tmp)); 1826a6e350fSMatthew Dillon pfs.ondisk = &mrec_tmp.pfs.pfsd; 1836a6e350fSMatthew Dillon pfs.bytes = sizeof(mrec_tmp.pfs.pfsd); 1846a6e350fSMatthew Dillon pfs.pfs_id = -1; 1856a6e350fSMatthew Dillon 1866a6e350fSMatthew Dillon printf("cleanup %-20s -", path); 1876a6e350fSMatthew Dillon fd = open(path, O_RDONLY); 1886a6e350fSMatthew Dillon if (fd < 0) { 1896a6e350fSMatthew Dillon printf(" unable to access directory: %s\n", strerror(errno)); 1906a6e350fSMatthew Dillon return; 1916a6e350fSMatthew Dillon } 19283f2a3aaSMatthew Dillon if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) < 0) { 1936a6e350fSMatthew Dillon printf(" not a HAMMER filesystem: %s\n", strerror(errno)); 19483f2a3aaSMatthew Dillon close(fd); 1956a6e350fSMatthew Dillon return; 1966a6e350fSMatthew Dillon } 1976a6e350fSMatthew Dillon if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) { 1986a6e350fSMatthew Dillon printf(" unrecognized HAMMER version\n"); 19983f2a3aaSMatthew Dillon close(fd); 2006a6e350fSMatthew Dillon return; 2016a6e350fSMatthew Dillon } 20283f2a3aaSMatthew Dillon bzero(&version, sizeof(version)); 20383f2a3aaSMatthew Dillon if (ioctl(fd, HAMMERIOC_GET_VERSION, &version) < 0) { 20483f2a3aaSMatthew Dillon printf(" HAMMER filesystem but couldn't retrieve version!\n"); 20583f2a3aaSMatthew Dillon close(fd); 20683f2a3aaSMatthew Dillon return; 20783f2a3aaSMatthew Dillon } 20883f2a3aaSMatthew Dillon 20983f2a3aaSMatthew Dillon bzero(&config, sizeof(config)); 21083f2a3aaSMatthew Dillon if (version.cur_version >= 3) { 21183f2a3aaSMatthew Dillon if (ioctl(fd, HAMMERIOC_GET_CONFIG, &config) == 0 && 21283f2a3aaSMatthew Dillon config.head.error == 0) { 21383f2a3aaSMatthew Dillon new_config = 1; 21483f2a3aaSMatthew Dillon } 21583f2a3aaSMatthew Dillon } 2166a6e350fSMatthew Dillon 2176a6e350fSMatthew Dillon /* 2186a6e350fSMatthew Dillon * Make sure we have not already handled this PFS. Several nullfs 2196a6e350fSMatthew Dillon * mounts might alias the same PFS. 2206a6e350fSMatthew Dillon */ 221c453712aSMatthew Dillon for (didpfs = FirstPFS; didpfs; didpfs = didpfs->next) { 222c453712aSMatthew Dillon if (bcmp(&didpfs->uuid, &mrec_tmp.pfs.pfsd.unique_uuid, sizeof(uuid_t)) == 0) { 223bb8e52c0SThomas Nikolajsen printf(" PFS #%d already handled\n", pfs.pfs_id); 22483f2a3aaSMatthew Dillon close(fd); 2256a6e350fSMatthew Dillon return; 2266a6e350fSMatthew Dillon } 227c453712aSMatthew Dillon } 228c453712aSMatthew Dillon didpfs = malloc(sizeof(*didpfs)); 229c453712aSMatthew Dillon didpfs->next = FirstPFS; 230c453712aSMatthew Dillon FirstPFS = didpfs; 231c453712aSMatthew Dillon didpfs->uuid = mrec_tmp.pfs.pfsd.unique_uuid; 2326a6e350fSMatthew Dillon 2336a6e350fSMatthew Dillon /* 234b5ec5ad4SMatthew Dillon * Calculate the old snapshots directory for HAMMER VERSION < 3 235b5ec5ad4SMatthew Dillon * 236b5ec5ad4SMatthew Dillon * If the directory is explicitly specified in the PFS config 237b5ec5ad4SMatthew Dillon * we flag it and will not migrate it later. 238ff1c9800SMatthew Dillon */ 239ff1c9800SMatthew Dillon if (mrec_tmp.pfs.pfsd.snapshots[0] == '/') { 240ff1c9800SMatthew Dillon asprintf(&snapshots_path, "%s", mrec_tmp.pfs.pfsd.snapshots); 241b5ec5ad4SMatthew Dillon snapshots_from_pfs = 1; 242ff1c9800SMatthew Dillon } else if (mrec_tmp.pfs.pfsd.snapshots[0]) { 243ff1c9800SMatthew Dillon printf(" WARNING: pfs-slave's snapshots dir is not absolute\n"); 24483f2a3aaSMatthew Dillon close(fd); 245ff1c9800SMatthew Dillon return; 246ff1c9800SMatthew Dillon } else if (mrec_tmp.pfs.pfsd.mirror_flags & HAMMER_PFSD_SLAVE) { 24782893617SThomas Nikolajsen if (version.cur_version < 3) { 248ff1c9800SMatthew Dillon printf(" WARNING: must configure snapshot dir for PFS slave\n"); 249ff1c9800SMatthew Dillon printf("\tWe suggest <fs>/var/slaves/<name> where " 250ff1c9800SMatthew Dillon "<fs> is the base HAMMER fs\n"); 251bb8e52c0SThomas Nikolajsen printf("\tcontaining the slave\n"); 25283f2a3aaSMatthew Dillon close(fd); 253ff1c9800SMatthew Dillon return; 25482893617SThomas Nikolajsen } 255ff1c9800SMatthew Dillon } else { 256ff1c9800SMatthew Dillon asprintf(&snapshots_path, 257ff1c9800SMatthew Dillon "%s%ssnapshots", path, dividing_slash(path)); 258ff1c9800SMatthew Dillon } 259ff1c9800SMatthew Dillon 260ff1c9800SMatthew Dillon /* 261b5ec5ad4SMatthew Dillon * Check for old-style config file 2626a6e350fSMatthew Dillon */ 26382893617SThomas Nikolajsen if (snapshots_path) { 2646a6e350fSMatthew Dillon asprintf(&config_path, "%s/config", snapshots_path); 2656a6e350fSMatthew Dillon fp = fopen(config_path, "r"); 26682893617SThomas Nikolajsen } 26783f2a3aaSMatthew Dillon 26883f2a3aaSMatthew Dillon /* 26983f2a3aaSMatthew Dillon * Handle upgrades to hammer version 3, move the config 27083f2a3aaSMatthew Dillon * file into meta-data. 27183f2a3aaSMatthew Dillon * 27283f2a3aaSMatthew Dillon * For the old config read the file into the config structure, 27383f2a3aaSMatthew Dillon * we will parse it out of the config structure regardless. 27483f2a3aaSMatthew Dillon */ 27583f2a3aaSMatthew Dillon if (version.cur_version >= 3) { 27683f2a3aaSMatthew Dillon if (fp) { 27783f2a3aaSMatthew Dillon printf("(migrating) "); 27883f2a3aaSMatthew Dillon fflush(stdout); 27983f2a3aaSMatthew Dillon migrate_config(fp, &config); 28083f2a3aaSMatthew Dillon migrate_snapshots(fd, snapshots_path); 28183f2a3aaSMatthew Dillon fclose(fp); 28283f2a3aaSMatthew Dillon if (ioctl(fd, HAMMERIOC_SET_CONFIG, &config) < 0) { 28383f2a3aaSMatthew Dillon printf(" cannot init meta-data config!\n"); 28483f2a3aaSMatthew Dillon close(fd); 2856a6e350fSMatthew Dillon return; 2866a6e350fSMatthew Dillon } 28783f2a3aaSMatthew Dillon remove(config_path); 28883f2a3aaSMatthew Dillon } else if (new_config == 0) { 28983f2a3aaSMatthew Dillon config_init(path, &config); 29083f2a3aaSMatthew Dillon if (ioctl(fd, HAMMERIOC_SET_CONFIG, &config) < 0) { 29183f2a3aaSMatthew Dillon printf(" cannot init meta-data config!\n"); 29283f2a3aaSMatthew Dillon close(fd); 29383f2a3aaSMatthew Dillon return; 29483f2a3aaSMatthew Dillon } 29583f2a3aaSMatthew Dillon } 29683f2a3aaSMatthew Dillon new_config = 1; 29783f2a3aaSMatthew Dillon } else { 298b5ec5ad4SMatthew Dillon /* 299b5ec5ad4SMatthew Dillon * Create missing snapshots directory for HAMMER VERSION < 3 300b5ec5ad4SMatthew Dillon */ 301b5ec5ad4SMatthew Dillon if (stat(snapshots_path, &st) < 0) { 302b5ec5ad4SMatthew Dillon if (mkdir(snapshots_path, 0755) != 0) { 303b5ec5ad4SMatthew Dillon free(snapshots_path); 304b5ec5ad4SMatthew Dillon printf(" unable to create snapshot dir \"%s\": %s\n", 305b5ec5ad4SMatthew Dillon snapshots_path, strerror(errno)); 306b5ec5ad4SMatthew Dillon close(fd); 307b5ec5ad4SMatthew Dillon return; 308b5ec5ad4SMatthew Dillon } 309b5ec5ad4SMatthew Dillon } 310b5ec5ad4SMatthew Dillon 311b5ec5ad4SMatthew Dillon /* 312b5ec5ad4SMatthew Dillon * Create missing config file for HAMMER VERSION < 3 313b5ec5ad4SMatthew Dillon */ 31483f2a3aaSMatthew Dillon if (fp == NULL) { 31583f2a3aaSMatthew Dillon config_init(path, &config); 31683f2a3aaSMatthew Dillon fp = fopen(config_path, "w"); 31783f2a3aaSMatthew Dillon if (fp) { 31883f2a3aaSMatthew Dillon fwrite(config.config.text, 1, 31983f2a3aaSMatthew Dillon strlen(config.config.text), fp); 32083f2a3aaSMatthew Dillon fclose(fp); 32183f2a3aaSMatthew Dillon } 32283f2a3aaSMatthew Dillon } else { 32383f2a3aaSMatthew Dillon migrate_config(fp, &config); 32483f2a3aaSMatthew Dillon fclose(fp); 32583f2a3aaSMatthew Dillon } 32683f2a3aaSMatthew Dillon } 3276a6e350fSMatthew Dillon 328b5ec5ad4SMatthew Dillon /* 329b5ec5ad4SMatthew Dillon * If snapshots_from_pfs is not set we calculate the new snapshots 330b5ec5ad4SMatthew Dillon * directory default (in /var) for HAMMER VERSION >= 3 and migrate 331b5ec5ad4SMatthew Dillon * the old snapshots directory over. 332b5ec5ad4SMatthew Dillon * 333b5ec5ad4SMatthew Dillon * People who have set an explicit snapshots directory will have 334b5ec5ad4SMatthew Dillon * to migrate the data manually into /var/hammer, or not bother at 335b5ec5ad4SMatthew Dillon * all. People running slaves may wish to migrate it and then 336b5ec5ad4SMatthew Dillon * clear the snapshots specification in the PFS config for the 337b5ec5ad4SMatthew Dillon * slave. 338b5ec5ad4SMatthew Dillon */ 339b5ec5ad4SMatthew Dillon if (new_config && snapshots_from_pfs == 0) { 340b5ec5ad4SMatthew Dillon char *npath; 341b5ec5ad4SMatthew Dillon 342b5ec5ad4SMatthew Dillon assert(path[0] == '/'); 343b5ec5ad4SMatthew Dillon if (strcmp(path, "/") == 0) 344b5ec5ad4SMatthew Dillon asprintf(&npath, "%s/root", SNAPSHOTS_BASE); 345b5ec5ad4SMatthew Dillon else 346b5ec5ad4SMatthew Dillon asprintf(&npath, "%s/%s", SNAPSHOTS_BASE, path + 1); 34782893617SThomas Nikolajsen if (snapshots_path) { 348b5ec5ad4SMatthew Dillon if (stat(npath, &st) < 0 && errno == ENOENT) { 349b5ec5ad4SMatthew Dillon if (stat(snapshots_path, &st) < 0 && errno == ENOENT) { 350b5ec5ad4SMatthew Dillon printf(" HAMMER UPGRADE: Creating snapshots\n" 351b5ec5ad4SMatthew Dillon "\tCreating snapshots in %s\n", 352b5ec5ad4SMatthew Dillon npath); 353b5ec5ad4SMatthew Dillon runcmd(&r, "mkdir -p %s", npath); 354b5ec5ad4SMatthew Dillon } else { 355b5ec5ad4SMatthew Dillon printf(" HAMMER UPGRADE: Moving snapshots\n" 356b5ec5ad4SMatthew Dillon "\tMoving snapshots from %s to %s\n", 357b5ec5ad4SMatthew Dillon snapshots_path, npath); 358b5ec5ad4SMatthew Dillon runcmd(&r, "mkdir -p %s", npath); 359b5ec5ad4SMatthew Dillon runcmd(&r, "cpdup %s %s", snapshots_path, npath); 360b5ec5ad4SMatthew Dillon if (r != 0) { 361b5ec5ad4SMatthew Dillon printf("Unable to move snapshots directory!\n"); 362b5ec5ad4SMatthew Dillon printf("Please fix this critical error.\n"); 363b5ec5ad4SMatthew Dillon printf("Aborting cleanup of %s\n", path); 364b5ec5ad4SMatthew Dillon close(fd); 365b5ec5ad4SMatthew Dillon return; 366b5ec5ad4SMatthew Dillon } 367b5ec5ad4SMatthew Dillon runcmd(&r, "rm -rf %s", snapshots_path); 368b5ec5ad4SMatthew Dillon } 369b5ec5ad4SMatthew Dillon } 370b5ec5ad4SMatthew Dillon free(snapshots_path); 37182893617SThomas Nikolajsen } else if (stat(npath, &st) < 0 && errno == ENOENT) { 37282893617SThomas Nikolajsen runcmd(&r, "mkdir -p %s", npath); 37382893617SThomas Nikolajsen } 374b5ec5ad4SMatthew Dillon snapshots_path = npath; 375b5ec5ad4SMatthew Dillon } 376b5ec5ad4SMatthew Dillon 377b5ec5ad4SMatthew Dillon /* 378b5ec5ad4SMatthew Dillon * Lock the PFS. fd is the base directory of the mounted PFS. 379b5ec5ad4SMatthew Dillon */ 38083f2a3aaSMatthew Dillon if (flock(fd, LOCK_EX|LOCK_NB) == -1) { 3815bd5f172SSimon Schubert if (errno == EWOULDBLOCK) 3825bd5f172SSimon Schubert printf(" PFS #%d locked by other process\n", pfs.pfs_id); 3835bd5f172SSimon Schubert else 3845bd5f172SSimon Schubert printf(" can not lock %s: %s\n", config_path, strerror(errno)); 38583f2a3aaSMatthew Dillon close(fd); 3865bd5f172SSimon Schubert return; 3875bd5f172SSimon Schubert } 3885bd5f172SSimon Schubert 389ff1c9800SMatthew Dillon printf(" handle PFS #%d using %s\n", pfs.pfs_id, snapshots_path); 3906a6e350fSMatthew Dillon 391*a360fddeSJohn Marino struct pidfh *pfh = NULL; 392*a360fddeSJohn Marino static char pidfile[PIDFILE_BUFSIZE]; 393*a360fddeSJohn Marino 394*a360fddeSJohn Marino snprintf (pidfile, PIDFILE_BUFSIZE, "%s/hammer.cleanup.%d", 395*a360fddeSJohn Marino pidfile_loc, getpid()); 396*a360fddeSJohn Marino pfh = pidfile_open(pidfile, 0644, NULL); 397*a360fddeSJohn Marino if (pfh == NULL) { 398*a360fddeSJohn Marino warn ("Unable to open or create %s", pidfile); 399*a360fddeSJohn Marino } 400*a360fddeSJohn Marino pidfile_write(pfh); 401*a360fddeSJohn Marino 4026a6e350fSMatthew Dillon /* 4036a6e350fSMatthew Dillon * Process the config file 4046a6e350fSMatthew Dillon */ 40583f2a3aaSMatthew Dillon cbase = config.config.text; 40683f2a3aaSMatthew Dillon 40783f2a3aaSMatthew Dillon while ((cptr = strchr(cbase, '\n')) != NULL) { 40883f2a3aaSMatthew Dillon bcopy(cbase, buf, cptr - cbase); 40983f2a3aaSMatthew Dillon buf[cptr - cbase] = 0; 41083f2a3aaSMatthew Dillon cbase = cptr + 1; 41183f2a3aaSMatthew Dillon 4126a6e350fSMatthew Dillon cmd = strtok(buf, WS); 413743332abSMatthew Dillon if (cmd == NULL || cmd[0] == '#') 414743332abSMatthew Dillon continue; 415743332abSMatthew Dillon 4166a6e350fSMatthew Dillon arg1 = 0; 4176a6e350fSMatthew Dillon arg2 = 0; 4185e435c92SMatthew Dillon arg3 = NULL; 4196a6e350fSMatthew Dillon if ((ptr = strtok(NULL, WS)) != NULL) { 4206a6e350fSMatthew Dillon arg1 = strtosecs(ptr); 4215e435c92SMatthew Dillon if ((ptr = strtok(NULL, WS)) != NULL) { 4226a6e350fSMatthew Dillon arg2 = strtosecs(ptr); 4235e435c92SMatthew Dillon arg3 = strtok(NULL, WS); 4245e435c92SMatthew Dillon } 4256a6e350fSMatthew Dillon } 4266a6e350fSMatthew Dillon 4276a6e350fSMatthew Dillon printf("%20s - ", cmd); 4286a6e350fSMatthew Dillon fflush(stdout); 4296a6e350fSMatthew Dillon 4306a6e350fSMatthew Dillon r = 1; 4316a6e350fSMatthew Dillon if (strcmp(cmd, "snapshots") == 0) { 4325e435c92SMatthew Dillon if (arg1 == 0) { 43383f2a3aaSMatthew Dillon if (arg2 && 43483f2a3aaSMatthew Dillon check_softlinks(fd, new_config, 43583f2a3aaSMatthew Dillon snapshots_path)) { 4365e435c92SMatthew Dillon printf("only removing old snapshots\n"); 4375e435c92SMatthew Dillon prune_warning = 1; 43883f2a3aaSMatthew Dillon cleanup_softlinks(fd, new_config, 43983f2a3aaSMatthew Dillon snapshots_path, 4405e435c92SMatthew Dillon arg2, arg3); 4415e435c92SMatthew Dillon } else { 4425e435c92SMatthew Dillon printf("disabled\n"); 4435e435c92SMatthew Dillon snapshots_disabled = 1; 4445e435c92SMatthew Dillon } 4455e435c92SMatthew Dillon } else 4466a6e350fSMatthew Dillon if (check_period(snapshots_path, cmd, arg1, &savet)) { 4476a6e350fSMatthew Dillon printf("run\n"); 44883f2a3aaSMatthew Dillon cleanup_softlinks(fd, new_config, 44983f2a3aaSMatthew Dillon snapshots_path, 4505e435c92SMatthew Dillon arg2, arg3); 451b5ec5ad4SMatthew Dillon r = create_snapshot(path, snapshots_path); 4526a6e350fSMatthew Dillon } else { 4536a6e350fSMatthew Dillon printf("skip\n"); 4546a6e350fSMatthew Dillon } 4555e435c92SMatthew Dillon } else if (arg1 == 0) { 4565e435c92SMatthew Dillon /* 4575e435c92SMatthew Dillon * The commands following this check can't handle 4585e435c92SMatthew Dillon * a period of 0, so call the feature disabled and 4595e435c92SMatthew Dillon * ignore the directive. 4605e435c92SMatthew Dillon */ 4615e435c92SMatthew Dillon printf("disabled\n"); 4626a6e350fSMatthew Dillon } else if (strcmp(cmd, "prune") == 0) { 4636a6e350fSMatthew Dillon if (check_period(snapshots_path, cmd, arg1, &savet)) { 4645e435c92SMatthew Dillon if (prune_warning) { 4655e435c92SMatthew Dillon printf("run - WARNING snapshot " 4665e435c92SMatthew Dillon "softlinks present " 467226f3799SThomas Nikolajsen "but snapshots disabled\n"); 4685e435c92SMatthew Dillon } else { 469c6c298a7SMatthew Dillon printf("run\n"); 4705e435c92SMatthew Dillon } 4716a6e350fSMatthew Dillon r = cleanup_prune(path, snapshots_path, 472c6c298a7SMatthew Dillon arg1, arg2, snapshots_disabled); 4736a6e350fSMatthew Dillon } else { 4746a6e350fSMatthew Dillon printf("skip\n"); 4756a6e350fSMatthew Dillon } 4760b8bd7daSMatthew Dillon } else if (strcmp(cmd, "rebalance") == 0) { 4770b8bd7daSMatthew Dillon found_rebal = 1; 4780b8bd7daSMatthew Dillon if (check_period(snapshots_path, cmd, arg1, &savet)) { 4790b8bd7daSMatthew Dillon printf("run"); 4800b8bd7daSMatthew Dillon fflush(stdout); 4810b8bd7daSMatthew Dillon if (VerboseOpt) 4820b8bd7daSMatthew Dillon printf("\n"); 4830b8bd7daSMatthew Dillon r = cleanup_rebalance(path, snapshots_path, 4840b8bd7daSMatthew Dillon arg1, arg2); 4850b8bd7daSMatthew Dillon } else { 4860b8bd7daSMatthew Dillon printf("skip\n"); 4870b8bd7daSMatthew Dillon } 4886a6e350fSMatthew Dillon } else if (strcmp(cmd, "reblock") == 0) { 4896a6e350fSMatthew Dillon if (check_period(snapshots_path, cmd, arg1, &savet)) { 4906a6e350fSMatthew Dillon printf("run"); 4916a6e350fSMatthew Dillon fflush(stdout); 4926a6e350fSMatthew Dillon if (VerboseOpt) 4936a6e350fSMatthew Dillon printf("\n"); 4946a6e350fSMatthew Dillon r = cleanup_reblock(path, snapshots_path, 4956a6e350fSMatthew Dillon arg1, arg2); 4966a6e350fSMatthew Dillon } else { 4976a6e350fSMatthew Dillon printf("skip\n"); 4986a6e350fSMatthew Dillon } 4996a6e350fSMatthew Dillon } else if (strcmp(cmd, "recopy") == 0) { 5006a6e350fSMatthew Dillon if (check_period(snapshots_path, cmd, arg1, &savet)) { 5016a6e350fSMatthew Dillon printf("run"); 5026a6e350fSMatthew Dillon fflush(stdout); 5036a6e350fSMatthew Dillon if (VerboseOpt) 5046a6e350fSMatthew Dillon printf("\n"); 5056a6e350fSMatthew Dillon r = cleanup_recopy(path, snapshots_path, 5066a6e350fSMatthew Dillon arg1, arg2); 5076a6e350fSMatthew Dillon } else { 5086a6e350fSMatthew Dillon printf("skip\n"); 5096a6e350fSMatthew Dillon } 510bb29b5d8SMatthew Dillon } else if (strcmp(cmd, "dedup") == 0) { 511bb29b5d8SMatthew Dillon if (check_period(snapshots_path, cmd, arg1, &savet)) { 512bb29b5d8SMatthew Dillon printf("run"); 513bb29b5d8SMatthew Dillon fflush(stdout); 514bb29b5d8SMatthew Dillon if (VerboseOpt) 515bb29b5d8SMatthew Dillon printf("\n"); 516bb29b5d8SMatthew Dillon r = cleanup_dedup(path, snapshots_path, 517bb29b5d8SMatthew Dillon arg1, arg2); 518bb29b5d8SMatthew Dillon } else { 519bb29b5d8SMatthew Dillon printf("skip\n"); 520bb29b5d8SMatthew Dillon } 5216a6e350fSMatthew Dillon } else { 5226a6e350fSMatthew Dillon printf("unknown directive\n"); 5236a6e350fSMatthew Dillon r = 1; 5246a6e350fSMatthew Dillon } 5256a6e350fSMatthew Dillon if (r == 0) 5266a6e350fSMatthew Dillon save_period(snapshots_path, cmd, savet); 5276a6e350fSMatthew Dillon } 5280b8bd7daSMatthew Dillon 5290b8bd7daSMatthew Dillon /* 53083f2a3aaSMatthew Dillon * Add new rebalance feature if the config doesn't have it. 531b5ec5ad4SMatthew Dillon * (old style config only). 5320b8bd7daSMatthew Dillon */ 53383f2a3aaSMatthew Dillon if (new_config == 0 && found_rebal == 0) { 5340b8bd7daSMatthew Dillon if ((fp = fopen(config_path, "r+")) != NULL) { 5350b8bd7daSMatthew Dillon fseek(fp, 0L, 2); 5360b8bd7daSMatthew Dillon fprintf(fp, "rebalance 1d 5m\n"); 5370b8bd7daSMatthew Dillon fclose(fp); 5380b8bd7daSMatthew Dillon } 5390b8bd7daSMatthew Dillon } 54083f2a3aaSMatthew Dillon 54183f2a3aaSMatthew Dillon /* 54283f2a3aaSMatthew Dillon * Cleanup, and delay a little 54383f2a3aaSMatthew Dillon */ 54483f2a3aaSMatthew Dillon close(fd); 5456a6e350fSMatthew Dillon usleep(1000); 546*a360fddeSJohn Marino pidfile_close(pfh); 547*a360fddeSJohn Marino pidfile_remove(pfh); 5486a6e350fSMatthew Dillon } 5496a6e350fSMatthew Dillon 55083f2a3aaSMatthew Dillon /* 55183f2a3aaSMatthew Dillon * Initialize new config data (new or old style) 55283f2a3aaSMatthew Dillon */ 55383f2a3aaSMatthew Dillon static void 55483f2a3aaSMatthew Dillon config_init(const char *path, struct hammer_ioc_config *config) 55583f2a3aaSMatthew Dillon { 55683f2a3aaSMatthew Dillon const char *snapshots; 55783f2a3aaSMatthew Dillon 55883f2a3aaSMatthew Dillon if (strcmp(path, "/tmp") == 0 || 55983f2a3aaSMatthew Dillon strcmp(path, "/var/tmp") == 0 || 56083f2a3aaSMatthew Dillon strcmp(path, "/usr/obj") == 0) { 56183f2a3aaSMatthew Dillon snapshots = "snapshots 0d 0d\n"; 56283f2a3aaSMatthew Dillon } else { 56383f2a3aaSMatthew Dillon snapshots = "snapshots 1d 60d\n"; 56483f2a3aaSMatthew Dillon } 56583f2a3aaSMatthew Dillon bzero(config->config.text, sizeof(config->config.text)); 56683f2a3aaSMatthew Dillon snprintf(config->config.text, sizeof(config->config.text) - 1, "%s%s", 56783f2a3aaSMatthew Dillon snapshots, 56883f2a3aaSMatthew Dillon "prune 1d 5m\n" 56983f2a3aaSMatthew Dillon "rebalance 1d 5m\n" 570bb29b5d8SMatthew Dillon "#dedup 1d 5m\n" 57183f2a3aaSMatthew Dillon "reblock 1d 5m\n" 572f6532f03SThomas Nikolajsen "recopy 30d 10m\n"); 57383f2a3aaSMatthew Dillon } 57483f2a3aaSMatthew Dillon 57583f2a3aaSMatthew Dillon /* 57683f2a3aaSMatthew Dillon * Migrate configuration data from the old snapshots/config 57716265794SThomas Nikolajsen * file to the new meta-data format. 57883f2a3aaSMatthew Dillon */ 57983f2a3aaSMatthew Dillon static void 58083f2a3aaSMatthew Dillon migrate_config(FILE *fp, struct hammer_ioc_config *config) 58183f2a3aaSMatthew Dillon { 58283f2a3aaSMatthew Dillon int n; 58383f2a3aaSMatthew Dillon 58483f2a3aaSMatthew Dillon n = fread(config->config.text, 1, sizeof(config->config.text) - 1, fp); 58583f2a3aaSMatthew Dillon if (n >= 0) 58683f2a3aaSMatthew Dillon bzero(config->config.text + n, sizeof(config->config.text) - n); 58783f2a3aaSMatthew Dillon } 58883f2a3aaSMatthew Dillon 58983f2a3aaSMatthew Dillon /* 59083f2a3aaSMatthew Dillon * Migrate snapshot softlinks in the snapshots directory to the 59183f2a3aaSMatthew Dillon * new meta-data format. The softlinks are left intact, but 59283f2a3aaSMatthew Dillon * this way the pruning code won't lose track of them if you 59383f2a3aaSMatthew Dillon * happen to blow away the snapshots directory. 59483f2a3aaSMatthew Dillon */ 59583f2a3aaSMatthew Dillon static void 59683f2a3aaSMatthew Dillon migrate_snapshots(int fd, const char *snapshots_path) 59783f2a3aaSMatthew Dillon { 59883f2a3aaSMatthew Dillon struct hammer_ioc_snapshot snapshot; 59983f2a3aaSMatthew Dillon struct dirent *den; 60083f2a3aaSMatthew Dillon struct stat st; 60183f2a3aaSMatthew Dillon DIR *dir; 60283f2a3aaSMatthew Dillon char *fpath; 60383f2a3aaSMatthew Dillon 60483f2a3aaSMatthew Dillon bzero(&snapshot, sizeof(snapshot)); 60583f2a3aaSMatthew Dillon 60683f2a3aaSMatthew Dillon if ((dir = opendir(snapshots_path)) != NULL) { 60783f2a3aaSMatthew Dillon while ((den = readdir(dir)) != NULL) { 60883f2a3aaSMatthew Dillon if (den->d_name[0] == '.') 60983f2a3aaSMatthew Dillon continue; 61083f2a3aaSMatthew Dillon asprintf(&fpath, "%s/%s", snapshots_path, den->d_name); 61183f2a3aaSMatthew Dillon if (lstat(fpath, &st) == 0 && S_ISLNK(st.st_mode)) { 61283f2a3aaSMatthew Dillon migrate_one_snapshot(fd, fpath, &snapshot); 61383f2a3aaSMatthew Dillon } 61483f2a3aaSMatthew Dillon free(fpath); 61583f2a3aaSMatthew Dillon } 61683f2a3aaSMatthew Dillon closedir(dir); 61783f2a3aaSMatthew Dillon } 61883f2a3aaSMatthew Dillon migrate_one_snapshot(fd, NULL, &snapshot); 61983f2a3aaSMatthew Dillon 62083f2a3aaSMatthew Dillon } 62183f2a3aaSMatthew Dillon 62283f2a3aaSMatthew Dillon /* 62383f2a3aaSMatthew Dillon * Migrate a single snapshot. If fpath is NULL the ioctl is flushed, 62483f2a3aaSMatthew Dillon * otherwise it is flushed when it fills up. 62583f2a3aaSMatthew Dillon */ 62683f2a3aaSMatthew Dillon static void 62783f2a3aaSMatthew Dillon migrate_one_snapshot(int fd, const char *fpath, 62883f2a3aaSMatthew Dillon struct hammer_ioc_snapshot *snapshot) 62983f2a3aaSMatthew Dillon { 63083f2a3aaSMatthew Dillon if (fpath) { 63183f2a3aaSMatthew Dillon struct hammer_snapshot_data *snap; 63283f2a3aaSMatthew Dillon struct tm tm; 63383f2a3aaSMatthew Dillon time_t t; 63483f2a3aaSMatthew Dillon int year; 63583f2a3aaSMatthew Dillon int month; 63683f2a3aaSMatthew Dillon int day = 0; 63783f2a3aaSMatthew Dillon int hour = 0; 63883f2a3aaSMatthew Dillon int minute = 0; 63983f2a3aaSMatthew Dillon int r; 64083f2a3aaSMatthew Dillon char linkbuf[1024]; 64183f2a3aaSMatthew Dillon const char *ptr; 64283f2a3aaSMatthew Dillon hammer_tid_t tid; 64383f2a3aaSMatthew Dillon 64483f2a3aaSMatthew Dillon t = (time_t)-1; 64583f2a3aaSMatthew Dillon tid = (hammer_tid_t)(int64_t)-1; 64683f2a3aaSMatthew Dillon 647f510bf3eSYONETANI Tomokazu /* fpath may contain directory components */ 648f510bf3eSYONETANI Tomokazu if ((ptr = strrchr(fpath, '/')) != NULL) 649f510bf3eSYONETANI Tomokazu ++ptr; 650f510bf3eSYONETANI Tomokazu else 65183f2a3aaSMatthew Dillon ptr = fpath; 65283f2a3aaSMatthew Dillon while (*ptr && *ptr != '-' && *ptr != '.') 65383f2a3aaSMatthew Dillon ++ptr; 65483f2a3aaSMatthew Dillon if (*ptr) 65583f2a3aaSMatthew Dillon ++ptr; 65683f2a3aaSMatthew Dillon r = sscanf(ptr, "%4d%2d%2d-%2d%2d", 65783f2a3aaSMatthew Dillon &year, &month, &day, &hour, &minute); 65883f2a3aaSMatthew Dillon 65983f2a3aaSMatthew Dillon if (r >= 3) { 66083f2a3aaSMatthew Dillon bzero(&tm, sizeof(tm)); 66183f2a3aaSMatthew Dillon tm.tm_isdst = -1; 66283f2a3aaSMatthew Dillon tm.tm_min = minute; 66383f2a3aaSMatthew Dillon tm.tm_hour = hour; 66483f2a3aaSMatthew Dillon tm.tm_mday = day; 66583f2a3aaSMatthew Dillon tm.tm_mon = month - 1; 66683f2a3aaSMatthew Dillon tm.tm_year = year - 1900; 66783f2a3aaSMatthew Dillon t = mktime(&tm); 66883f2a3aaSMatthew Dillon } 66983f2a3aaSMatthew Dillon bzero(linkbuf, sizeof(linkbuf)); 67083f2a3aaSMatthew Dillon if (readlink(fpath, linkbuf, sizeof(linkbuf) - 1) > 0 && 67183f2a3aaSMatthew Dillon (ptr = strrchr(linkbuf, '@')) != NULL && 67283f2a3aaSMatthew Dillon ptr > linkbuf && ptr[-1] == '@') { 67383f2a3aaSMatthew Dillon tid = strtoull(ptr + 1, NULL, 16); 67483f2a3aaSMatthew Dillon } 67583f2a3aaSMatthew Dillon if (t != (time_t)-1 && tid != (hammer_tid_t)(int64_t)-1) { 67683f2a3aaSMatthew Dillon snap = &snapshot->snaps[snapshot->count]; 67783f2a3aaSMatthew Dillon bzero(snap, sizeof(*snap)); 67883f2a3aaSMatthew Dillon snap->tid = tid; 67983f2a3aaSMatthew Dillon snap->ts = (u_int64_t)t * 1000000ULL; 68083f2a3aaSMatthew Dillon snprintf(snap->label, sizeof(snap->label), 68183f2a3aaSMatthew Dillon "migrated"); 68283f2a3aaSMatthew Dillon ++snapshot->count; 683f510bf3eSYONETANI Tomokazu } else { 684f510bf3eSYONETANI Tomokazu printf(" non-canonical snapshot softlink: %s->%s\n", 685f510bf3eSYONETANI Tomokazu fpath, linkbuf); 68683f2a3aaSMatthew Dillon } 68783f2a3aaSMatthew Dillon } 68883f2a3aaSMatthew Dillon 68983f2a3aaSMatthew Dillon if ((fpath == NULL && snapshot->count) || 69083f2a3aaSMatthew Dillon snapshot->count == HAMMER_SNAPS_PER_IOCTL) { 69183f2a3aaSMatthew Dillon printf(" (%d snapshots)", snapshot->count); 69283f2a3aaSMatthew Dillon again: 69383f2a3aaSMatthew Dillon if (ioctl(fd, HAMMERIOC_ADD_SNAPSHOT, snapshot) < 0) { 69483f2a3aaSMatthew Dillon printf(" Ioctl to migrate snapshots failed: %s\n", 69583f2a3aaSMatthew Dillon strerror(errno)); 69683f2a3aaSMatthew Dillon } else if (snapshot->head.error == EALREADY) { 69783f2a3aaSMatthew Dillon ++snapshot->index; 69883f2a3aaSMatthew Dillon goto again; 69983f2a3aaSMatthew Dillon } else if (snapshot->head.error) { 700068da2c2SMatthew Dillon printf(" Ioctl to migrate snapshots failed: %s\n", 70183f2a3aaSMatthew Dillon strerror(snapshot->head.error)); 70283f2a3aaSMatthew Dillon } 70383f2a3aaSMatthew Dillon printf("index %d\n", snapshot->index); 70483f2a3aaSMatthew Dillon snapshot->index = 0; 70583f2a3aaSMatthew Dillon snapshot->count = 0; 70683f2a3aaSMatthew Dillon snapshot->head.error = 0; 70783f2a3aaSMatthew Dillon } 70883f2a3aaSMatthew Dillon } 70983f2a3aaSMatthew Dillon 7106a6e350fSMatthew Dillon static 7116a6e350fSMatthew Dillon int 7126a6e350fSMatthew Dillon strtosecs(char *ptr) 7136a6e350fSMatthew Dillon { 7146a6e350fSMatthew Dillon int val; 7156a6e350fSMatthew Dillon 7166a6e350fSMatthew Dillon val = strtol(ptr, &ptr, 0); 7176a6e350fSMatthew Dillon switch(*ptr) { 7186a6e350fSMatthew Dillon case 'd': 7196a6e350fSMatthew Dillon val *= 24; 7206a6e350fSMatthew Dillon /* fall through */ 7216a6e350fSMatthew Dillon case 'h': 7226a6e350fSMatthew Dillon val *= 60; 7236a6e350fSMatthew Dillon /* fall through */ 7246a6e350fSMatthew Dillon case 'm': 7256a6e350fSMatthew Dillon val *= 60; 7266a6e350fSMatthew Dillon /* fall through */ 7276a6e350fSMatthew Dillon case 's': 7286a6e350fSMatthew Dillon break; 7296a6e350fSMatthew Dillon default: 7306a6e350fSMatthew Dillon errx(1, "illegal suffix converting %s\n", ptr); 7316a6e350fSMatthew Dillon break; 7326a6e350fSMatthew Dillon } 7336a6e350fSMatthew Dillon return(val); 7346a6e350fSMatthew Dillon } 7356a6e350fSMatthew Dillon 7366a6e350fSMatthew Dillon static const char * 7376a6e350fSMatthew Dillon dividing_slash(const char *path) 7386a6e350fSMatthew Dillon { 7396a6e350fSMatthew Dillon int len = strlen(path); 7406a6e350fSMatthew Dillon if (len && path[len-1] == '/') 7416a6e350fSMatthew Dillon return(""); 7426a6e350fSMatthew Dillon else 7436a6e350fSMatthew Dillon return("/"); 7446a6e350fSMatthew Dillon } 7456a6e350fSMatthew Dillon 7466a6e350fSMatthew Dillon /* 7476a6e350fSMatthew Dillon * Check whether the desired period has elapsed since the last successful 7486a6e350fSMatthew Dillon * run. The run may take a while and cross a boundary so we remember the 7496a6e350fSMatthew Dillon * current time_t so we can save it later on. 7506a6e350fSMatthew Dillon * 7516a6e350fSMatthew Dillon * Periods in minutes, hours, or days are assumed to have been crossed 7526a6e350fSMatthew Dillon * if the local time crosses a minute, hour, or day boundary regardless 7536a6e350fSMatthew Dillon * of how close the last operation actually was. 754bdc54107SMatthew Dillon * 755bdc54107SMatthew Dillon * If ForceOpt is set always return true. 7566a6e350fSMatthew Dillon */ 7576a6e350fSMatthew Dillon static int 7586a6e350fSMatthew Dillon check_period(const char *snapshots_path, const char *cmd, int arg1, 7596a6e350fSMatthew Dillon time_t *savep) 7606a6e350fSMatthew Dillon { 7616a6e350fSMatthew Dillon char *check_path; 7626a6e350fSMatthew Dillon struct tm tp1; 7636a6e350fSMatthew Dillon struct tm tp2; 7646a6e350fSMatthew Dillon FILE *fp; 7656a6e350fSMatthew Dillon time_t baset, lastt; 7666a6e350fSMatthew Dillon char buf[256]; 7676a6e350fSMatthew Dillon 7686a6e350fSMatthew Dillon time(savep); 7696a6e350fSMatthew Dillon localtime_r(savep, &tp1); 7706a6e350fSMatthew Dillon 7716a6e350fSMatthew Dillon /* 772bdc54107SMatthew Dillon * Force run if -F 773bdc54107SMatthew Dillon */ 774bdc54107SMatthew Dillon if (ForceOpt) 775bdc54107SMatthew Dillon return(1); 776bdc54107SMatthew Dillon 777bdc54107SMatthew Dillon /* 7786a6e350fSMatthew Dillon * Retrieve the start time of the last successful operation. 7796a6e350fSMatthew Dillon */ 7806a6e350fSMatthew Dillon asprintf(&check_path, "%s/.%s.period", snapshots_path, cmd); 7816a6e350fSMatthew Dillon fp = fopen(check_path, "r"); 7826a6e350fSMatthew Dillon free(check_path); 7836a6e350fSMatthew Dillon if (fp == NULL) 7846a6e350fSMatthew Dillon return(1); 7856a6e350fSMatthew Dillon if (fgets(buf, sizeof(buf), fp) == NULL) { 7866a6e350fSMatthew Dillon fclose(fp); 7876a6e350fSMatthew Dillon return(1); 7886a6e350fSMatthew Dillon } 7896a6e350fSMatthew Dillon fclose(fp); 7906a6e350fSMatthew Dillon 7916a6e350fSMatthew Dillon lastt = strtol(buf, NULL, 0); 7926a6e350fSMatthew Dillon localtime_r(&lastt, &tp2); 7936a6e350fSMatthew Dillon 7946a6e350fSMatthew Dillon /* 7956a6e350fSMatthew Dillon * Normalize the times. e.g. if asked to do something on a 1-day 7966a6e350fSMatthew Dillon * interval the operation will be performed as soon as the day 7976a6e350fSMatthew Dillon * turns over relative to the previous operation, even if the previous 7986a6e350fSMatthew Dillon * operation ran a few seconds ago just before midnight. 7996a6e350fSMatthew Dillon */ 8006a6e350fSMatthew Dillon if (arg1 % 60 == 0) { 8016a6e350fSMatthew Dillon tp1.tm_sec = 0; 8026a6e350fSMatthew Dillon tp2.tm_sec = 0; 8036a6e350fSMatthew Dillon } 8046a6e350fSMatthew Dillon if (arg1 % (60 * 60) == 0) { 8056a6e350fSMatthew Dillon tp1.tm_min = 0; 8066a6e350fSMatthew Dillon tp2.tm_min = 0; 8076a6e350fSMatthew Dillon } 8086a6e350fSMatthew Dillon if (arg1 % (24 * 60 * 60) == 0) { 8096a6e350fSMatthew Dillon tp1.tm_hour = 0; 8106a6e350fSMatthew Dillon tp2.tm_hour = 0; 8116a6e350fSMatthew Dillon } 8126a6e350fSMatthew Dillon 8136a6e350fSMatthew Dillon baset = mktime(&tp1); 8146a6e350fSMatthew Dillon lastt = mktime(&tp2); 8156a6e350fSMatthew Dillon 8166a6e350fSMatthew Dillon #if 0 8176a6e350fSMatthew Dillon printf("%lld vs %lld\n", (long long)(baset - lastt), (long long)arg1); 8186a6e350fSMatthew Dillon #endif 8196a6e350fSMatthew Dillon 8206a6e350fSMatthew Dillon if ((int)(baset - lastt) >= arg1) 8216a6e350fSMatthew Dillon return(1); 8226a6e350fSMatthew Dillon return(0); 8236a6e350fSMatthew Dillon } 8246a6e350fSMatthew Dillon 8256a6e350fSMatthew Dillon /* 8266a6e350fSMatthew Dillon * Store the start time of the last successful operation. 8276a6e350fSMatthew Dillon */ 8286a6e350fSMatthew Dillon static void 8296a6e350fSMatthew Dillon save_period(const char *snapshots_path, const char *cmd, 8306a6e350fSMatthew Dillon time_t savet) 8316a6e350fSMatthew Dillon { 8326a6e350fSMatthew Dillon char *ocheck_path; 8336a6e350fSMatthew Dillon char *ncheck_path; 8346a6e350fSMatthew Dillon FILE *fp; 8356a6e350fSMatthew Dillon 8366a6e350fSMatthew Dillon asprintf(&ocheck_path, "%s/.%s.period", snapshots_path, cmd); 8376a6e350fSMatthew Dillon asprintf(&ncheck_path, "%s/.%s.period.new", snapshots_path, cmd); 8386a6e350fSMatthew Dillon fp = fopen(ncheck_path, "w"); 8393b9fbdeaSMatthew Dillon if (fp) { 8406a6e350fSMatthew Dillon fprintf(fp, "0x%08llx\n", (long long)savet); 8416a6e350fSMatthew Dillon if (fclose(fp) == 0) 8426a6e350fSMatthew Dillon rename(ncheck_path, ocheck_path); 8436a6e350fSMatthew Dillon remove(ncheck_path); 8443b9fbdeaSMatthew Dillon } else { 8453b9fbdeaSMatthew Dillon fprintf(stderr, "hammer: Unable to create period-file %s: %s\n", 8463b9fbdeaSMatthew Dillon ncheck_path, strerror(errno)); 8473b9fbdeaSMatthew Dillon } 8486a6e350fSMatthew Dillon } 8496a6e350fSMatthew Dillon 850ff1c9800SMatthew Dillon /* 851ff1c9800SMatthew Dillon * Simply count the number of softlinks in the snapshots dir 852ff1c9800SMatthew Dillon */ 853c6c298a7SMatthew Dillon static int 85483f2a3aaSMatthew Dillon check_softlinks(int fd, int new_config, const char *snapshots_path) 855c6c298a7SMatthew Dillon { 856c6c298a7SMatthew Dillon struct dirent *den; 857c6c298a7SMatthew Dillon struct stat st; 858c6c298a7SMatthew Dillon DIR *dir; 859c6c298a7SMatthew Dillon char *fpath; 860c6c298a7SMatthew Dillon int res = 0; 861c6c298a7SMatthew Dillon 86283f2a3aaSMatthew Dillon /* 86383f2a3aaSMatthew Dillon * Old-style softlink-based snapshots 86483f2a3aaSMatthew Dillon */ 865c6c298a7SMatthew Dillon if ((dir = opendir(snapshots_path)) != NULL) { 866c6c298a7SMatthew Dillon while ((den = readdir(dir)) != NULL) { 867c6c298a7SMatthew Dillon if (den->d_name[0] == '.') 868c6c298a7SMatthew Dillon continue; 869c6c298a7SMatthew Dillon asprintf(&fpath, "%s/%s", snapshots_path, den->d_name); 870c6c298a7SMatthew Dillon if (lstat(fpath, &st) == 0 && S_ISLNK(st.st_mode)) 871c6c298a7SMatthew Dillon ++res; 872c6c298a7SMatthew Dillon free(fpath); 873c6c298a7SMatthew Dillon } 874c6c298a7SMatthew Dillon closedir(dir); 875c6c298a7SMatthew Dillon } 87683f2a3aaSMatthew Dillon 87783f2a3aaSMatthew Dillon /* 87883f2a3aaSMatthew Dillon * New-style snapshots are stored as filesystem meta-data, 87983f2a3aaSMatthew Dillon * count those too. 88083f2a3aaSMatthew Dillon */ 88183f2a3aaSMatthew Dillon if (new_config) { 88283f2a3aaSMatthew Dillon struct hammer_ioc_snapshot snapshot; 88383f2a3aaSMatthew Dillon 88483f2a3aaSMatthew Dillon bzero(&snapshot, sizeof(snapshot)); 88583f2a3aaSMatthew Dillon do { 88683f2a3aaSMatthew Dillon if (ioctl(fd, HAMMERIOC_GET_SNAPSHOT, &snapshot) < 0) { 88783f2a3aaSMatthew Dillon err(2, "hammer cleanup: check_softlink " 88883f2a3aaSMatthew Dillon "snapshot error"); 88983f2a3aaSMatthew Dillon /* not reached */ 89083f2a3aaSMatthew Dillon } 89183f2a3aaSMatthew Dillon res += snapshot.count; 89283f2a3aaSMatthew Dillon } while (snapshot.head.error == 0 && snapshot.count); 89383f2a3aaSMatthew Dillon } 894c6c298a7SMatthew Dillon return (res); 895c6c298a7SMatthew Dillon } 896c6c298a7SMatthew Dillon 8976a6e350fSMatthew Dillon /* 898ff1c9800SMatthew Dillon * Clean up expired softlinks in the snapshots dir 899ff1c9800SMatthew Dillon */ 900ff1c9800SMatthew Dillon static void 90183f2a3aaSMatthew Dillon cleanup_softlinks(int fd, int new_config, 90283f2a3aaSMatthew Dillon const char *snapshots_path, int arg2, char *arg3) 903ff1c9800SMatthew Dillon { 904ff1c9800SMatthew Dillon struct dirent *den; 905ff1c9800SMatthew Dillon struct stat st; 906ff1c9800SMatthew Dillon DIR *dir; 907ff1c9800SMatthew Dillon char *fpath; 9085e435c92SMatthew Dillon int anylink = 0; 9095e435c92SMatthew Dillon 91009e1b0d6SMatthew Dillon if (arg3 != NULL && strstr(arg3, "any") != NULL) 9115e435c92SMatthew Dillon anylink = 1; 912ff1c9800SMatthew Dillon 913ff1c9800SMatthew Dillon if ((dir = opendir(snapshots_path)) != NULL) { 914ff1c9800SMatthew Dillon while ((den = readdir(dir)) != NULL) { 915ff1c9800SMatthew Dillon if (den->d_name[0] == '.') 916ff1c9800SMatthew Dillon continue; 917ff1c9800SMatthew Dillon asprintf(&fpath, "%s/%s", snapshots_path, den->d_name); 918ff1c9800SMatthew Dillon if (lstat(fpath, &st) == 0 && S_ISLNK(st.st_mode) && 9195e435c92SMatthew Dillon (anylink || strncmp(den->d_name, "snap-", 5) == 0) 9205e435c92SMatthew Dillon ) { 921ff1c9800SMatthew Dillon if (check_expired(den->d_name, arg2)) { 922ff1c9800SMatthew Dillon if (VerboseOpt) { 923ff1c9800SMatthew Dillon printf(" expire %s\n", 924ff1c9800SMatthew Dillon fpath); 925ff1c9800SMatthew Dillon } 926ff1c9800SMatthew Dillon remove(fpath); 927ff1c9800SMatthew Dillon } 928ff1c9800SMatthew Dillon } 929ff1c9800SMatthew Dillon free(fpath); 930ff1c9800SMatthew Dillon } 931ff1c9800SMatthew Dillon closedir(dir); 932ff1c9800SMatthew Dillon } 93383f2a3aaSMatthew Dillon 93483f2a3aaSMatthew Dillon /* 93583f2a3aaSMatthew Dillon * New-style snapshots are stored as filesystem meta-data, 93683f2a3aaSMatthew Dillon * count those too. 93783f2a3aaSMatthew Dillon */ 93883f2a3aaSMatthew Dillon if (new_config) { 93983f2a3aaSMatthew Dillon struct hammer_ioc_snapshot snapshot; 94083f2a3aaSMatthew Dillon struct hammer_ioc_snapshot dsnapshot; 94183f2a3aaSMatthew Dillon struct hammer_snapshot_data *snap; 94283f2a3aaSMatthew Dillon struct tm *tp; 94383f2a3aaSMatthew Dillon time_t t; 944ec2aa11dSMatthew Dillon time_t dt; 94583f2a3aaSMatthew Dillon char snapts[32]; 94683f2a3aaSMatthew Dillon u_int32_t i; 94783f2a3aaSMatthew Dillon 94883f2a3aaSMatthew Dillon bzero(&snapshot, sizeof(snapshot)); 94983f2a3aaSMatthew Dillon bzero(&dsnapshot, sizeof(dsnapshot)); 95083f2a3aaSMatthew Dillon do { 95183f2a3aaSMatthew Dillon if (ioctl(fd, HAMMERIOC_GET_SNAPSHOT, &snapshot) < 0) { 95283f2a3aaSMatthew Dillon err(2, "hammer cleanup: check_softlink " 95383f2a3aaSMatthew Dillon "snapshot error"); 95483f2a3aaSMatthew Dillon /* not reached */ 95583f2a3aaSMatthew Dillon } 95683f2a3aaSMatthew Dillon for (i = 0; i < snapshot.count; ++i) { 95783f2a3aaSMatthew Dillon snap = &snapshot.snaps[i]; 958ec2aa11dSMatthew Dillon t = snap->ts / 1000000ULL; 959ec2aa11dSMatthew Dillon dt = time(NULL) - t; 960ec2aa11dSMatthew Dillon if ((int)dt > arg2 || snap->tid == 0) { 96183f2a3aaSMatthew Dillon dsnapshot.snaps[dsnapshot.count++] = 96283f2a3aaSMatthew Dillon *snap; 96383f2a3aaSMatthew Dillon } 964ec2aa11dSMatthew Dillon if ((int)dt > arg2 && VerboseOpt) { 96583f2a3aaSMatthew Dillon tp = localtime(&t); 96683f2a3aaSMatthew Dillon strftime(snapts, sizeof(snapts), 96783f2a3aaSMatthew Dillon "%Y-%m-%d %H:%M:%S %Z", tp); 96883f2a3aaSMatthew Dillon printf(" expire 0x%016jx %s %s\n", 96983f2a3aaSMatthew Dillon (uintmax_t)snap->tid, 97083f2a3aaSMatthew Dillon snapts, 97183f2a3aaSMatthew Dillon snap->label); 97283f2a3aaSMatthew Dillon } 973ec2aa11dSMatthew Dillon if (dsnapshot.count == HAMMER_SNAPS_PER_IOCTL) 974ec2aa11dSMatthew Dillon delete_snapshots(fd, &dsnapshot); 97583f2a3aaSMatthew Dillon } 97683f2a3aaSMatthew Dillon } while (snapshot.head.error == 0 && snapshot.count); 97783f2a3aaSMatthew Dillon 978ec2aa11dSMatthew Dillon if (dsnapshot.count) 979ec2aa11dSMatthew Dillon delete_snapshots(fd, &dsnapshot); 98083f2a3aaSMatthew Dillon } 98183f2a3aaSMatthew Dillon } 982ec2aa11dSMatthew Dillon 983ec2aa11dSMatthew Dillon static void 984ec2aa11dSMatthew Dillon delete_snapshots(int fd, struct hammer_ioc_snapshot *dsnapshot) 985ec2aa11dSMatthew Dillon { 986ec2aa11dSMatthew Dillon for (;;) { 987ec2aa11dSMatthew Dillon if (ioctl(fd, HAMMERIOC_DEL_SNAPSHOT, dsnapshot) < 0) { 988ec2aa11dSMatthew Dillon printf(" Ioctl to delete snapshots failed: %s\n", 989ec2aa11dSMatthew Dillon strerror(errno)); 990ec2aa11dSMatthew Dillon break; 991ec2aa11dSMatthew Dillon } 992ec2aa11dSMatthew Dillon if (dsnapshot->head.error) { 993ec2aa11dSMatthew Dillon printf(" Ioctl to delete snapshots failed at " 994ec2aa11dSMatthew Dillon "snap=%016jx: %s\n", 995ec2aa11dSMatthew Dillon dsnapshot->snaps[dsnapshot->index].tid, 996ec2aa11dSMatthew Dillon strerror(dsnapshot->head.error)); 997ec2aa11dSMatthew Dillon if (++dsnapshot->index < dsnapshot->count) 998ec2aa11dSMatthew Dillon continue; 999ec2aa11dSMatthew Dillon } 1000ec2aa11dSMatthew Dillon break; 1001ec2aa11dSMatthew Dillon } 1002ec2aa11dSMatthew Dillon dsnapshot->index = 0; 1003ec2aa11dSMatthew Dillon dsnapshot->count = 0; 1004ec2aa11dSMatthew Dillon dsnapshot->head.error = 0; 1005ff1c9800SMatthew Dillon } 1006ff1c9800SMatthew Dillon 1007ff1c9800SMatthew Dillon /* 1008ff1c9800SMatthew Dillon * Take a softlink path in the form snap-yyyymmdd-hhmm and the 1009ff1c9800SMatthew Dillon * expiration in seconds (arg2) and return non-zero if the softlink 1010ff1c9800SMatthew Dillon * has expired. 1011ff1c9800SMatthew Dillon */ 1012ff1c9800SMatthew Dillon static int 1013ff1c9800SMatthew Dillon check_expired(const char *fpath, int arg2) 1014ff1c9800SMatthew Dillon { 1015ff1c9800SMatthew Dillon struct tm tm; 1016ff1c9800SMatthew Dillon time_t t; 1017ff1c9800SMatthew Dillon int year; 1018ff1c9800SMatthew Dillon int month; 10195e435c92SMatthew Dillon int day = 0; 10205e435c92SMatthew Dillon int hour = 0; 10215e435c92SMatthew Dillon int minute = 0; 1022ff1c9800SMatthew Dillon int r; 1023ff1c9800SMatthew Dillon 10245e435c92SMatthew Dillon while (*fpath && *fpath != '-' && *fpath != '.') 10255e435c92SMatthew Dillon ++fpath; 10265e435c92SMatthew Dillon if (*fpath) 10275e435c92SMatthew Dillon ++fpath; 10285e435c92SMatthew Dillon 10295e435c92SMatthew Dillon r = sscanf(fpath, "%4d%2d%2d-%2d%2d", 1030ff1c9800SMatthew Dillon &year, &month, &day, &hour, &minute); 10315e435c92SMatthew Dillon 10325e435c92SMatthew Dillon if (r >= 3) { 1033ff1c9800SMatthew Dillon bzero(&tm, sizeof(tm)); 1034ff1c9800SMatthew Dillon tm.tm_isdst = -1; 1035ff1c9800SMatthew Dillon tm.tm_min = minute; 1036ff1c9800SMatthew Dillon tm.tm_hour = hour; 1037ff1c9800SMatthew Dillon tm.tm_mday = day; 1038ff1c9800SMatthew Dillon tm.tm_mon = month - 1; 1039ff1c9800SMatthew Dillon tm.tm_year = year - 1900; 10405e435c92SMatthew Dillon t = mktime(&tm); 10415e435c92SMatthew Dillon if (t == (time_t)-1) 10425e435c92SMatthew Dillon return(0); 10435e435c92SMatthew Dillon t = time(NULL) - t; 1044ff1c9800SMatthew Dillon if ((int)t > arg2) 1045ff1c9800SMatthew Dillon return(1); 1046ff1c9800SMatthew Dillon } 1047ff1c9800SMatthew Dillon return(0); 1048ff1c9800SMatthew Dillon } 1049ff1c9800SMatthew Dillon 1050ff1c9800SMatthew Dillon /* 10516a6e350fSMatthew Dillon * Issue a snapshot. 10526a6e350fSMatthew Dillon */ 10536a6e350fSMatthew Dillon static int 1054b5ec5ad4SMatthew Dillon create_snapshot(const char *path, const char *snapshots_path) 10556a6e350fSMatthew Dillon { 10566a6e350fSMatthew Dillon int r; 10576a6e350fSMatthew Dillon 1058ff1c9800SMatthew Dillon runcmd(&r, "hammer snapshot %s %s", path, snapshots_path); 10596a6e350fSMatthew Dillon return(r); 10606a6e350fSMatthew Dillon } 10616a6e350fSMatthew Dillon 10626a6e350fSMatthew Dillon static int 1063eb5751f6SMatthew Dillon cleanup_prune(const char *path, const char *snapshots_path, 1064c6c298a7SMatthew Dillon int arg1 __unused, int arg2, int snapshots_disabled) 10656a6e350fSMatthew Dillon { 1066eb5751f6SMatthew Dillon const char *path_or_snapshots_path; 1067eb5751f6SMatthew Dillon struct softprune *base = NULL; 1068eb5751f6SMatthew Dillon struct hammer_ioc_prune dummy_template; 1069eb5751f6SMatthew Dillon 1070eb5751f6SMatthew Dillon bzero(&dummy_template, sizeof(dummy_template)); 1071eb5751f6SMatthew Dillon hammer_softprune_scandir(&base, &dummy_template, snapshots_path); 1072eb5751f6SMatthew Dillon 1073eb5751f6SMatthew Dillon /* 1074eb5751f6SMatthew Dillon * If the snapshots_path (e.g. /var/hammer/...) has no snapshots 1075eb5751f6SMatthew Dillon * in it then prune will get confused and prune the filesystem 1076eb5751f6SMatthew Dillon * containing the snapshots_path instead of the requested 1077eb5751f6SMatthew Dillon * filesystem. De-confuse prune. We need a better way. 1078eb5751f6SMatthew Dillon */ 1079eb5751f6SMatthew Dillon path_or_snapshots_path = base ? snapshots_path : path; 1080eb5751f6SMatthew Dillon 1081c6c298a7SMatthew Dillon /* 1082c6c298a7SMatthew Dillon * If snapshots have been disabled run prune-everything instead 1083c6c298a7SMatthew Dillon * of prune. 1084c6c298a7SMatthew Dillon */ 1085c6c298a7SMatthew Dillon if (snapshots_disabled && arg2) { 1086eb5751f6SMatthew Dillon runcmd(NULL, 1087eb5751f6SMatthew Dillon "hammer -c %s/.prune.cycle -t %d prune-everything %s", 1088c6c298a7SMatthew Dillon snapshots_path, arg2, path); 1089c6c298a7SMatthew Dillon } else if (snapshots_disabled) { 1090c6c298a7SMatthew Dillon runcmd(NULL, "hammer prune-everything %s", path); 1091c6c298a7SMatthew Dillon } else if (arg2) { 10926a6e350fSMatthew Dillon runcmd(NULL, "hammer -c %s/.prune.cycle -t %d prune %s", 1093eb5751f6SMatthew Dillon snapshots_path, arg2, path_or_snapshots_path); 10946a6e350fSMatthew Dillon } else { 1095eb5751f6SMatthew Dillon runcmd(NULL, "hammer prune %s", path_or_snapshots_path); 10966a6e350fSMatthew Dillon } 10976a6e350fSMatthew Dillon return(0); 10986a6e350fSMatthew Dillon } 10996a6e350fSMatthew Dillon 11006a6e350fSMatthew Dillon static int 11010b8bd7daSMatthew Dillon cleanup_rebalance(const char *path, const char *snapshots_path, 11020b8bd7daSMatthew Dillon int arg1 __unused, int arg2) 11030b8bd7daSMatthew Dillon { 11040b8bd7daSMatthew Dillon if (VerboseOpt == 0) { 11050b8bd7daSMatthew Dillon printf("."); 11060b8bd7daSMatthew Dillon fflush(stdout); 11070b8bd7daSMatthew Dillon } 11080b8bd7daSMatthew Dillon 11090b8bd7daSMatthew Dillon runcmd(NULL, 11100b8bd7daSMatthew Dillon "hammer -c %s/.rebalance.cycle -t %d rebalance %s", 11110b8bd7daSMatthew Dillon snapshots_path, arg2, path); 11120b8bd7daSMatthew Dillon if (VerboseOpt == 0) { 11130b8bd7daSMatthew Dillon printf("."); 11140b8bd7daSMatthew Dillon fflush(stdout); 11150b8bd7daSMatthew Dillon } 11160b8bd7daSMatthew Dillon if (VerboseOpt == 0) 11170b8bd7daSMatthew Dillon printf("\n"); 11180b8bd7daSMatthew Dillon return(0); 11190b8bd7daSMatthew Dillon } 11200b8bd7daSMatthew Dillon 11210b8bd7daSMatthew Dillon static int 11226a6e350fSMatthew Dillon cleanup_reblock(const char *path, const char *snapshots_path, 11236a6e350fSMatthew Dillon int arg1 __unused, int arg2) 11246a6e350fSMatthew Dillon { 11256a6e350fSMatthew Dillon if (VerboseOpt == 0) { 11266a6e350fSMatthew Dillon printf("."); 11276a6e350fSMatthew Dillon fflush(stdout); 11286a6e350fSMatthew Dillon } 1129797a0b63SMatthew Dillon 1130797a0b63SMatthew Dillon /* 1131797a0b63SMatthew Dillon * When reblocking the B-Tree always reblock everything in normal 1132797a0b63SMatthew Dillon * mode. 1133797a0b63SMatthew Dillon */ 11346a6e350fSMatthew Dillon runcmd(NULL, 1135797a0b63SMatthew Dillon "hammer -c %s/.reblock-1.cycle -t %d reblock-btree %s", 11366a6e350fSMatthew Dillon snapshots_path, arg2, path); 11376a6e350fSMatthew Dillon if (VerboseOpt == 0) { 11386a6e350fSMatthew Dillon printf("."); 11396a6e350fSMatthew Dillon fflush(stdout); 11406a6e350fSMatthew Dillon } 1141797a0b63SMatthew Dillon 1142797a0b63SMatthew Dillon /* 1143797a0b63SMatthew Dillon * When reblocking the inodes always reblock everything in normal 1144797a0b63SMatthew Dillon * mode. 1145797a0b63SMatthew Dillon */ 11466a6e350fSMatthew Dillon runcmd(NULL, 1147797a0b63SMatthew Dillon "hammer -c %s/.reblock-2.cycle -t %d reblock-inodes %s", 11486a6e350fSMatthew Dillon snapshots_path, arg2, path); 11496a6e350fSMatthew Dillon if (VerboseOpt == 0) { 11506a6e350fSMatthew Dillon printf("."); 11516a6e350fSMatthew Dillon fflush(stdout); 11526a6e350fSMatthew Dillon } 1153797a0b63SMatthew Dillon 1154797a0b63SMatthew Dillon /* 1155797a0b63SMatthew Dillon * When reblocking the directories always reblock everything in normal 1156797a0b63SMatthew Dillon * mode. 1157797a0b63SMatthew Dillon */ 1158797a0b63SMatthew Dillon runcmd(NULL, 1159797a0b63SMatthew Dillon "hammer -c %s/.reblock-4.cycle -t %d reblock-dirs %s", 1160797a0b63SMatthew Dillon snapshots_path, arg2, path); 1161797a0b63SMatthew Dillon if (VerboseOpt == 0) { 1162797a0b63SMatthew Dillon printf("."); 1163797a0b63SMatthew Dillon fflush(stdout); 1164797a0b63SMatthew Dillon } 1165797a0b63SMatthew Dillon 1166797a0b63SMatthew Dillon /* 1167797a0b63SMatthew Dillon * Do not reblock all the data in normal mode. 1168797a0b63SMatthew Dillon */ 11696a6e350fSMatthew Dillon runcmd(NULL, 11706a6e350fSMatthew Dillon "hammer -c %s/.reblock-3.cycle -t %d reblock-data %s 95", 11716a6e350fSMatthew Dillon snapshots_path, arg2, path); 11726a6e350fSMatthew Dillon if (VerboseOpt == 0) 11736a6e350fSMatthew Dillon printf("\n"); 11746a6e350fSMatthew Dillon return(0); 11756a6e350fSMatthew Dillon } 11766a6e350fSMatthew Dillon 11776a6e350fSMatthew Dillon static int 11786a6e350fSMatthew Dillon cleanup_recopy(const char *path, const char *snapshots_path, 11796a6e350fSMatthew Dillon int arg1 __unused, int arg2) 11806a6e350fSMatthew Dillon { 11816a6e350fSMatthew Dillon if (VerboseOpt == 0) { 11826a6e350fSMatthew Dillon printf("."); 11836a6e350fSMatthew Dillon fflush(stdout); 11846a6e350fSMatthew Dillon } 11856a6e350fSMatthew Dillon runcmd(NULL, 11866a6e350fSMatthew Dillon "hammer -c %s/.recopy-1.cycle -t %d reblock-btree %s", 11876a6e350fSMatthew Dillon snapshots_path, arg2, path); 11886a6e350fSMatthew Dillon if (VerboseOpt == 0) { 11896a6e350fSMatthew Dillon printf("."); 11906a6e350fSMatthew Dillon fflush(stdout); 11916a6e350fSMatthew Dillon } 11926a6e350fSMatthew Dillon runcmd(NULL, 11936a6e350fSMatthew Dillon "hammer -c %s/.recopy-2.cycle -t %d reblock-inodes %s", 11946a6e350fSMatthew Dillon snapshots_path, arg2, path); 11956a6e350fSMatthew Dillon if (VerboseOpt == 0) { 11966a6e350fSMatthew Dillon printf("."); 11976a6e350fSMatthew Dillon fflush(stdout); 11986a6e350fSMatthew Dillon } 11996a6e350fSMatthew Dillon runcmd(NULL, 1200797a0b63SMatthew Dillon "hammer -c %s/.recopy-4.cycle -t %d reblock-dirs %s", 1201797a0b63SMatthew Dillon snapshots_path, arg2, path); 1202797a0b63SMatthew Dillon if (VerboseOpt == 0) { 1203797a0b63SMatthew Dillon printf("."); 1204797a0b63SMatthew Dillon fflush(stdout); 1205797a0b63SMatthew Dillon } 1206797a0b63SMatthew Dillon runcmd(NULL, 12076a6e350fSMatthew Dillon "hammer -c %s/.recopy-3.cycle -t %d reblock-data %s", 12086a6e350fSMatthew Dillon snapshots_path, arg2, path); 12096a6e350fSMatthew Dillon if (VerboseOpt == 0) 12106a6e350fSMatthew Dillon printf("\n"); 12116a6e350fSMatthew Dillon return(0); 12126a6e350fSMatthew Dillon } 12136a6e350fSMatthew Dillon 1214bb29b5d8SMatthew Dillon static int 1215bb29b5d8SMatthew Dillon cleanup_dedup(const char *path, const char *snapshots_path __unused, 1216bb29b5d8SMatthew Dillon int arg1 __unused, int arg2) 1217bb29b5d8SMatthew Dillon { 1218bb29b5d8SMatthew Dillon if (VerboseOpt == 0) { 1219bb29b5d8SMatthew Dillon printf("."); 1220bb29b5d8SMatthew Dillon fflush(stdout); 1221bb29b5d8SMatthew Dillon } 1222bb29b5d8SMatthew Dillon 1223bb29b5d8SMatthew Dillon runcmd(NULL, "hammer -t %d dedup %s", arg2, path); 1224bb29b5d8SMatthew Dillon if (VerboseOpt == 0) { 1225bb29b5d8SMatthew Dillon printf("."); 1226bb29b5d8SMatthew Dillon fflush(stdout); 1227bb29b5d8SMatthew Dillon } 1228bb29b5d8SMatthew Dillon if (VerboseOpt == 0) 1229bb29b5d8SMatthew Dillon printf("\n"); 1230bb29b5d8SMatthew Dillon return(0); 1231bb29b5d8SMatthew Dillon } 1232bb29b5d8SMatthew Dillon 12336a6e350fSMatthew Dillon static 12346a6e350fSMatthew Dillon void 12356a6e350fSMatthew Dillon runcmd(int *resp, const char *ctl, ...) 12366a6e350fSMatthew Dillon { 12376a6e350fSMatthew Dillon va_list va; 12386a6e350fSMatthew Dillon char *cmd; 12396a6e350fSMatthew Dillon char *arg; 12406a6e350fSMatthew Dillon char **av; 12416a6e350fSMatthew Dillon int n; 12426a6e350fSMatthew Dillon int nmax; 12436a6e350fSMatthew Dillon int res; 12446a6e350fSMatthew Dillon pid_t pid; 12456a6e350fSMatthew Dillon 12466a6e350fSMatthew Dillon /* 12476a6e350fSMatthew Dillon * Generate the command 12486a6e350fSMatthew Dillon */ 12496a6e350fSMatthew Dillon va_start(va, ctl); 12506a6e350fSMatthew Dillon vasprintf(&cmd, ctl, va); 12516a6e350fSMatthew Dillon va_end(va); 12526a6e350fSMatthew Dillon if (VerboseOpt) 12536a6e350fSMatthew Dillon printf(" %s\n", cmd); 12546a6e350fSMatthew Dillon 12556a6e350fSMatthew Dillon /* 12566a6e350fSMatthew Dillon * Break us down into arguments. We do not just use system() here 12576a6e350fSMatthew Dillon * because it blocks SIGINT and friends. 12586a6e350fSMatthew Dillon */ 12596a6e350fSMatthew Dillon n = 0; 12606a6e350fSMatthew Dillon nmax = 16; 12616a6e350fSMatthew Dillon av = malloc(sizeof(char *) * nmax); 12626a6e350fSMatthew Dillon 12636a6e350fSMatthew Dillon for (arg = strtok(cmd, WS); arg; arg = strtok(NULL, WS)) { 1264c453712aSMatthew Dillon if (n == nmax - 1) { 12656a6e350fSMatthew Dillon nmax += 16; 12666a6e350fSMatthew Dillon av = realloc(av, sizeof(char *) * nmax); 12676a6e350fSMatthew Dillon } 12686a6e350fSMatthew Dillon av[n++] = arg; 12696a6e350fSMatthew Dillon } 1270c453712aSMatthew Dillon av[n++] = NULL; 12716a6e350fSMatthew Dillon 12726a6e350fSMatthew Dillon /* 12736a6e350fSMatthew Dillon * Run the command. 12746a6e350fSMatthew Dillon */ 1275445faa69SMatthew Dillon RunningIoctl = 1; 12766a6e350fSMatthew Dillon if ((pid = fork()) == 0) { 12776a6e350fSMatthew Dillon if (VerboseOpt < 2) { 12786a6e350fSMatthew Dillon int fd = open("/dev/null", O_RDWR); 12796a6e350fSMatthew Dillon dup2(fd, 1); 12806a6e350fSMatthew Dillon close(fd); 12816a6e350fSMatthew Dillon } 12826a6e350fSMatthew Dillon execvp(av[0], av); 12836a6e350fSMatthew Dillon _exit(127); 12846a6e350fSMatthew Dillon } else if (pid < 0) { 12856a6e350fSMatthew Dillon res = 127; 12866a6e350fSMatthew Dillon } else { 12876a6e350fSMatthew Dillon int status; 1288445faa69SMatthew Dillon 12896a6e350fSMatthew Dillon while (waitpid(pid, &status, 0) != pid) 12906a6e350fSMatthew Dillon ; 12916a6e350fSMatthew Dillon res = WEXITSTATUS(status); 12926a6e350fSMatthew Dillon } 1293445faa69SMatthew Dillon RunningIoctl = 0; 1294445faa69SMatthew Dillon if (DidInterrupt) 1295445faa69SMatthew Dillon _exit(1); 12966a6e350fSMatthew Dillon 12976a6e350fSMatthew Dillon free(cmd); 12986a6e350fSMatthew Dillon free(av); 12996a6e350fSMatthew Dillon if (resp) 13006a6e350fSMatthew Dillon *resp = res; 13016a6e350fSMatthew Dillon } 1302