xref: /dflybsd-src/sbin/hammer/cmd_pfs.c (revision 207ba6700e31c36bb4e38d4da221a4f86e466ee9)
14daef0ddSTomohiro Kusumi /*
24daef0ddSTomohiro Kusumi  * Copyright (c) 2008 The DragonFly Project.  All rights reserved.
34daef0ddSTomohiro Kusumi  *
44daef0ddSTomohiro Kusumi  * This code is derived from software contributed to The DragonFly Project
54daef0ddSTomohiro Kusumi  * by Matthew Dillon <dillon@backplane.com>
64daef0ddSTomohiro Kusumi  *
74daef0ddSTomohiro Kusumi  * Redistribution and use in source and binary forms, with or without
84daef0ddSTomohiro Kusumi  * modification, are permitted provided that the following conditions
94daef0ddSTomohiro Kusumi  * are met:
104daef0ddSTomohiro Kusumi  *
114daef0ddSTomohiro Kusumi  * 1. Redistributions of source code must retain the above copyright
124daef0ddSTomohiro Kusumi  *    notice, this list of conditions and the following disclaimer.
134daef0ddSTomohiro Kusumi  * 2. Redistributions in binary form must reproduce the above copyright
144daef0ddSTomohiro Kusumi  *    notice, this list of conditions and the following disclaimer in
154daef0ddSTomohiro Kusumi  *    the documentation and/or other materials provided with the
164daef0ddSTomohiro Kusumi  *    distribution.
174daef0ddSTomohiro Kusumi  * 3. Neither the name of The DragonFly Project nor the names of its
184daef0ddSTomohiro Kusumi  *    contributors may be used to endorse or promote products derived
194daef0ddSTomohiro Kusumi  *    from this software without specific, prior written permission.
204daef0ddSTomohiro Kusumi  *
214daef0ddSTomohiro Kusumi  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
224daef0ddSTomohiro Kusumi  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
234daef0ddSTomohiro Kusumi  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
244daef0ddSTomohiro Kusumi  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
254daef0ddSTomohiro Kusumi  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
264daef0ddSTomohiro Kusumi  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
274daef0ddSTomohiro Kusumi  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
284daef0ddSTomohiro Kusumi  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
294daef0ddSTomohiro Kusumi  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
304daef0ddSTomohiro Kusumi  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
314daef0ddSTomohiro Kusumi  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
324daef0ddSTomohiro Kusumi  * SUCH DAMAGE.
334daef0ddSTomohiro Kusumi  *
344daef0ddSTomohiro Kusumi  * $DragonFly: src/sbin/hammer/cmd_pseudofs.c,v 1.12 2008/10/08 21:01:54 thomas Exp $
354daef0ddSTomohiro Kusumi  */
364daef0ddSTomohiro Kusumi 
374daef0ddSTomohiro Kusumi #include "hammer.h"
384daef0ddSTomohiro Kusumi 
394daef0ddSTomohiro Kusumi static int scanpfsid(struct hammer_ioc_pseudofs_rw *pfs, const char *path);
404daef0ddSTomohiro Kusumi static void parse_pfsd_options(char **av, int ac, hammer_pseudofs_data_t pfsd);
414daef0ddSTomohiro Kusumi static void init_pfsd(hammer_pseudofs_data_t pfsd, int is_slave);
424daef0ddSTomohiro Kusumi static void pseudofs_usage(int code);
434daef0ddSTomohiro Kusumi static int timetosecs(char *str);
444daef0ddSTomohiro Kusumi 
451b23fc22STomohiro Kusumi void
clrpfs(struct hammer_ioc_pseudofs_rw * pfs,hammer_pseudofs_data_t pfsd,int pfs_id)461b23fc22STomohiro Kusumi clrpfs(struct hammer_ioc_pseudofs_rw *pfs, hammer_pseudofs_data_t pfsd,
471b23fc22STomohiro Kusumi 	int pfs_id)
481b23fc22STomohiro Kusumi {
491b23fc22STomohiro Kusumi 	bzero(pfs, sizeof(*pfs));
501b23fc22STomohiro Kusumi 
511b23fc22STomohiro Kusumi 	if (pfsd)
521b23fc22STomohiro Kusumi 		pfs->ondisk = pfsd;
531b23fc22STomohiro Kusumi 	else
541b23fc22STomohiro Kusumi 		pfs->ondisk = malloc(sizeof(*pfs->ondisk));
551b23fc22STomohiro Kusumi 	bzero(pfs->ondisk, sizeof(*pfs->ondisk));
561b23fc22STomohiro Kusumi 
571b23fc22STomohiro Kusumi 	pfs->pfs_id = pfs_id;
581b23fc22STomohiro Kusumi 	pfs->bytes = sizeof(*pfs->ondisk);
591b23fc22STomohiro Kusumi 	pfs->version = HAMMER_IOC_PSEUDOFS_VERSION;
601b23fc22STomohiro Kusumi }
611b23fc22STomohiro Kusumi 
624daef0ddSTomohiro Kusumi /*
63578f0d56STomohiro Kusumi  * If path is a symlink, return strdup'd path.
64578f0d56STomohiro Kusumi  * If it's a directory via symlink, strip trailing /
65578f0d56STomohiro Kusumi  * from strdup'd path and return the symlink.
66578f0d56STomohiro Kusumi  */
67005a4da7STomohiro Kusumi static
68005a4da7STomohiro Kusumi char*
getlink(const char * path)69578f0d56STomohiro Kusumi getlink(const char *path)
70578f0d56STomohiro Kusumi {
71578f0d56STomohiro Kusumi 	int i;
72578f0d56STomohiro Kusumi 	char *linkpath;
73578f0d56STomohiro Kusumi 	struct stat st;
74578f0d56STomohiro Kusumi 
75578f0d56STomohiro Kusumi 	if (lstat(path, &st))
76578f0d56STomohiro Kusumi 		return(NULL);
77578f0d56STomohiro Kusumi 	linkpath = strdup(path);
78578f0d56STomohiro Kusumi 
79578f0d56STomohiro Kusumi 	if (S_ISDIR(st.st_mode)) {
80578f0d56STomohiro Kusumi 		i = strlen(linkpath) - 1;
81578f0d56STomohiro Kusumi 		while (i > 0 && linkpath[i] == '/')
82578f0d56STomohiro Kusumi 			linkpath[i--] = 0;
83578f0d56STomohiro Kusumi 		lstat(linkpath, &st);
84578f0d56STomohiro Kusumi 	}
8552e2f1b5STomohiro Kusumi 	if (S_ISLNK(st.st_mode))
86578f0d56STomohiro Kusumi 		return(linkpath);
87578f0d56STomohiro Kusumi 
88578f0d56STomohiro Kusumi 	free(linkpath);
89578f0d56STomohiro Kusumi 	return(NULL);
90578f0d56STomohiro Kusumi }
91578f0d56STomohiro Kusumi 
92578f0d56STomohiro Kusumi /*
9314331391STomohiro Kusumi  * Calculate the PFS id given a path to a file/directory or
9414331391STomohiro Kusumi  * a @@%llx:%d softlink.
954daef0ddSTomohiro Kusumi  */
964daef0ddSTomohiro Kusumi int
getpfs(struct hammer_ioc_pseudofs_rw * pfs,const char * path)97578f0d56STomohiro Kusumi getpfs(struct hammer_ioc_pseudofs_rw *pfs, const char *path)
984daef0ddSTomohiro Kusumi {
99*207ba670SMatthew Dillon 	char *tmpbuf;
1004daef0ddSTomohiro Kusumi 	int fd;
1014daef0ddSTomohiro Kusumi 
1021b23fc22STomohiro Kusumi 	clrpfs(pfs, NULL, -1);
1034daef0ddSTomohiro Kusumi 
1044daef0ddSTomohiro Kusumi 	/*
105445b5ba6STomohiro Kusumi 	 * Extract the PFS id.
106a5ff7917STomohiro Kusumi 	 * dirname(path) is supposed to be a directory in root PFS.
107*207ba670SMatthew Dillon 	 *
108*207ba670SMatthew Dillon 	 * strips trailing / first if any.
109445b5ba6STomohiro Kusumi 	 */
110*207ba670SMatthew Dillon 	tmpbuf = strdup(path);
11152e2f1b5STomohiro Kusumi 	if (scanpfsid(pfs, path) == 0)
112*207ba670SMatthew Dillon 		path = dirname(tmpbuf);
113445b5ba6STomohiro Kusumi 
114445b5ba6STomohiro Kusumi 	/*
115445b5ba6STomohiro Kusumi 	 * Open the path regardless of scanpfsid() result, since some
116445b5ba6STomohiro Kusumi 	 * commands can take a regular file/directory (e.g. pfs-status).
1174daef0ddSTomohiro Kusumi 	 */
1184daef0ddSTomohiro Kusumi 	fd = open(path, O_RDONLY);
119052fd72bSTomohiro Kusumi 	if (fd < 0) {
12002318f07STomohiro Kusumi 		err(1, "Failed to open %s", path);
121052fd72bSTomohiro Kusumi 		/* not reached */
122052fd72bSTomohiro Kusumi 	}
1234daef0ddSTomohiro Kusumi 
1244daef0ddSTomohiro Kusumi 	/*
125ec206eb9STomohiro Kusumi 	 * If pfs.pfs_id has been set to non -1, the file descriptor fd
1264daef0ddSTomohiro Kusumi 	 * could be any fd of HAMMER inodes since HAMMERIOC_GET_PSEUDOFS
1274daef0ddSTomohiro Kusumi 	 * doesn't depend on inode attributes if it's set to a valid id.
1284daef0ddSTomohiro Kusumi 	 */
129052fd72bSTomohiro Kusumi 	if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, pfs) < 0) {
13002318f07STomohiro Kusumi 		err(1, "Cannot access %s", path);
131052fd72bSTomohiro Kusumi 		/* not reached */
132052fd72bSTomohiro Kusumi 	}
13302318f07STomohiro Kusumi 
134*207ba670SMatthew Dillon 	free(tmpbuf);
135*207ba670SMatthew Dillon 
1364daef0ddSTomohiro Kusumi 	return(fd);
1374daef0ddSTomohiro Kusumi }
1384daef0ddSTomohiro Kusumi 
1394daef0ddSTomohiro Kusumi /*
140445b5ba6STomohiro Kusumi  * Extract the PFS id from path.
1414daef0ddSTomohiro Kusumi  */
142005a4da7STomohiro Kusumi static
143005a4da7STomohiro Kusumi int
scanpfsid(struct hammer_ioc_pseudofs_rw * pfs,const char * path)1444daef0ddSTomohiro Kusumi scanpfsid(struct hammer_ioc_pseudofs_rw *pfs, const char *path)
1454daef0ddSTomohiro Kusumi {
146578f0d56STomohiro Kusumi 	char *linkpath;
147*207ba670SMatthew Dillon 	char *tmpbuf;
1484daef0ddSTomohiro Kusumi 	char buf[64];
1494daef0ddSTomohiro Kusumi 	uintmax_t dummy_tid;
1504daef0ddSTomohiro Kusumi 	struct stat st;
1514daef0ddSTomohiro Kusumi 
15252e2f1b5STomohiro Kusumi 	if (stat(path, &st))
15352e2f1b5STomohiro Kusumi 		; /* possibly slave PFS */
15452e2f1b5STomohiro Kusumi 	else if (S_ISDIR(st.st_mode))
15552e2f1b5STomohiro Kusumi 		; /* possibly master or slave PFS */
15652e2f1b5STomohiro Kusumi 	else
15740649c3eSTomohiro Kusumi 		return(-1);  /* neither */
1584daef0ddSTomohiro Kusumi 
159578f0d56STomohiro Kusumi 	linkpath = getlink(path);
160578f0d56STomohiro Kusumi 	if (linkpath) {
1614daef0ddSTomohiro Kusumi 		/*
162578f0d56STomohiro Kusumi 		 * Read the symlink assuming it's a link to PFS.
1634daef0ddSTomohiro Kusumi 		 */
16440649c3eSTomohiro Kusumi 		bzero(buf, sizeof(buf));
165578f0d56STomohiro Kusumi 		if (readlink(linkpath, buf, sizeof(buf) - 1) < 0) {
166578f0d56STomohiro Kusumi 			free(linkpath);
16740649c3eSTomohiro Kusumi 			return(-1);
168578f0d56STomohiro Kusumi 		}
169578f0d56STomohiro Kusumi 		free(linkpath);
170445b5ba6STomohiro Kusumi 		path = buf;
1714daef0ddSTomohiro Kusumi 	}
1724daef0ddSTomohiro Kusumi 
1734daef0ddSTomohiro Kusumi 	/*
1744daef0ddSTomohiro Kusumi 	 * The symlink created by pfs-master|slave is just a symlink.
1754daef0ddSTomohiro Kusumi 	 * One could happen to remove a symlink and relink PFS as
1764daef0ddSTomohiro Kusumi 	 * # ln -s ./@@-1:00001 ./link
1774daef0ddSTomohiro Kusumi 	 * which results PFS having something extra before @@.
1784daef0ddSTomohiro Kusumi 	 * One could also directly use the PFS and results the same.
1794daef0ddSTomohiro Kusumi 	 * Get rid of it before we extract the PFS id.
1804daef0ddSTomohiro Kusumi 	 */
181*207ba670SMatthew Dillon 	tmpbuf = strdup(path);
182445b5ba6STomohiro Kusumi 	if (strchr(path, '/')) {
183*207ba670SMatthew Dillon 		path = basename(tmpbuf); /* strips trailing / first if any */
184052fd72bSTomohiro Kusumi 		if (path == NULL) {
1854daef0ddSTomohiro Kusumi 			err(1, "basename");
186052fd72bSTomohiro Kusumi 			/* not reached */
187052fd72bSTomohiro Kusumi 		}
1884daef0ddSTomohiro Kusumi 	}
1894daef0ddSTomohiro Kusumi 
1904daef0ddSTomohiro Kusumi 	/*
191445b5ba6STomohiro Kusumi 	 * Test and extract the PFS id from the link.
1925dce36edSTomohiro Kusumi 	 * "@@%jx:%d" covers both "@@-1:%05d" format for master PFS
19314331391STomohiro Kusumi 	 * and "@@0x%016jx:%05d" format for slave PFS.
1944daef0ddSTomohiro Kusumi 	 */
195445b5ba6STomohiro Kusumi 	if (sscanf(path, "@@%jx:%d", &dummy_tid, &pfs->pfs_id) == 2) {
196*207ba670SMatthew Dillon 		free(tmpbuf);
197445b5ba6STomohiro Kusumi 		assert(pfs->pfs_id > 0);
198445b5ba6STomohiro Kusumi 		return(0);
199dc17a1e0STomohiro Kusumi 	}
200*207ba670SMatthew Dillon 	free(tmpbuf);
20114331391STomohiro Kusumi 
202445b5ba6STomohiro Kusumi 	return(-1);
2034daef0ddSTomohiro Kusumi }
2044daef0ddSTomohiro Kusumi 
2054daef0ddSTomohiro Kusumi void
relpfs(int fd,struct hammer_ioc_pseudofs_rw * pfs)2064daef0ddSTomohiro Kusumi relpfs(int fd, struct hammer_ioc_pseudofs_rw *pfs)
2074daef0ddSTomohiro Kusumi {
2084daef0ddSTomohiro Kusumi 	if (fd >= 0)
2094daef0ddSTomohiro Kusumi 		close(fd);
2104daef0ddSTomohiro Kusumi 	if (pfs->ondisk) {
2114daef0ddSTomohiro Kusumi 		free(pfs->ondisk);
2124daef0ddSTomohiro Kusumi 		pfs->ondisk = NULL;
2134daef0ddSTomohiro Kusumi 	}
2144daef0ddSTomohiro Kusumi }
2154daef0ddSTomohiro Kusumi 
216005a4da7STomohiro Kusumi static
217005a4da7STomohiro Kusumi void
print_pfs_status(char * path)218fdba9548STomohiro Kusumi print_pfs_status(char *path)
2194daef0ddSTomohiro Kusumi {
2204daef0ddSTomohiro Kusumi 	struct hammer_ioc_pseudofs_rw pfs;
2214daef0ddSTomohiro Kusumi 	int fd;
2224daef0ddSTomohiro Kusumi 
223fdba9548STomohiro Kusumi 	fd = getpfs(&pfs, path);
224fdba9548STomohiro Kusumi 	printf("%s\t", path);
2254daef0ddSTomohiro Kusumi 	if (fd < 0 || ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) < 0) {
226fdba9548STomohiro Kusumi 		printf("Invalid PFS path %s\n", path);
2274daef0ddSTomohiro Kusumi 	} else {
2284daef0ddSTomohiro Kusumi 		printf("PFS#%d {\n", pfs.pfs_id);
2294daef0ddSTomohiro Kusumi 		dump_pfsd(pfs.ondisk, fd);
2304daef0ddSTomohiro Kusumi 		printf("}\n");
2314daef0ddSTomohiro Kusumi 	}
2324daef0ddSTomohiro Kusumi 	if (fd >= 0)
2334daef0ddSTomohiro Kusumi 		close(fd);
2344daef0ddSTomohiro Kusumi 	if (pfs.ondisk)
2354daef0ddSTomohiro Kusumi 		free(pfs.ondisk);
2364daef0ddSTomohiro Kusumi 	relpfs(fd, &pfs);
2374daef0ddSTomohiro Kusumi }
238fdba9548STomohiro Kusumi 
239fdba9548STomohiro Kusumi void
hammer_cmd_pseudofs_status(char ** av,int ac)240fdba9548STomohiro Kusumi hammer_cmd_pseudofs_status(char **av, int ac)
241fdba9548STomohiro Kusumi {
242fdba9548STomohiro Kusumi 	int i;
243fdba9548STomohiro Kusumi 
244fdba9548STomohiro Kusumi 	if (ac == 0) {
245fdba9548STomohiro Kusumi 		char buf[2] = "."; /* can't be readonly string */
246fdba9548STomohiro Kusumi 		print_pfs_status(buf);
247fdba9548STomohiro Kusumi 		return;
248fdba9548STomohiro Kusumi 	}
249fdba9548STomohiro Kusumi 
250fdba9548STomohiro Kusumi 	for (i = 0; i < ac; ++i)
251fdba9548STomohiro Kusumi 		print_pfs_status(av[i]);
2524daef0ddSTomohiro Kusumi }
2534daef0ddSTomohiro Kusumi 
2544daef0ddSTomohiro Kusumi void
hammer_cmd_pseudofs_create(char ** av,int ac,int is_slave)2554daef0ddSTomohiro Kusumi hammer_cmd_pseudofs_create(char **av, int ac, int is_slave)
2564daef0ddSTomohiro Kusumi {
2574daef0ddSTomohiro Kusumi 	struct hammer_ioc_pseudofs_rw pfs;
2584daef0ddSTomohiro Kusumi 	struct hammer_pseudofs_data pfsd;
2594daef0ddSTomohiro Kusumi 	struct stat st;
2604daef0ddSTomohiro Kusumi 	const char *path;
261*207ba670SMatthew Dillon 	char *tmpbuf;
2624daef0ddSTomohiro Kusumi 	char *dirpath;
2634daef0ddSTomohiro Kusumi 	char *linkpath;
2644daef0ddSTomohiro Kusumi 	int pfs_id;
2654daef0ddSTomohiro Kusumi 	int fd;
2664daef0ddSTomohiro Kusumi 
26788cdee70STomohiro Kusumi 	if (ac == 0) {
2684daef0ddSTomohiro Kusumi 		pseudofs_usage(1);
26988cdee70STomohiro Kusumi 		/* not reached */
27088cdee70STomohiro Kusumi 	}
2714daef0ddSTomohiro Kusumi 	path = av[0];
272052fd72bSTomohiro Kusumi 	if (lstat(path, &st) == 0) {
27302318f07STomohiro Kusumi 		errx(1, "Cannot create %s, file exists!", path);
274052fd72bSTomohiro Kusumi 		/* not reached */
275052fd72bSTomohiro Kusumi 	} else if (path[strlen(path) - 1] == '/') {
27602318f07STomohiro Kusumi 		errx(1, "Invalid PFS path %s with trailing /", path);
277052fd72bSTomohiro Kusumi 		/* not reached */
278052fd72bSTomohiro Kusumi 	}
2794daef0ddSTomohiro Kusumi 
2804daef0ddSTomohiro Kusumi 	/*
2814daef0ddSTomohiro Kusumi 	 * Figure out the directory prefix, taking care of degenerate
2824daef0ddSTomohiro Kusumi 	 * cases.
2834daef0ddSTomohiro Kusumi 	 */
284*207ba670SMatthew Dillon 	tmpbuf = strdup(path);
285*207ba670SMatthew Dillon 	dirpath = dirname(tmpbuf);
2864daef0ddSTomohiro Kusumi 	fd = open(dirpath, O_RDONLY);
287052fd72bSTomohiro Kusumi 	if (fd < 0) {
28802318f07STomohiro Kusumi 		err(1, "Cannot open directory %s", dirpath);
289052fd72bSTomohiro Kusumi 		/* not reached */
290052fd72bSTomohiro Kusumi 	}
2914daef0ddSTomohiro Kusumi 
2924daef0ddSTomohiro Kusumi 	/*
2934daef0ddSTomohiro Kusumi 	 * Avoid foot-shooting.  Don't let the user create a PFS
2944daef0ddSTomohiro Kusumi 	 * softlink via a PFS.  PFS softlinks may only be accessed
2954daef0ddSTomohiro Kusumi 	 * via the master filesystem.  Checking it here ensures
2964daef0ddSTomohiro Kusumi 	 * other PFS commands access PFS under the master filesystem.
2974daef0ddSTomohiro Kusumi 	 */
2981b23fc22STomohiro Kusumi 	clrpfs(&pfs, &pfsd, -1);
2994daef0ddSTomohiro Kusumi 
3004daef0ddSTomohiro Kusumi 	ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs);
301753daa18STomohiro Kusumi 	if (pfs.pfs_id != HAMMER_ROOT_PFSID) {
3024daef0ddSTomohiro Kusumi 		fprintf(stderr,
3034daef0ddSTomohiro Kusumi 			"You are attempting to access a PFS softlink "
3044daef0ddSTomohiro Kusumi 			"from a PFS.  It may not represent the PFS\n"
3054daef0ddSTomohiro Kusumi 			"on the main filesystem mount that you "
3064daef0ddSTomohiro Kusumi 			"expect!  You may only access PFS softlinks\n"
3074daef0ddSTomohiro Kusumi 			"via the main filesystem mount!\n");
3084daef0ddSTomohiro Kusumi 		exit(1);
3094daef0ddSTomohiro Kusumi 	}
3104daef0ddSTomohiro Kusumi 
3114daef0ddSTomohiro Kusumi 	for (pfs_id = 0; pfs_id < HAMMER_MAX_PFS; ++pfs_id) {
3121b23fc22STomohiro Kusumi 		clrpfs(&pfs, &pfsd, pfs_id);
3134daef0ddSTomohiro Kusumi 		if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) < 0) {
314052fd72bSTomohiro Kusumi 			if (errno != ENOENT) {
31502318f07STomohiro Kusumi 				err(1, "Cannot create %s", path);
316052fd72bSTomohiro Kusumi 				/* not reached */
317052fd72bSTomohiro Kusumi 			}
3184daef0ddSTomohiro Kusumi 			break;
3194daef0ddSTomohiro Kusumi 		}
3204daef0ddSTomohiro Kusumi 	}
321052fd72bSTomohiro Kusumi 	if (pfs_id == HAMMER_MAX_PFS) {
32202318f07STomohiro Kusumi 		errx(1, "Cannot create %s, all PFSs in use", path);
323052fd72bSTomohiro Kusumi 		/* not reached */
324052fd72bSTomohiro Kusumi 	} else if (pfs_id == HAMMER_ROOT_PFSID) {
32502318f07STomohiro Kusumi 		errx(1, "Fatal error: PFS#%d must exist", HAMMER_ROOT_PFSID);
326052fd72bSTomohiro Kusumi 		/* not reached */
327052fd72bSTomohiro Kusumi 	}
328e1c333cbSTomohiro Kusumi 
3294daef0ddSTomohiro Kusumi 	/*
3304daef0ddSTomohiro Kusumi 	 * Create the new PFS
3314daef0ddSTomohiro Kusumi 	 */
3324daef0ddSTomohiro Kusumi 	printf("Creating PFS#%d\t", pfs_id);
3331b23fc22STomohiro Kusumi 	clrpfs(&pfs, &pfsd, pfs_id);
3344daef0ddSTomohiro Kusumi 	init_pfsd(&pfsd, is_slave);
3354daef0ddSTomohiro Kusumi 
3364daef0ddSTomohiro Kusumi 	if (ioctl(fd, HAMMERIOC_SET_PSEUDOFS, &pfs) < 0) {
3374daef0ddSTomohiro Kusumi 		printf("failed: %s\n", strerror(errno));
3384daef0ddSTomohiro Kusumi 	} else {
3394daef0ddSTomohiro Kusumi 		/* special symlink, must be exactly 10 characters */
3404daef0ddSTomohiro Kusumi 		asprintf(&linkpath, "@@PFS%05d", pfs_id);
3414daef0ddSTomohiro Kusumi 		if (symlink(linkpath, path) < 0) {
3424daef0ddSTomohiro Kusumi 			printf("failed: cannot create symlink: %s\n",
3434daef0ddSTomohiro Kusumi 				strerror(errno));
3444daef0ddSTomohiro Kusumi 		} else {
3454daef0ddSTomohiro Kusumi 			printf("succeeded!\n");
3464daef0ddSTomohiro Kusumi 			hammer_cmd_pseudofs_update(av, ac);
3474daef0ddSTomohiro Kusumi 		}
3484daef0ddSTomohiro Kusumi 	}
349*207ba670SMatthew Dillon 	free(tmpbuf);
350*207ba670SMatthew Dillon 
3514daef0ddSTomohiro Kusumi 	close(fd);
3524daef0ddSTomohiro Kusumi }
3534daef0ddSTomohiro Kusumi 
3544daef0ddSTomohiro Kusumi void
hammer_cmd_pseudofs_destroy(char ** av,int ac)3554daef0ddSTomohiro Kusumi hammer_cmd_pseudofs_destroy(char **av, int ac)
3564daef0ddSTomohiro Kusumi {
3574daef0ddSTomohiro Kusumi 	struct hammer_ioc_pseudofs_rw pfs;
358578f0d56STomohiro Kusumi 	char *linkpath;
3594daef0ddSTomohiro Kusumi 	int fd;
3604daef0ddSTomohiro Kusumi 	int i;
3614daef0ddSTomohiro Kusumi 
36288cdee70STomohiro Kusumi 	if (ac == 0) {
3634daef0ddSTomohiro Kusumi 		pseudofs_usage(1);
36488cdee70STomohiro Kusumi 		/* not reached */
36588cdee70STomohiro Kusumi 	}
3664daef0ddSTomohiro Kusumi 	fd = getpfs(&pfs, av[0]);
3674daef0ddSTomohiro Kusumi 
368052fd72bSTomohiro Kusumi 	if (pfs.pfs_id == HAMMER_ROOT_PFSID) {
369a5ff7917STomohiro Kusumi 		errx(1, "You cannot destroy PFS#%d", HAMMER_ROOT_PFSID);
370052fd72bSTomohiro Kusumi 		/* not reached */
371052fd72bSTomohiro Kusumi 	}
37202318f07STomohiro Kusumi 
3734daef0ddSTomohiro Kusumi 	printf("You have requested that PFS#%d (%s) be destroyed\n",
3744daef0ddSTomohiro Kusumi 		pfs.pfs_id, pfs.ondisk->label);
3754daef0ddSTomohiro Kusumi 	printf("This will irrevocably destroy all data on this PFS!!!!!\n");
376fa9d9a9cSTomohiro Kusumi 	printf("Do you really want to do this? [y/n] ");
3774daef0ddSTomohiro Kusumi 	fflush(stdout);
378052fd72bSTomohiro Kusumi 	if (getyn() == 0) {
37902318f07STomohiro Kusumi 		errx(1, "No action taken on PFS#%d", pfs.pfs_id);
380052fd72bSTomohiro Kusumi 		/* not reached */
381052fd72bSTomohiro Kusumi 	}
3824daef0ddSTomohiro Kusumi 
3832bacc088STomohiro Kusumi 	if (hammer_is_pfs_master(pfs.ondisk)) {
3844daef0ddSTomohiro Kusumi 		printf("This PFS is currently setup as a MASTER!\n");
385fa9d9a9cSTomohiro Kusumi 		printf("Are you absolutely sure you want to destroy it? [y/n] ");
3864daef0ddSTomohiro Kusumi 		fflush(stdout);
387052fd72bSTomohiro Kusumi 		if (getyn() == 0) {
38802318f07STomohiro Kusumi 			errx(1, "No action taken on PFS#%d", pfs.pfs_id);
389052fd72bSTomohiro Kusumi 			/* not reached */
390052fd72bSTomohiro Kusumi 		}
3914daef0ddSTomohiro Kusumi 	}
3924daef0ddSTomohiro Kusumi 
393fd102df4STomohiro Kusumi 	printf("Destroying PFS#%d (%s)", pfs.pfs_id, pfs.ondisk->label);
394fd102df4STomohiro Kusumi 	if (DebugOpt) {
395fd102df4STomohiro Kusumi 		printf("\n");
396fd102df4STomohiro Kusumi 	} else {
397fd102df4STomohiro Kusumi 		printf(" in");
3984daef0ddSTomohiro Kusumi 		for (i = 5; i; --i) {
3994daef0ddSTomohiro Kusumi 			printf(" %d", i);
4004daef0ddSTomohiro Kusumi 			fflush(stdout);
4014daef0ddSTomohiro Kusumi 			sleep(1);
4024daef0ddSTomohiro Kusumi 		}
4034daef0ddSTomohiro Kusumi 		printf(".. starting destruction pass\n");
404fd102df4STomohiro Kusumi 	}
4054daef0ddSTomohiro Kusumi 
4064daef0ddSTomohiro Kusumi 	/*
4079376f71bSTomohiro Kusumi 	 * Remove the softlink on success.
4084daef0ddSTomohiro Kusumi 	 */
4094daef0ddSTomohiro Kusumi 	if (ioctl(fd, HAMMERIOC_RMR_PSEUDOFS, &pfs) == 0) {
4104daef0ddSTomohiro Kusumi 		printf("pfs-destroy of PFS#%d succeeded!\n", pfs.pfs_id);
411578f0d56STomohiro Kusumi 		linkpath = getlink(av[0]);
412578f0d56STomohiro Kusumi 		if (linkpath) {
413052fd72bSTomohiro Kusumi 			if (remove(linkpath) < 0) {
414f4bb1f26STomohiro Kusumi 				err(1, "Unable to remove softlink %s", linkpath);
415052fd72bSTomohiro Kusumi 				/* not reached */
416052fd72bSTomohiro Kusumi 			}
417578f0d56STomohiro Kusumi 			free(linkpath);
4184daef0ddSTomohiro Kusumi 		}
4194daef0ddSTomohiro Kusumi 	} else {
4204daef0ddSTomohiro Kusumi 		printf("pfs-destroy of PFS#%d failed: %s\n",
4214daef0ddSTomohiro Kusumi 			pfs.pfs_id, strerror(errno));
4224daef0ddSTomohiro Kusumi 	}
4234daef0ddSTomohiro Kusumi 	relpfs(fd, &pfs);
4244daef0ddSTomohiro Kusumi }
4254daef0ddSTomohiro Kusumi 
4264daef0ddSTomohiro Kusumi void
hammer_cmd_pseudofs_upgrade(char ** av,int ac)4274daef0ddSTomohiro Kusumi hammer_cmd_pseudofs_upgrade(char **av, int ac)
4284daef0ddSTomohiro Kusumi {
4294daef0ddSTomohiro Kusumi 	struct hammer_ioc_pseudofs_rw pfs;
4304daef0ddSTomohiro Kusumi 	int fd;
4314daef0ddSTomohiro Kusumi 
43288cdee70STomohiro Kusumi 	if (ac == 0) {
4334daef0ddSTomohiro Kusumi 		pseudofs_usage(1);
43488cdee70STomohiro Kusumi 		/* not reached */
43588cdee70STomohiro Kusumi 	}
4364daef0ddSTomohiro Kusumi 	fd = getpfs(&pfs, av[0]);
4374daef0ddSTomohiro Kusumi 
438f254e677STomohiro Kusumi 	if (pfs.pfs_id == HAMMER_ROOT_PFSID) {
439a5ff7917STomohiro Kusumi 		errx(1, "You cannot upgrade PFS#%d"
440a5ff7917STomohiro Kusumi 			" (It should already be a master)",
441a5ff7917STomohiro Kusumi 			HAMMER_ROOT_PFSID);
442052fd72bSTomohiro Kusumi 		/* not reached */
443f254e677STomohiro Kusumi 	} else if (hammer_is_pfs_master(pfs.ondisk)) {
44402318f07STomohiro Kusumi 		errx(1, "It is already a master");
445052fd72bSTomohiro Kusumi 		/* not reached */
446f254e677STomohiro Kusumi 	}
4474daef0ddSTomohiro Kusumi 
448f254e677STomohiro Kusumi 	if (ioctl(fd, HAMMERIOC_UPG_PSEUDOFS, &pfs) == 0) {
4494daef0ddSTomohiro Kusumi 		printf("pfs-upgrade of PFS#%d (%s) succeeded\n",
4504daef0ddSTomohiro Kusumi 			pfs.pfs_id, pfs.ondisk->label);
451f254e677STomohiro Kusumi 	} else {
452f4bb1f26STomohiro Kusumi 		err(1, "pfs-upgrade of PFS#%d (%s) failed",
453f4bb1f26STomohiro Kusumi 			pfs.pfs_id, pfs.ondisk->label);
454052fd72bSTomohiro Kusumi 		/* not reached */
455f254e677STomohiro Kusumi 	}
4564daef0ddSTomohiro Kusumi 	relpfs(fd, &pfs);
4574daef0ddSTomohiro Kusumi }
4584daef0ddSTomohiro Kusumi 
4594daef0ddSTomohiro Kusumi void
hammer_cmd_pseudofs_downgrade(char ** av,int ac)4604daef0ddSTomohiro Kusumi hammer_cmd_pseudofs_downgrade(char **av, int ac)
4614daef0ddSTomohiro Kusumi {
4624daef0ddSTomohiro Kusumi 	struct hammer_ioc_pseudofs_rw pfs;
4634daef0ddSTomohiro Kusumi 	int fd;
4644daef0ddSTomohiro Kusumi 
46588cdee70STomohiro Kusumi 	if (ac == 0) {
4664daef0ddSTomohiro Kusumi 		pseudofs_usage(1);
46788cdee70STomohiro Kusumi 		/* not reached */
46888cdee70STomohiro Kusumi 	}
4694daef0ddSTomohiro Kusumi 	fd = getpfs(&pfs, av[0]);
4704daef0ddSTomohiro Kusumi 
471052fd72bSTomohiro Kusumi 	if (pfs.pfs_id == HAMMER_ROOT_PFSID) {
472a5ff7917STomohiro Kusumi 		errx(1, "You cannot downgrade PFS#%d", HAMMER_ROOT_PFSID);
473052fd72bSTomohiro Kusumi 		/* not reached */
474052fd72bSTomohiro Kusumi 	} else if (hammer_is_pfs_slave(pfs.ondisk)) {
47502318f07STomohiro Kusumi 		errx(1, "It is already a slave");
476052fd72bSTomohiro Kusumi 		/* not reached */
477052fd72bSTomohiro Kusumi 	}
4784daef0ddSTomohiro Kusumi 
479f254e677STomohiro Kusumi 	if (ioctl(fd, HAMMERIOC_DGD_PSEUDOFS, &pfs) == 0) {
4804daef0ddSTomohiro Kusumi 		printf("pfs-downgrade of PFS#%d (%s) succeeded\n",
4814daef0ddSTomohiro Kusumi 			pfs.pfs_id, pfs.ondisk->label);
482f254e677STomohiro Kusumi 	} else {
483f4bb1f26STomohiro Kusumi 		err(1, "pfs-downgrade of PFS#%d (%s) failed",
484f4bb1f26STomohiro Kusumi 			pfs.pfs_id, pfs.ondisk->label);
485052fd72bSTomohiro Kusumi 		/* not reached */
486f254e677STomohiro Kusumi 	}
4874daef0ddSTomohiro Kusumi 	relpfs(fd, &pfs);
4884daef0ddSTomohiro Kusumi }
4894daef0ddSTomohiro Kusumi 
4904daef0ddSTomohiro Kusumi void
hammer_cmd_pseudofs_update(char ** av,int ac)4914daef0ddSTomohiro Kusumi hammer_cmd_pseudofs_update(char **av, int ac)
4924daef0ddSTomohiro Kusumi {
4934daef0ddSTomohiro Kusumi 	struct hammer_ioc_pseudofs_rw pfs;
4944daef0ddSTomohiro Kusumi 	int fd;
4954daef0ddSTomohiro Kusumi 
49688cdee70STomohiro Kusumi 	if (ac == 0) {
4974daef0ddSTomohiro Kusumi 		pseudofs_usage(1);
49888cdee70STomohiro Kusumi 		/* not reached */
49988cdee70STomohiro Kusumi 	}
5004daef0ddSTomohiro Kusumi 	fd = getpfs(&pfs, av[0]);
5014daef0ddSTomohiro Kusumi 
5024daef0ddSTomohiro Kusumi 	printf("%s\n", av[0]);
5034daef0ddSTomohiro Kusumi 	fflush(stdout);
5044daef0ddSTomohiro Kusumi 
5054daef0ddSTomohiro Kusumi 	if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) == 0) {
5064daef0ddSTomohiro Kusumi 		parse_pfsd_options(av + 1, ac - 1, pfs.ondisk);
5072bacc088STomohiro Kusumi 		if (hammer_is_pfs_slave(pfs.ondisk) &&
508f254e677STomohiro Kusumi 		    pfs.pfs_id == HAMMER_ROOT_PFSID) {
50902318f07STomohiro Kusumi 			errx(1, "The real mount point cannot be made a PFS "
5104daef0ddSTomohiro Kusumi 			       "slave, only PFS sub-directories can be made "
51102318f07STomohiro Kusumi 			       "slaves");
512052fd72bSTomohiro Kusumi 			/* not reached */
513f254e677STomohiro Kusumi 		}
5144daef0ddSTomohiro Kusumi 		pfs.bytes = sizeof(*pfs.ondisk);
5154daef0ddSTomohiro Kusumi 		if (ioctl(fd, HAMMERIOC_SET_PSEUDOFS, &pfs) == 0) {
516f254e677STomohiro Kusumi 			if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) == 0) {
5174daef0ddSTomohiro Kusumi 				dump_pfsd(pfs.ondisk, fd);
518f254e677STomohiro Kusumi 			} else {
51902318f07STomohiro Kusumi 				err(1, "Unable to retrieve PFS configuration "
52002318f07STomohiro Kusumi 					"after successful update");
521052fd72bSTomohiro Kusumi 				/* not reached */
522f254e677STomohiro Kusumi 			}
5234daef0ddSTomohiro Kusumi 		} else {
52402318f07STomohiro Kusumi 			err(1, "Unable to adjust PFS configuration");
525052fd72bSTomohiro Kusumi 			/* not reached */
5264daef0ddSTomohiro Kusumi 		}
5274daef0ddSTomohiro Kusumi 	}
5284daef0ddSTomohiro Kusumi 	relpfs(fd, &pfs);
5294daef0ddSTomohiro Kusumi }
5304daef0ddSTomohiro Kusumi 
531005a4da7STomohiro Kusumi static
532005a4da7STomohiro Kusumi void
init_pfsd(hammer_pseudofs_data_t pfsd,int is_slave)5334daef0ddSTomohiro Kusumi init_pfsd(hammer_pseudofs_data_t pfsd, int is_slave)
5344daef0ddSTomohiro Kusumi {
5354daef0ddSTomohiro Kusumi 	bzero(pfsd, sizeof(*pfsd));
5364daef0ddSTomohiro Kusumi 	pfsd->sync_beg_tid = 1;
5374daef0ddSTomohiro Kusumi 	pfsd->sync_end_tid = 1;
5384daef0ddSTomohiro Kusumi 	pfsd->sync_beg_ts = 0;
5394daef0ddSTomohiro Kusumi 	pfsd->sync_end_ts = 0;
5403cd578edSTomohiro Kusumi 	hammer_uuid_create(&pfsd->shared_uuid);
5413cd578edSTomohiro Kusumi 	hammer_uuid_create(&pfsd->unique_uuid);
5424daef0ddSTomohiro Kusumi 	if (is_slave)
5434daef0ddSTomohiro Kusumi 		pfsd->mirror_flags |= HAMMER_PFSD_SLAVE;
5444daef0ddSTomohiro Kusumi }
5454daef0ddSTomohiro Kusumi 
5464daef0ddSTomohiro Kusumi void
dump_pfsd(hammer_pseudofs_data_t pfsd,int fd)5474daef0ddSTomohiro Kusumi dump_pfsd(hammer_pseudofs_data_t pfsd, int fd)
5484daef0ddSTomohiro Kusumi {
5494daef0ddSTomohiro Kusumi 	struct hammer_ioc_version	version;
5504daef0ddSTomohiro Kusumi 	char *str = NULL;
5514daef0ddSTomohiro Kusumi 
5524daef0ddSTomohiro Kusumi 	printf("    sync-beg-tid=0x%016jx\n", (uintmax_t)pfsd->sync_beg_tid);
5534daef0ddSTomohiro Kusumi 	printf("    sync-end-tid=0x%016jx\n", (uintmax_t)pfsd->sync_end_tid);
5543cd578edSTomohiro Kusumi 	hammer_uuid_to_string(&pfsd->shared_uuid, &str);
5554daef0ddSTomohiro Kusumi 	printf("    shared-uuid=%s\n", str);
5564daef0ddSTomohiro Kusumi 	free(str);
5573cd578edSTomohiro Kusumi 	hammer_uuid_to_string(&pfsd->unique_uuid, &str);
5584daef0ddSTomohiro Kusumi 	printf("    unique-uuid=%s\n", str);
5594daef0ddSTomohiro Kusumi 	free(str);
5604daef0ddSTomohiro Kusumi 	printf("    label=\"%s\"\n", pfsd->label);
5614daef0ddSTomohiro Kusumi 	if (pfsd->snapshots[0])
5624daef0ddSTomohiro Kusumi 		printf("    snapshots=\"%s\"\n", pfsd->snapshots);
563f254e677STomohiro Kusumi 	if (pfsd->prune_min < (60 * 60 * 24)) {
5644daef0ddSTomohiro Kusumi 		printf("    prune-min=%02d:%02d:%02d\n",
5654daef0ddSTomohiro Kusumi 			pfsd->prune_min / 60 / 60 % 24,
5664daef0ddSTomohiro Kusumi 			pfsd->prune_min / 60 % 60,
5674daef0ddSTomohiro Kusumi 			pfsd->prune_min % 60);
568f254e677STomohiro Kusumi 	} else if (pfsd->prune_min % (60 * 60 * 24)) {
5694daef0ddSTomohiro Kusumi 		printf("    prune-min=%dd/%02d:%02d:%02d\n",
5704daef0ddSTomohiro Kusumi 			pfsd->prune_min / 60 / 60 / 24,
5714daef0ddSTomohiro Kusumi 			pfsd->prune_min / 60 / 60 % 24,
5724daef0ddSTomohiro Kusumi 			pfsd->prune_min / 60 % 60,
5734daef0ddSTomohiro Kusumi 			pfsd->prune_min % 60);
574f254e677STomohiro Kusumi 	} else {
5754daef0ddSTomohiro Kusumi 		printf("    prune-min=%dd\n", pfsd->prune_min / 60 / 60 / 24);
576f254e677STomohiro Kusumi 	}
5774daef0ddSTomohiro Kusumi 
57852e2f1b5STomohiro Kusumi 	if (hammer_is_pfs_slave(pfsd))
5794daef0ddSTomohiro Kusumi 		printf("    operating as a SLAVE\n");
58052e2f1b5STomohiro Kusumi 	else
5814daef0ddSTomohiro Kusumi 		printf("    operating as a MASTER\n");
5824daef0ddSTomohiro Kusumi 
5834daef0ddSTomohiro Kusumi 	/*
5844daef0ddSTomohiro Kusumi 	 * Snapshots directory cannot be shown when there is no fd since
5854daef0ddSTomohiro Kusumi 	 * hammer version can't be retrieved. mirror-dump passes -1 because
5864daef0ddSTomohiro Kusumi 	 * its input came from mirror-read output thus no path is available
5874daef0ddSTomohiro Kusumi 	 * to open(2).
5884daef0ddSTomohiro Kusumi 	 */
5894daef0ddSTomohiro Kusumi 	if (fd >= 0 && pfsd->snapshots[0] == 0) {
5904daef0ddSTomohiro Kusumi 		bzero(&version, sizeof(version));
5914daef0ddSTomohiro Kusumi 		if (ioctl(fd, HAMMERIOC_GET_VERSION, &version) < 0)
5924daef0ddSTomohiro Kusumi 			return;
5934c09d9c4SMatthew Dillon 		HammerVersion = version.cur_version;
5944daef0ddSTomohiro Kusumi 		if (version.cur_version < 3) {
595f254e677STomohiro Kusumi 			if (hammer_is_pfs_slave(pfsd)) {
5964daef0ddSTomohiro Kusumi 				printf("    snapshots directory not set for "
5974daef0ddSTomohiro Kusumi 				       "slave\n");
598f254e677STomohiro Kusumi 			} else {
5994daef0ddSTomohiro Kusumi 				printf("    snapshots directory for master "
6004daef0ddSTomohiro Kusumi 				       "defaults to <pfs>/snapshots\n");
601f254e677STomohiro Kusumi 			}
6024daef0ddSTomohiro Kusumi 		} else {
6034daef0ddSTomohiro Kusumi 			printf("    snapshots directory defaults to "
6044daef0ddSTomohiro Kusumi 			       "/var/hammer/<pfs>\n");
6054daef0ddSTomohiro Kusumi 		}
6064daef0ddSTomohiro Kusumi 	}
6074daef0ddSTomohiro Kusumi }
6084daef0ddSTomohiro Kusumi 
609005a4da7STomohiro Kusumi static
610005a4da7STomohiro Kusumi void
parse_pfsd_options(char ** av,int ac,hammer_pseudofs_data_t pfsd)6114daef0ddSTomohiro Kusumi parse_pfsd_options(char **av, int ac, hammer_pseudofs_data_t pfsd)
6124daef0ddSTomohiro Kusumi {
6134daef0ddSTomohiro Kusumi 	char *cmd;
6144daef0ddSTomohiro Kusumi 	char *ptr;
6154daef0ddSTomohiro Kusumi 	int len;
6164daef0ddSTomohiro Kusumi 
6174daef0ddSTomohiro Kusumi 	while (ac) {
6184daef0ddSTomohiro Kusumi 		cmd = *av;
6194daef0ddSTomohiro Kusumi 		if ((ptr = strchr(cmd, '=')) != NULL)
6204daef0ddSTomohiro Kusumi 			*ptr++ = 0;
6214daef0ddSTomohiro Kusumi 
6224daef0ddSTomohiro Kusumi 		/*
6234daef0ddSTomohiro Kusumi 		 * Basic assignment value test
6244daef0ddSTomohiro Kusumi 		 */
625052fd72bSTomohiro Kusumi 		if (ptr == NULL) {
62602318f07STomohiro Kusumi 			errx(1, "option %s requires an assignment", cmd);
627052fd72bSTomohiro Kusumi 			/* not reached */
628052fd72bSTomohiro Kusumi 		}
6294daef0ddSTomohiro Kusumi 
6304daef0ddSTomohiro Kusumi 		if (strcmp(cmd, "sync-beg-tid") == 0) {
6314daef0ddSTomohiro Kusumi 			pfsd->sync_beg_tid = strtoull(ptr, NULL, 16);
6324daef0ddSTomohiro Kusumi 		} else if (strcmp(cmd, "sync-end-tid") == 0) {
6334daef0ddSTomohiro Kusumi 			pfsd->sync_end_tid = strtoull(ptr, NULL, 16);
6344daef0ddSTomohiro Kusumi 		} else if (strcmp(cmd, "shared-uuid") == 0) {
6353cd578edSTomohiro Kusumi 			if (hammer_uuid_from_string(ptr, &pfsd->shared_uuid)) {
6363cd578edSTomohiro Kusumi 				errx(1, "option %s: error parsing uuid %s",
6373cd578edSTomohiro Kusumi 					cmd, ptr);
6383cd578edSTomohiro Kusumi 				/* not reached */
6393cd578edSTomohiro Kusumi 			}
6404daef0ddSTomohiro Kusumi 		} else if (strcmp(cmd, "unique-uuid") == 0) {
6413cd578edSTomohiro Kusumi 			if (hammer_uuid_from_string(ptr, &pfsd->unique_uuid)) {
6423cd578edSTomohiro Kusumi 				errx(1, "option %s: error parsing uuid %s",
6433cd578edSTomohiro Kusumi 					cmd, ptr);
6443cd578edSTomohiro Kusumi 				/* not reached */
6453cd578edSTomohiro Kusumi 			}
6464daef0ddSTomohiro Kusumi 		} else if (strcmp(cmd, "label") == 0) {
6474daef0ddSTomohiro Kusumi 			len = strlen(ptr);
6484daef0ddSTomohiro Kusumi 			if (ptr[0] == '"' && ptr[len-1] == '"') {
6494daef0ddSTomohiro Kusumi 				ptr[len-1] = 0;
6504daef0ddSTomohiro Kusumi 				++ptr;
6514daef0ddSTomohiro Kusumi 			} else if (ptr[0] == '"') {
65202318f07STomohiro Kusumi 				errx(1, "option %s: malformed string", cmd);
653052fd72bSTomohiro Kusumi 				/* not reached */
6544daef0ddSTomohiro Kusumi 			}
6554daef0ddSTomohiro Kusumi 			snprintf(pfsd->label, sizeof(pfsd->label), "%s", ptr);
6564daef0ddSTomohiro Kusumi 		} else if (strcmp(cmd, "snapshots") == 0) {
6574daef0ddSTomohiro Kusumi 			len = strlen(ptr);
6584daef0ddSTomohiro Kusumi 			if (ptr[0] != '/') {
6594daef0ddSTomohiro Kusumi 				fprintf(stderr,
6604daef0ddSTomohiro Kusumi 					"option %s: '%s' must be an "
6614daef0ddSTomohiro Kusumi 					"absolute path\n", cmd, ptr);
662f254e677STomohiro Kusumi 				if (ptr[0] == 0) {
6634daef0ddSTomohiro Kusumi 					fprintf(stderr,
6644daef0ddSTomohiro Kusumi 						"use 'snapshots-clear' "
6654daef0ddSTomohiro Kusumi 						"to unset snapshots dir\n");
666f254e677STomohiro Kusumi 				}
6674daef0ddSTomohiro Kusumi 				exit(1);
6684daef0ddSTomohiro Kusumi 			}
669f254e677STomohiro Kusumi 			if (len >= (int)sizeof(pfsd->snapshots)) {
67002318f07STomohiro Kusumi 				errx(1, "option %s: path too long, %d "
67102318f07STomohiro Kusumi 					"character limit", cmd, len);
672052fd72bSTomohiro Kusumi 				/* not reached */
673f254e677STomohiro Kusumi 			}
6744daef0ddSTomohiro Kusumi 			snprintf(pfsd->snapshots, sizeof(pfsd->snapshots),
6754daef0ddSTomohiro Kusumi 				 "%s", ptr);
6764daef0ddSTomohiro Kusumi 		} else if (strcmp(cmd, "snapshots-clear") == 0) {
6774daef0ddSTomohiro Kusumi 			pfsd->snapshots[0] = 0;
6784daef0ddSTomohiro Kusumi 		} else if (strcmp(cmd, "prune-min") == 0) {
6794daef0ddSTomohiro Kusumi 			pfsd->prune_min = timetosecs(ptr);
680f254e677STomohiro Kusumi 			if (pfsd->prune_min < 0) {
68102318f07STomohiro Kusumi 				errx(1, "option %s: illegal time spec, "
68202318f07STomohiro Kusumi 					"use Nd or [Nd/]hh[:mm[:ss]]", ptr);
683052fd72bSTomohiro Kusumi 				/* not reached */
684f254e677STomohiro Kusumi 			}
6854daef0ddSTomohiro Kusumi 		} else {
68602318f07STomohiro Kusumi 			errx(1, "invalid option: %s", cmd);
687052fd72bSTomohiro Kusumi 			/* not reached */
6884daef0ddSTomohiro Kusumi 		}
6894daef0ddSTomohiro Kusumi 		--ac;
6904daef0ddSTomohiro Kusumi 		++av;
6914daef0ddSTomohiro Kusumi 	}
6924daef0ddSTomohiro Kusumi }
6934daef0ddSTomohiro Kusumi 
6944daef0ddSTomohiro Kusumi static
6954daef0ddSTomohiro Kusumi void
pseudofs_usage(int code)6964daef0ddSTomohiro Kusumi pseudofs_usage(int code)
6974daef0ddSTomohiro Kusumi {
6984daef0ddSTomohiro Kusumi 	fprintf(stderr,
6994daef0ddSTomohiro Kusumi 		"hammer pfs-status <dirpath> ...\n"
7004daef0ddSTomohiro Kusumi 		"hammer pfs-master <dirpath> [options]\n"
7014daef0ddSTomohiro Kusumi 		"hammer pfs-slave <dirpath> [options]\n"
7024daef0ddSTomohiro Kusumi 		"hammer pfs-update <dirpath> [options]\n"
7034daef0ddSTomohiro Kusumi 		"hammer pfs-upgrade <dirpath>\n"
7044daef0ddSTomohiro Kusumi 		"hammer pfs-downgrade <dirpath>\n"
7054daef0ddSTomohiro Kusumi 		"hammer pfs-destroy <dirpath>\n"
7064daef0ddSTomohiro Kusumi 		"\n"
7074daef0ddSTomohiro Kusumi 		"options:\n"
7084daef0ddSTomohiro Kusumi 		"    sync-beg-tid=0x16llx\n"
7094daef0ddSTomohiro Kusumi 		"    sync-end-tid=0x16llx\n"
7104daef0ddSTomohiro Kusumi 		"    shared-uuid=0x16llx\n"
7114daef0ddSTomohiro Kusumi 		"    unique-uuid=0x16llx\n"
7124daef0ddSTomohiro Kusumi 		"    label=\"string\"\n"
7134daef0ddSTomohiro Kusumi 		"    snapshots=\"/path\"\n"
7144daef0ddSTomohiro Kusumi 		"    snapshots-clear\n"
7154daef0ddSTomohiro Kusumi 		"    prune-min=Nd\n"
7164daef0ddSTomohiro Kusumi 		"    prune-min=[Nd/]hh[:mm[:ss]]\n"
7174daef0ddSTomohiro Kusumi 	);
7184daef0ddSTomohiro Kusumi 	exit(code);
7194daef0ddSTomohiro Kusumi }
7204daef0ddSTomohiro Kusumi 
7214daef0ddSTomohiro Kusumi /*
7224daef0ddSTomohiro Kusumi  * Convert time in the form [Nd/]hh[:mm[:ss]] to seconds.
7234daef0ddSTomohiro Kusumi  *
7244daef0ddSTomohiro Kusumi  * Return -1 if a parse error occurs.
7254daef0ddSTomohiro Kusumi  * Return 0x7FFFFFFF if the time exceeds the maximum allowed.
7264daef0ddSTomohiro Kusumi  */
7274daef0ddSTomohiro Kusumi static
7284daef0ddSTomohiro Kusumi int
timetosecs(char * str)7294daef0ddSTomohiro Kusumi timetosecs(char *str)
7304daef0ddSTomohiro Kusumi {
7314daef0ddSTomohiro Kusumi 	int days = 0;
7324daef0ddSTomohiro Kusumi 	int hrs = 0;
7334daef0ddSTomohiro Kusumi 	int mins = 0;
7344daef0ddSTomohiro Kusumi 	int secs = 0;
7354daef0ddSTomohiro Kusumi 	int n;
7364daef0ddSTomohiro Kusumi 	long long v;
7374daef0ddSTomohiro Kusumi 	char *ptr;
7384daef0ddSTomohiro Kusumi 
7394daef0ddSTomohiro Kusumi 	n = strtol(str, &ptr, 10);
7404daef0ddSTomohiro Kusumi 	if (n < 0)
7414daef0ddSTomohiro Kusumi 		return(-1);
7424daef0ddSTomohiro Kusumi 	if (*ptr == 'd') {
7434daef0ddSTomohiro Kusumi 		days = n;
7444daef0ddSTomohiro Kusumi 		++ptr;
7454daef0ddSTomohiro Kusumi 		if (*ptr == '/')
7464daef0ddSTomohiro Kusumi 		    n = strtol(ptr + 1, &ptr, 10);
7474daef0ddSTomohiro Kusumi 		else
7484daef0ddSTomohiro Kusumi 		    n = 0;
7494daef0ddSTomohiro Kusumi 	}
7504daef0ddSTomohiro Kusumi 	if (n < 0)
7514daef0ddSTomohiro Kusumi 		return(-1);
7524daef0ddSTomohiro Kusumi 	hrs = n;
7534daef0ddSTomohiro Kusumi 	if (*ptr == ':') {
7544daef0ddSTomohiro Kusumi 		n = strtol(ptr + 1, &ptr, 10);
7554daef0ddSTomohiro Kusumi 		if (n < 0)
7564daef0ddSTomohiro Kusumi 			return(-1);
7574daef0ddSTomohiro Kusumi 		mins = n;
7584daef0ddSTomohiro Kusumi 		if (*ptr == ':') {
7594daef0ddSTomohiro Kusumi 			n = strtol(ptr + 1, &ptr, 10);
7604daef0ddSTomohiro Kusumi 			if (n < 0)
7614daef0ddSTomohiro Kusumi 				return(-1);
7624daef0ddSTomohiro Kusumi 			secs = n;
7634daef0ddSTomohiro Kusumi 		}
7644daef0ddSTomohiro Kusumi 	}
7654daef0ddSTomohiro Kusumi 	if (*ptr)
7664daef0ddSTomohiro Kusumi 		return(-1);
7674daef0ddSTomohiro Kusumi 	v = days * 24 * 60 * 60 + hrs *  60 * 60 + mins * 60 + secs;
7684daef0ddSTomohiro Kusumi 	if (v > 0x7FFFFFFF)
7694daef0ddSTomohiro Kusumi 		v = 0x7FFFFFFF;
7704daef0ddSTomohiro Kusumi 	return((int)v);
7714daef0ddSTomohiro Kusumi }
772