xref: /freebsd/usr.sbin/pnfsdsfile/pnfsdsfile.c (revision ba3c1f5972d7b90feb6e6da47905ff2757e0fe57)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2017 Rick Macklem
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  */
28 
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31 
32 #include <err.h>
33 #include <getopt.h>
34 #include <netdb.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <unistd.h>
39 #include <sys/param.h>
40 #include <sys/extattr.h>
41 #include <sys/mount.h>
42 #include <sys/socket.h>
43 #include <netinet/in.h>
44 #include <fs/nfs/nfskpiport.h>
45 #include <fs/nfs/nfsproto.h>
46 #include <fs/nfs/nfs.h>
47 #include <fs/nfs/nfsrvstate.h>
48 
49 static void usage(void) __dead2;
50 
51 static struct option longopts[] = {
52 	{ "changeds",	required_argument,	NULL,	'c'	},
53 	{ "mirror",	required_argument,	NULL,	'm'	},
54 	{ "quiet",	no_argument,		NULL,	'q'	},
55 	{ "zerods",	required_argument,	NULL,	'r'	},
56 	{ "ds",		required_argument,	NULL,	's'	},
57 	{ "zerofh",	no_argument,		NULL,	'z'	},
58 	{ NULL,		0,			NULL,	0	}
59 };
60 
61 /*
62  * This program displays the location information of a data storage file
63  * for a given file on a MetaData Server (MDS) in a pNFS service.  This program
64  * must be run on the MDS and the file argument must be a file in a local
65  * file system that has been exported for the pNFS service.
66  */
67 int
68 main(int argc, char *argv[])
69 {
70 	struct addrinfo *res, *ad, *newres;
71 	struct sockaddr_in *sin, adsin;
72 	struct sockaddr_in6 *sin6, adsin6;
73 	char hostn[2 * NI_MAXHOST + 2], *cp;
74 	struct pnfsdsfile dsfile[NFSDEV_MAXMIRRORS];
75 	int ch, dosetxattr, i, mirrorcnt, mirrorit, quiet, zerods, zerofh;
76 	in_port_t tport;
77 	ssize_t xattrsize, xattrsize2;
78 
79 	zerods = 0;
80 	zerofh = 0;
81 	mirrorit = 0;
82 	quiet = 0;
83 	dosetxattr = 0;
84 	res = NULL;
85 	newres = NULL;
86 	cp = NULL;
87 	while ((ch = getopt_long(argc, argv, "c:m:qr:s:z", longopts, NULL)) !=
88 	    -1) {
89 		switch (ch) {
90 		case 'c':
91 			/* Replace the first DS server with the second one. */
92 			if (zerofh != 0 || zerods != 0 || mirrorit != 0 ||
93 			    newres != NULL || res != NULL)
94 				errx(1, "-c, -m, -r, -s and -z are mutually "
95 				    "exclusive and only can be used once");
96 			strlcpy(hostn, optarg, 2 * NI_MAXHOST + 2);
97 			cp = strchr(hostn, ',');
98 			if (cp == NULL)
99 				errx(1, "Bad -c argument %s", hostn);
100 			*cp = '\0';
101 			if (getaddrinfo(hostn, NULL, NULL, &res) != 0)
102 				errx(1, "Can't get IP# for %s", hostn);
103 			*cp++ = ',';
104 			if (getaddrinfo(cp, NULL, NULL, &newres) != 0)
105 				errx(1, "Can't get IP# for %s", cp);
106 			break;
107 		case 'm':
108 			/* Add 0.0.0.0 entries up to mirror level. */
109 			if (zerofh != 0 || zerods != 0 || mirrorit != 0 ||
110 			    newres != NULL || res != NULL)
111 				errx(1, "-c, -m, -r, -s and -z are mutually "
112 				    "exclusive and only can be used once");
113 			mirrorit = atoi(optarg);
114 			if (mirrorit < 2 || mirrorit > NFSDEV_MAXMIRRORS)
115 				errx(1, "-m %d out of range", mirrorit);
116 			break;
117 		case 'q':
118 			quiet = 1;
119 			break;
120 		case 'r':
121 			/* Reset the DS server in a mirror with 0.0.0.0. */
122 			if (zerofh != 0 || zerods != 0 || mirrorit != 0 ||
123 			    newres != NULL || res != NULL)
124 				errx(1, "-c, -m, -r, -s and -z are mutually "
125 				    "exclusive and only can be used once");
126 			zerods = 1;
127 			/* Translate the server name to an IP address. */
128 			if (getaddrinfo(optarg, NULL, NULL, &res) != 0)
129 				errx(1, "Can't get IP# for %s", optarg);
130 			break;
131 		case 's':
132 			/* Translate the server name to an IP address. */
133 			if (zerods != 0 || mirrorit != 0 || newres != NULL ||
134 			    res != NULL)
135 				errx(1, "-c, -m and -r are mutually exclusive "
136 				    "from use with -s and -z");
137 			if (getaddrinfo(optarg, NULL, NULL, &res) != 0)
138 				errx(1, "Can't get IP# for %s", optarg);
139 			break;
140 		case 'z':
141 			if (zerofh != 0 || zerods != 0 || mirrorit != 0 ||
142 			    newres != NULL)
143 				errx(1, "-c, -m and -r are mutually exclusive "
144 				    "from use with -s and -z");
145 			zerofh = 1;
146 			break;
147 		default:
148 			usage();
149 		}
150 	}
151 	argc -= optind;
152 	if (argc != 1)
153 		usage();
154 	argv += optind;
155 
156 	/*
157 	 * The host address and directory where the data storage file is
158 	 * located is in the extended attribute "pnfsd.dsfile".
159 	 */
160 	xattrsize = extattr_get_file(*argv, EXTATTR_NAMESPACE_SYSTEM,
161 	    "pnfsd.dsfile", dsfile, sizeof(dsfile));
162 	mirrorcnt = xattrsize / sizeof(struct pnfsdsfile);
163 	xattrsize2 = mirrorcnt * sizeof(struct pnfsdsfile);
164 	if (mirrorcnt < 1 || xattrsize != xattrsize2)
165 		err(1, "Can't get extattr pnfsd.dsfile for %s", *argv);
166 
167 	if (quiet == 0)
168 		printf("%s:\t", *argv);
169 	for (i = 0; i < mirrorcnt; i++) {
170 		if (i > 0 && quiet == 0)
171 			printf("\t");
172 		/* Do the zerofh option. You must be root. */
173 		if (zerofh != 0) {
174 			if (geteuid() != 0)
175 				errx(1, "Must be root/su to zerofh");
176 
177 			/*
178 			 * Do it for the server specified by -s/--ds or all
179 			 * servers, if -s/--ds was not specified.
180 			 */
181 			sin = &dsfile[i].dsf_sin;
182 			sin6 = &dsfile[i].dsf_sin6;
183 			ad = res;
184 			while (ad != NULL) {
185 				if (ad->ai_addr->sa_family == AF_INET &&
186 				    sin->sin_family == AF_INET &&
187 				    ad->ai_addrlen >= sizeof(adsin)) {
188 					memcpy(&adsin, ad->ai_addr,
189 					    sizeof(adsin));
190 					if (sin->sin_addr.s_addr ==
191 					    adsin.sin_addr.s_addr)
192 						break;
193 				}
194 				if (ad->ai_addr->sa_family == AF_INET6 &&
195 				    sin6->sin6_family == AF_INET6 &&
196 				    ad->ai_addrlen >= sizeof(adsin6)) {
197 					memcpy(&adsin6, ad->ai_addr,
198 					    sizeof(adsin6));
199 					if (IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr,
200 					    &adsin6.sin6_addr))
201 						break;
202 				}
203 				ad = ad->ai_next;
204 			}
205 			if (res == NULL || ad != NULL) {
206 				memset(&dsfile[i].dsf_fh, 0, sizeof(fhandle_t));
207 				dosetxattr = 1;
208 			}
209 		}
210 
211 		/* Do the zerods option. You must be root. */
212 		if (zerods != 0 && mirrorcnt > 1) {
213 			if (geteuid() != 0)
214 				errx(1, "Must be root/su to zerods");
215 
216 			/*
217 			 * Do it for the server specified.
218 			 */
219 			sin = &dsfile[i].dsf_sin;
220 			sin6 = &dsfile[i].dsf_sin6;
221 			ad = res;
222 			while (ad != NULL) {
223 				if (ad->ai_addr->sa_family == AF_INET &&
224 				    sin->sin_family == AF_INET &&
225 				    ad->ai_addrlen >= sizeof(adsin)) {
226 					memcpy(&adsin, ad->ai_addr,
227 					    sizeof(adsin));
228 					if (sin->sin_addr.s_addr ==
229 					    adsin.sin_addr.s_addr)
230 						break;
231 				}
232 				if (ad->ai_addr->sa_family == AF_INET6 &&
233 				    sin6->sin6_family == AF_INET6 &&
234 				    ad->ai_addrlen >= sizeof(adsin6)) {
235 					memcpy(&adsin6, ad->ai_addr,
236 					    sizeof(adsin6));
237 					if (IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr,
238 					    &adsin6.sin6_addr))
239 						break;
240 				}
241 				ad = ad->ai_next;
242 			}
243 			if (ad != NULL) {
244 				sin->sin_family = AF_INET;
245 				sin->sin_len = sizeof(*sin);
246 				sin->sin_port = 0;
247 				sin->sin_addr.s_addr = 0;
248 				dosetxattr = 1;
249 			}
250 		}
251 
252 		/* Do the -c option to replace the DS host address. */
253 		if (newres != NULL) {
254 			if (geteuid() != 0)
255 				errx(1, "Must be root/su to replace the host"
256 				    " addr");
257 
258 			/*
259 			 * Check that the old host address matches.
260 			 */
261 			sin = &dsfile[i].dsf_sin;
262 			sin6 = &dsfile[i].dsf_sin6;
263 			ad = res;
264 			while (ad != NULL) {
265 				if (ad->ai_addr->sa_family == AF_INET &&
266 				    sin->sin_family == AF_INET &&
267 				    ad->ai_addrlen >= sizeof(adsin)) {
268 					memcpy(&adsin, ad->ai_addr,
269 					    sizeof(adsin));
270 					if (sin->sin_addr.s_addr ==
271 					    adsin.sin_addr.s_addr)
272 						break;
273 				}
274 				if (ad->ai_addr->sa_family == AF_INET6 &&
275 				    sin6->sin6_family == AF_INET6 &&
276 				    ad->ai_addrlen >= sizeof(adsin6)) {
277 					memcpy(&adsin6, ad->ai_addr,
278 					    sizeof(adsin6));
279 					if (IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr,
280 					    &adsin6.sin6_addr))
281 						break;
282 				}
283 				ad = ad->ai_next;
284 			}
285 			if (ad != NULL) {
286 				if (sin->sin_family == AF_INET)
287 					tport = sin->sin_port;
288 				else
289 					tport = sin6->sin6_port;
290 				/*
291 				 * We have a match, so replace it with the first
292 				 * AF_INET or AF_INET6 address in the newres
293 				 * list.
294 				 */
295 				while (newres->ai_addr->sa_family != AF_INET &&
296 				    newres->ai_addr->sa_family != AF_INET6) {
297 					newres = newres->ai_next;
298 					if (newres == NULL)
299 						errx(1, "Hostname %s has no"
300 						    " IP#", cp);
301 				}
302 				if (newres->ai_addr->sa_family == AF_INET) {
303 					memcpy(sin, newres->ai_addr,
304 					    sizeof(*sin));
305 					sin->sin_port = tport;
306 				} else if (newres->ai_addr->sa_family ==
307 				    AF_INET6) {
308 					memcpy(sin6, newres->ai_addr,
309 					    sizeof(*sin6));
310 					sin6->sin6_port = tport;
311 				}
312 				dosetxattr = 1;
313 			}
314 		}
315 
316 		if (quiet == 0) {
317 			/* Translate the IP address to a hostname. */
318 			if (getnameinfo((struct sockaddr *)&dsfile[i].dsf_sin,
319 			    dsfile[i].dsf_sin.sin_len, hostn, sizeof(hostn),
320 			    NULL, 0, 0) < 0)
321 				err(1, "Can't get hostname");
322 			printf("%s\tds%d/%s", hostn, dsfile[i].dsf_dir,
323 			    dsfile[i].dsf_filename);
324 		}
325 	}
326 	/* Add entrie(s) with IP address set to 0.0.0.0, as required. */
327 	for (i = mirrorcnt; i < mirrorit; i++) {
328 		dsfile[i] = dsfile[0];
329 		dsfile[i].dsf_sin.sin_family = AF_INET;
330 		dsfile[i].dsf_sin.sin_len = sizeof(struct sockaddr_in);
331 		dsfile[i].dsf_sin.sin_addr.s_addr = 0;
332 		dsfile[i].dsf_sin.sin_port = 0;
333 		if (quiet == 0) {
334 			/* Print out the 0.0.0.0 entry. */
335 			printf("\t0.0.0.0\tds%d/%s", dsfile[i].dsf_dir,
336 			    dsfile[i].dsf_filename);
337 		}
338 	}
339 	if (mirrorit > mirrorcnt) {
340 		xattrsize = mirrorit * sizeof(struct pnfsdsfile);
341 		dosetxattr = 1;
342 	}
343 	if (quiet == 0)
344 		printf("\n");
345 
346 	if (dosetxattr != 0 && extattr_set_file(*argv, EXTATTR_NAMESPACE_SYSTEM,
347 	    "pnfsd.dsfile", dsfile, xattrsize) != xattrsize)
348 		err(1, "Can't set pnfsd.dsfile");
349 }
350 
351 static void
352 usage(void)
353 {
354 
355 	fprintf(stderr, "pnfsdsfile [-q/--quiet] [-z/--zerofh] "
356 	    "[-c/--changeds <old dshostname> <new dshostname>] "
357 	    "[-r/--zerods <dshostname>] "
358 	    "[-s/--ds <dshostname>] "
359 	    "<filename>\n");
360 	exit(1);
361 }
362 
363