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