xref: /freebsd/usr.sbin/pnfsdsfile/pnfsdsfile.c (revision 964219664dcec4198441910904fb9064569d174d)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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);
50 
51 static struct option longopts[] = {
52 	{ "changeds",	required_argument,	NULL,	'c'	},
53 	{ "quiet",	no_argument,		NULL,	'q'	},
54 	{ "zerods",	required_argument,	NULL,	'r'	},
55 	{ "ds",		required_argument,	NULL,	's'	},
56 	{ "zerofh",	no_argument,		NULL,	'z'	},
57 	{ NULL,		0,			NULL,	0	}
58 };
59 
60 /*
61  * This program displays the location information of a data storage file
62  * for a given file on a MetaData Server (MDS) in a pNFS service.  This program
63  * must be run on the MDS and the file argument must be a file in a local
64  * file system that has been exported for the pNFS service.
65  */
66 int
67 main(int argc, char *argv[])
68 {
69 	struct addrinfo *res, *ad, *newres;
70 	struct sockaddr_in *sin, adsin;
71 	struct sockaddr_in6 *sin6, adsin6;
72 	char hostn[2 * NI_MAXHOST + 2], *cp;
73 	struct pnfsdsfile dsfile[NFSDEV_MAXMIRRORS];
74 	int ch, dosetxattr, i, mirrorcnt, quiet, zerods, zerofh;
75 	in_port_t tport;
76 	ssize_t xattrsize, xattrsize2;
77 
78 	zerods = 0;
79 	zerofh = 0;
80 	quiet = 0;
81 	dosetxattr = 0;
82 	res = NULL;
83 	newres = NULL;
84 	cp = NULL;
85 	while ((ch = getopt_long(argc, argv, "c:qr:s:z", longopts, NULL)) != -1)
86 	    {
87 		switch (ch) {
88 		case 'c':
89 			/* Replace the first DS server with the second one. */
90 			if (zerofh != 0 || zerods != 0)
91 				errx(1, "-c, -r and -z are mutually "
92 				    "exclusive");
93 			if (res != NULL)
94 				errx(1, "-c and -s are mutually exclusive");
95 			strlcpy(hostn, optarg, 2 * NI_MAXHOST + 2);
96 			cp = strchr(hostn, ',');
97 			if (cp == NULL)
98 				errx(1, "Bad -c argument %s", hostn);
99 			*cp = '\0';
100 			if (getaddrinfo(hostn, NULL, NULL, &res) != 0)
101 				errx(1, "Can't get IP# for %s", hostn);
102 			*cp++ = ',';
103 			if (getaddrinfo(cp, NULL, NULL, &newres) != 0)
104 				errx(1, "Can't get IP# for %s", cp);
105 			break;
106 		case 'q':
107 			quiet = 1;
108 			break;
109 		case 'r':
110 			/* Reset the DS server in a mirror with 0.0.0.0. */
111 			if (zerofh != 0 || res != NULL || newres != NULL)
112 				errx(1, "-r and -s, -z or -c are mutually "
113 				    "exclusive");
114 			zerods = 1;
115 			/* Translate the server name to an IP address. */
116 			if (getaddrinfo(optarg, NULL, NULL, &res) != 0)
117 				errx(1, "Can't get IP# for %s", optarg);
118 			break;
119 		case 's':
120 			if (res != NULL)
121 				errx(1, "-s, -c and -r are mutually "
122 				    "exclusive");
123 			/* Translate the server name to an IP address. */
124 			if (getaddrinfo(optarg, NULL, NULL, &res) != 0)
125 				errx(1, "Can't get IP# for %s", optarg);
126 			break;
127 		case 'z':
128 			if (newres != NULL || zerods != 0)
129 				errx(1, "-c, -r and -z are mutually "
130 				    "exclusive");
131 			zerofh = 1;
132 			break;
133 		default:
134 			usage();
135 		}
136 	}
137 	argc -= optind;
138 	if (argc != 1)
139 		usage();
140 	argv += optind;
141 
142 	/*
143 	 * The host address and directory where the data storage file is
144 	 * located is in the extended attribute "pnfsd.dsfile".
145 	 */
146 	xattrsize = extattr_get_file(*argv, EXTATTR_NAMESPACE_SYSTEM,
147 	    "pnfsd.dsfile", dsfile, sizeof(dsfile));
148 	mirrorcnt = xattrsize / sizeof(struct pnfsdsfile);
149 	xattrsize2 = mirrorcnt * sizeof(struct pnfsdsfile);
150 	if (mirrorcnt < 1 || xattrsize != xattrsize2)
151 		err(1, "Can't get extattr pnfsd.dsfile for %s", *argv);
152 
153 	if (quiet == 0)
154 		printf("%s:\t", *argv);
155 	for (i = 0; i < mirrorcnt; i++) {
156 		if (i > 0 && quiet == 0)
157 			printf("\t");
158 		/* Do the zerofh option. You must be root. */
159 		if (zerofh != 0) {
160 			if (geteuid() != 0)
161 				errx(1, "Must be root/su to zerofh");
162 
163 			/*
164 			 * Do it for the server specified by -s/--ds or all
165 			 * servers, if -s/--ds was not specified.
166 			 */
167 			sin = &dsfile[i].dsf_sin;
168 			sin6 = &dsfile[i].dsf_sin6;
169 			ad = res;
170 			while (ad != NULL) {
171 				if (ad->ai_addr->sa_family == AF_INET &&
172 				    sin->sin_family == AF_INET &&
173 				    ad->ai_addrlen >= sizeof(adsin)) {
174 					memcpy(&adsin, ad->ai_addr,
175 					    sizeof(adsin));
176 					if (sin->sin_addr.s_addr ==
177 					    adsin.sin_addr.s_addr)
178 						break;
179 				}
180 				if (ad->ai_addr->sa_family == AF_INET6 &&
181 				    sin6->sin6_family == AF_INET6 &&
182 				    ad->ai_addrlen >= sizeof(adsin6)) {
183 					memcpy(&adsin6, ad->ai_addr,
184 					    sizeof(adsin6));
185 					if (IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr,
186 					    &adsin6.sin6_addr))
187 						break;
188 				}
189 				ad = ad->ai_next;
190 			}
191 			if (res == NULL || ad != NULL) {
192 				memset(&dsfile[i].dsf_fh, 0, sizeof(fhandle_t));
193 				dosetxattr = 1;
194 			}
195 		}
196 
197 		/* Do the zerods option. You must be root. */
198 		if (zerods != 0 && mirrorcnt > 1) {
199 			if (geteuid() != 0)
200 				errx(1, "Must be root/su to zerods");
201 
202 			/*
203 			 * Do it for the server specified.
204 			 */
205 			sin = &dsfile[i].dsf_sin;
206 			sin6 = &dsfile[i].dsf_sin6;
207 			ad = res;
208 			while (ad != NULL) {
209 				if (ad->ai_addr->sa_family == AF_INET &&
210 				    sin->sin_family == AF_INET &&
211 				    ad->ai_addrlen >= sizeof(adsin)) {
212 					memcpy(&adsin, ad->ai_addr,
213 					    sizeof(adsin));
214 					if (sin->sin_addr.s_addr ==
215 					    adsin.sin_addr.s_addr)
216 						break;
217 				}
218 				if (ad->ai_addr->sa_family == AF_INET6 &&
219 				    sin6->sin6_family == AF_INET6 &&
220 				    ad->ai_addrlen >= sizeof(adsin6)) {
221 					memcpy(&adsin6, ad->ai_addr,
222 					    sizeof(adsin6));
223 					if (IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr,
224 					    &adsin6.sin6_addr))
225 						break;
226 				}
227 				ad = ad->ai_next;
228 			}
229 			if (ad != NULL) {
230 				sin->sin_family = AF_INET;
231 				sin->sin_len = sizeof(*sin);
232 				sin->sin_port = 0;
233 				sin->sin_addr.s_addr = 0;
234 				dosetxattr = 1;
235 			}
236 		}
237 
238 		/* Do the -c option to replace the DS host address. */
239 		if (newres != NULL) {
240 			if (geteuid() != 0)
241 				errx(1, "Must be root/su to replace the host"
242 				    " addr");
243 
244 			/*
245 			 * Check that the old host address matches.
246 			 */
247 			sin = &dsfile[i].dsf_sin;
248 			sin6 = &dsfile[i].dsf_sin6;
249 			ad = res;
250 			while (ad != NULL) {
251 				if (ad->ai_addr->sa_family == AF_INET &&
252 				    sin->sin_family == AF_INET &&
253 				    ad->ai_addrlen >= sizeof(adsin)) {
254 					memcpy(&adsin, ad->ai_addr,
255 					    sizeof(adsin));
256 					if (sin->sin_addr.s_addr ==
257 					    adsin.sin_addr.s_addr)
258 						break;
259 				}
260 				if (ad->ai_addr->sa_family == AF_INET6 &&
261 				    sin6->sin6_family == AF_INET6 &&
262 				    ad->ai_addrlen >= sizeof(adsin6)) {
263 					memcpy(&adsin6, ad->ai_addr,
264 					    sizeof(adsin6));
265 					if (IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr,
266 					    &adsin6.sin6_addr))
267 						break;
268 				}
269 				ad = ad->ai_next;
270 			}
271 			if (ad != NULL) {
272 				if (sin->sin_family == AF_INET)
273 					tport = sin->sin_port;
274 				else
275 					tport = sin6->sin6_port;
276 				/*
277 				 * We have a match, so replace it with the first
278 				 * AF_INET or AF_INET6 address in the newres
279 				 * list.
280 				 */
281 				while (newres->ai_addr->sa_family != AF_INET &&
282 				    newres->ai_addr->sa_family != AF_INET6) {
283 					newres = newres->ai_next;
284 					if (newres == NULL)
285 						errx(1, "Hostname %s has no"
286 						    " IP#", cp);
287 				}
288 				if (newres->ai_addr->sa_family == AF_INET) {
289 					memcpy(sin, newres->ai_addr,
290 					    sizeof(*sin));
291 					sin->sin_port = tport;
292 				} else if (newres->ai_addr->sa_family ==
293 				    AF_INET6) {
294 					memcpy(sin6, newres->ai_addr,
295 					    sizeof(*sin6));
296 					sin6->sin6_port = tport;
297 				}
298 				dosetxattr = 1;
299 			}
300 		}
301 
302 		if (quiet == 0) {
303 			/* Translate the IP address to a hostname. */
304 			if (getnameinfo((struct sockaddr *)&dsfile[i].dsf_sin,
305 			    dsfile[i].dsf_sin.sin_len, hostn, sizeof(hostn),
306 			    NULL, 0, 0) < 0)
307 				err(1, "Can't get hostname");
308 			printf("%s\tds%d/%s", hostn, dsfile[i].dsf_dir,
309 			    dsfile[i].dsf_filename);
310 		}
311 	}
312 	if (quiet == 0)
313 		printf("\n");
314 	if (dosetxattr != 0 && extattr_set_file(*argv, EXTATTR_NAMESPACE_SYSTEM,
315 	    "pnfsd.dsfile", dsfile, xattrsize) != xattrsize)
316 		err(1, "Can't set pnfsd.dsfile");
317 }
318 
319 static void
320 usage(void)
321 {
322 
323 	fprintf(stderr, "pnfsdsfile [-q/--quiet] [-z/--zerofh] "
324 	    "[-c/--changeds <old dshostname> <new dshostname>] "
325 	    "[-r/--zerods <dshostname>] "
326 	    "[-s/--ds <dshostname>] "
327 	    "<filename>\n");
328 	exit(1);
329 }
330 
331