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); 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