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