1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include <stdio.h> 28 #include <unistd.h> 29 #include <strings.h> 30 #include <string.h> 31 #include <limits.h> 32 #include <libnvpair.h> 33 #include <locale.h> 34 #include <sys/stat.h> 35 #include <sys/fs_reparse.h> 36 #include <rp_plugin.h> 37 #include <uuid/uuid.h> 38 #include <sys/types.h> 39 #include <sys/stat.h> 40 #include <fcntl.h> 41 #include <priv.h> 42 #include <nfs/nfs4.h> 43 #include <rpcsvc/nfs4_prot.h> 44 #include "ref_subr.h" 45 46 #ifndef TEXT_DOMAIN 47 #define TEXT_DOMAIN "SUNW_OST_OSCMD" 48 #endif /* TEXT_DOMAIN */ 49 50 extern int errno; 51 52 void 53 usage() 54 { 55 fprintf(stderr, gettext("Usage:\n")); 56 fprintf(stderr, 57 gettext("\tnfsref [-t type] add path location [location ...]\n")); 58 fprintf(stderr, gettext("\tnfsref [-t type] remove path\n")); 59 fprintf(stderr, gettext("\tnfsref [-t type] lookup path\n")); 60 } 61 62 /* 63 * Copy a string from source to destination, escaping space 64 * with a backslash and escaping the escape character as well. 65 */ 66 int 67 add_escape(char *src, char *dest, int limit) 68 { 69 char *sp, *dp; 70 int destlen = 0; 71 72 sp = src; 73 dp = dest; 74 75 while (*sp && destlen < limit) { 76 if (*sp == '\\') { 77 *dp++ = '\\'; 78 *dp++ = '\\'; 79 destlen++; 80 } else if (*sp == ' ') { 81 *dp++ = '\\'; 82 *dp++ = ' '; 83 destlen++; 84 } else 85 *dp++ = *sp; 86 destlen++; 87 sp++; 88 } 89 if (limit <= 0) 90 return (-1); 91 return (destlen); 92 } 93 94 int 95 addref(char *sl_path, char *svc_type, int optind, int argc, char *argv[]) 96 { 97 int err, fd, i, len, oldlen, notfound = 0; 98 char *text, *location; 99 nvlist_t *nvl = NULL; 100 char buf[SYMLINK_MAX]; 101 struct stat sbuf; 102 103 /* Get an nvlist */ 104 nvl = reparse_init(); 105 if (nvl == NULL) 106 return (ENOMEM); 107 108 /* Get the reparse point data, if the RP exists */ 109 err = readlink(sl_path, buf, SYMLINK_MAX); 110 if (err == -1) { 111 if (errno == ENOENT) { 112 notfound = 1; 113 err = 0; 114 } else { 115 reparse_free(nvl); 116 return (errno); 117 } 118 } else { 119 buf[err] = '\0'; 120 } 121 122 /* Get any data into nvlist */ 123 if (notfound == 0) 124 err = reparse_parse(buf, nvl); 125 if (err != 0) { 126 reparse_free(nvl); 127 return (err); 128 } 129 130 /* 131 * Accumulate multiple locations on the command line into 'buf' 132 */ 133 oldlen = len = 0; 134 location = NULL; 135 for (i = optind; i < argc; i++) { 136 bzero(buf, sizeof (buf)); 137 len += add_escape(argv[i], buf, SYMLINK_MAX) + 2; 138 location = realloc(location, len); 139 location[oldlen] = '\0'; 140 oldlen = len; 141 strlcat(location, buf, len); 142 strlcat(location, " ", len); 143 } 144 location[len - 2] = '\0'; 145 146 /* Add to the list */ 147 err = reparse_add(nvl, svc_type, location); 148 if (err) { 149 reparse_free(nvl); 150 return (err); 151 } 152 153 /* Get the new or modified symlink contents */ 154 err = reparse_unparse(nvl, &text); 155 reparse_free(nvl); 156 if (err) 157 return (err); 158 159 /* Delete first if found */ 160 if (notfound == 0) { 161 err = reparse_delete(sl_path); 162 if (err) { 163 free(text); 164 return (err); 165 } 166 } 167 168 /* Finally, write out the reparse point */ 169 err = reparse_create(sl_path, text); 170 free(text); 171 if (err) 172 return (err); 173 174 err = lstat(sl_path, &sbuf); 175 if (err == 0 && strcasecmp(sbuf.st_fstype, "ZFS") != 0) 176 printf(gettext( 177 "Warning: referrals do not work on this filesystem\n")); 178 179 if (notfound) 180 printf(gettext("Created reparse point %s\n"), sl_path); 181 else 182 printf(gettext("Added to reparse point %s\n"), sl_path); 183 184 return (0); 185 } 186 187 int 188 delref(char *sl_path, char *svc_type) 189 { 190 char *cp; 191 char *svc_data; 192 int err; 193 nvlist_t *nvl; 194 nvpair_t *curr; 195 char buf[SYMLINK_MAX]; 196 int fd, fd2; 197 FILE *fp, *fp2; 198 char uuid[UUID_PRINTABLE_STRING_LENGTH], path[256], loc[2048]; 199 200 /* Get an nvlist */ 201 if (!(nvl = reparse_init())) 202 return (ENOMEM); 203 204 /* Get the symlink data (should be there) */ 205 err = readlink(sl_path, buf, SYMLINK_MAX); 206 if (err == -1) { 207 reparse_free(nvl); 208 return (errno); 209 } 210 buf[err] = '\0'; 211 212 /* Get the records into the nvlist */ 213 err = reparse_parse(buf, nvl); 214 if (err) { 215 reparse_free(nvl); 216 return (err); 217 } 218 219 /* Remove from nvlist */ 220 err = reparse_remove(nvl, svc_type); 221 if (err) { 222 reparse_free(nvl); 223 return (err); 224 } 225 226 /* Any list entries left? If so, turn nvlist back to string. */ 227 curr = nvlist_next_nvpair(nvl, NULL); 228 if (curr != NULL) { 229 err = reparse_unparse(nvl, &cp); 230 reparse_free(nvl); 231 if (err) 232 return (err); 233 } else { 234 reparse_free(nvl); 235 cp = NULL; 236 } 237 238 /* Finally, delete and perhaps recreate the reparse point */ 239 err = reparse_delete(sl_path); 240 if (err) { 241 free(cp); 242 return (err); 243 } 244 245 if (cp != NULL) { 246 err = reparse_create(sl_path, cp); 247 free(cp); 248 if (err) 249 return (err); 250 } 251 printf(gettext("Removed svc_type '%s' from %s\n"), svc_type, sl_path); 252 return (err); 253 } 254 255 int 256 lookup(char *sl_path, char *svc_type, int type_set) 257 { 258 int err; 259 size_t bufsize; 260 char buf[1024]; 261 char *type, *svc_data; 262 nvlist_t *nvl; 263 nvpair_t *curr; 264 fs_locations4 fsl; 265 XDR xdr; 266 267 if (!(nvl = reparse_init())) 268 return (-1); 269 270 /* Get reparse point data */ 271 err = readlink(sl_path, buf, SYMLINK_MAX); 272 if (err == -1) 273 return (errno); 274 buf[err] = '\0'; 275 276 /* Parse it to an nvlist */ 277 err = reparse_parse(buf, nvl); 278 if (err) { 279 reparse_free(nvl); 280 return (err); 281 } 282 283 /* Look for entries of the requested service type */ 284 curr = NULL; 285 while ((curr = nvlist_next_nvpair(nvl, curr)) != NULL) { 286 type = nvpair_name(curr); 287 if (type_set && strcasecmp(type, svc_type) == 0) 288 break; 289 if (!type_set && strncasecmp(type, "nfs", 3) == 0) 290 break; 291 } 292 if (curr == NULL) { 293 reparse_free(nvl); 294 return (ENOENT); 295 } 296 297 /* Get the service data and look it up */ 298 nvpair_value_string(curr, &svc_data); 299 300 bufsize = sizeof (buf); 301 err = reparse_deref(type, svc_data, buf, &bufsize); 302 reparse_free(nvl); 303 if (err) 304 return (err); 305 306 xdrmem_create(&xdr, buf, bufsize, XDR_DECODE); 307 err = xdr_fs_locations4(&xdr, &fsl); 308 XDR_DESTROY(&xdr); 309 if (err != TRUE) 310 return (ENOENT); 311 printf(gettext("%s points to: "), sl_path); 312 print_referral_summary(&fsl); 313 return (0); 314 } 315 316 extern char *optarg; 317 extern int optind, optopt; 318 319 int 320 main(int argc, char *argv[]) 321 { 322 char *command, *sl_path, *svc_type; 323 int c, type_set, err; 324 325 (void) setlocale(LC_ALL, ""); 326 (void) textdomain(TEXT_DOMAIN); 327 328 svc_type = "nfs-basic"; /* Default from SMF some day */ 329 type_set = 0; /* Lookup any nfs type */ 330 331 /* Look for options (just the service type now) */ 332 while ((c = getopt(argc, argv, "t:")) != -1) { 333 switch (c) { 334 case 't': 335 svc_type = optarg; 336 type_set = 1; 337 break; 338 339 default: 340 usage(); 341 exit(1); 342 } 343 } 344 345 /* Make sure there's at least a command and one argument */ 346 if (optind + 1 >= argc) { 347 usage(); 348 exit(1); 349 } 350 351 err = rp_plugin_init(); 352 switch (err) { 353 case RP_OK: 354 break; 355 case RP_NO_PLUGIN_DIR: 356 fprintf(stderr, 357 gettext("Warning: no plugin directory, continuing...\n")); 358 break; 359 case RP_NO_PLUGIN: 360 fprintf(stderr, 361 gettext("Warning: no plugin found, continuing...\n")); 362 break; 363 case RP_NO_MEMORY: 364 fprintf(stderr, 365 gettext("rp_plugin_init failed, no memory\n")); 366 exit(0); 367 default: 368 fprintf(stderr, 369 gettext("rp_plugin_init failed, error %d\n"), err); 370 exit(0); 371 } 372 373 command = argv[optind++]; 374 sl_path = argv[optind++]; 375 376 if (strcmp(command, "add") == 0) { 377 378 if (optind >= argc) { 379 usage(); 380 exit(1); 381 } 382 383 err = addref(sl_path, svc_type, optind, argc, argv); 384 385 } else if (strcmp(command, "remove") == 0) { 386 387 err = delref(sl_path, svc_type); 388 389 } else if (strcmp(command, "lookup") == 0) { 390 391 err = lookup(sl_path, svc_type, type_set); 392 393 } else { 394 usage(); 395 exit(1); 396 } 397 if (err != 0) 398 fprintf(stderr, gettext("Command %s failed: %s\n"), command, 399 strerror(err)); 400 return (err); 401 } 402