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