2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2017 Rick Macklem
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
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>
49 static void usage(void);
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' },
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.
68 main(int argc, char *argv[])
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;
77 ssize_t xattrsize, xattrsize2;
87 while ((ch = getopt_long(argc, argv, "c:m:qr:s:z", longopts, NULL)) !=
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, ',');
99 errx(1, "Bad -c argument %s", hostn);
101 if (getaddrinfo(hostn, NULL, NULL, &res) != 0)
102 errx(1, "Can't get IP# for %s", hostn);
104 if (getaddrinfo(cp, NULL, NULL, &newres) != 0)
105 errx(1, "Can't get IP# for %s", cp);
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);
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");
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);
132 /* Translate the server name to an IP address. */
133 if (zerods != 0 || mirrorit != 0 || newres != 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);
141 if (zerofh != 0 || zerods != 0 || mirrorit != 0 ||
143 errx(1, "-c, -m and -r are mutually exclusive "
144 "from use with -s and -z");
157 * The host address and directory where the data storage file is
158 * located is in the extended attribute "pnfsd.dsfile".
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);
168 printf("%s:\t", *argv);
169 for (i = 0; i < mirrorcnt; i++) {
170 if (i > 0 && quiet == 0)
172 /* Do the zerofh option. You must be root. */
175 errx(1, "Must be root/su to zerofh");
178 * Do it for the server specified by -s/--ds or all
179 * servers, if -s/--ds was not specified.
181 sin = &dsfile[i].dsf_sin;
182 sin6 = &dsfile[i].dsf_sin6;
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,
190 if (sin->sin_addr.s_addr ==
191 adsin.sin_addr.s_addr)
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,
199 if (IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr,
205 if (res == NULL || ad != NULL) {
206 memset(&dsfile[i].dsf_fh, 0, sizeof(fhandle_t));
211 /* Do the zerods option. You must be root. */
212 if (zerods != 0 && mirrorcnt > 1) {
214 errx(1, "Must be root/su to zerods");
217 * Do it for the server specified.
219 sin = &dsfile[i].dsf_sin;
220 sin6 = &dsfile[i].dsf_sin6;
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,
228 if (sin->sin_addr.s_addr ==
229 adsin.sin_addr.s_addr)
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,
237 if (IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr,
244 sin->sin_family = AF_INET;
245 sin->sin_len = sizeof(*sin);
247 sin->sin_addr.s_addr = 0;
252 /* Do the -c option to replace the DS host address. */
253 if (newres != NULL) {
255 errx(1, "Must be root/su to replace the host"
259 * Check that the old host address matches.
261 sin = &dsfile[i].dsf_sin;
262 sin6 = &dsfile[i].dsf_sin6;
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,
270 if (sin->sin_addr.s_addr ==
271 adsin.sin_addr.s_addr)
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,
279 if (IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr,
286 if (sin->sin_family == AF_INET)
287 tport = sin->sin_port;
289 tport = sin6->sin6_port;
291 * We have a match, so replace it with the first
292 * AF_INET or AF_INET6 address in the newres
295 while (newres->ai_addr->sa_family != AF_INET &&
296 newres->ai_addr->sa_family != AF_INET6) {
297 newres = newres->ai_next;
299 errx(1, "Hostname %s has no"
302 if (newres->ai_addr->sa_family == AF_INET) {
303 memcpy(sin, newres->ai_addr,
305 sin->sin_port = tport;
306 } else if (newres->ai_addr->sa_family ==
308 memcpy(sin6, newres->ai_addr,
310 sin6->sin6_port = tport;
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),
321 err(1, "Can't get hostname");
322 printf("%s\tds%d/%s", hostn, dsfile[i].dsf_dir,
323 dsfile[i].dsf_filename);
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;
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);
339 if (mirrorit > mirrorcnt) {
340 xattrsize = mirrorit * sizeof(struct pnfsdsfile);
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");
355 fprintf(stderr, "pnfsdsfile [-q/--quiet] [-z/--zerofh] "
356 "[-c/--changeds <old dshostname> <new dshostname>] "
357 "[-r/--zerods <dshostname>] "
358 "[-s/--ds <dshostname>] "