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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 23 /* All Rights Reserved */ 24 25 26 /* 27 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 28 * Use is subject to license terms. 29 */ 30 31 #pragma ident "%Z%%M% %I% %E% SMI" 32 33 #include <stdio.h> 34 #include <limits.h> 35 #include <locale.h> 36 #include <libintl.h> 37 #include <sys/fstyp.h> 38 #include <errno.h> 39 #include <sys/vfstab.h> 40 #include <sys/types.h> 41 #include <sys/stat.h> 42 #include <fcntl.h> 43 #include <string.h> 44 #include <libdiskmgt.h> 45 #include "fslib.h" 46 47 48 static int match(char **opts, char *s); 49 static int has_Nflag(char *opts); 50 51 #define FSTYPE_MAX 8 52 #define ARGV_MAX 1024 53 #define VFS_PATH "/usr/lib/fs" 54 #define ALT_PATH "/etc/fs" 55 56 extern char *default_fstype(); 57 void stat_snap(char *, char *, char *); 58 char *special = NULL; /* device special name */ 59 char *fstype = NULL; /* fstype name is filled in here */ 60 char *cbasename; /* name of command */ 61 char *newargv[ARGV_MAX]; /* args for the fstype specific command */ 62 char vfstab[] = VFSTAB; 63 int newargc = 2; 64 65 /* 66 * TRANSLATION_NOTE - the usage strings in the c_usgstr[] of the 67 * following structures should be given a translation; the call to gettext 68 * is in the usage() function. The strings are the ones containing 69 * "[-F FSType]". 70 */ 71 72 struct commands { 73 char *c_basename; 74 char *c_optstr; 75 char *c_usgstr[4]; /* make sure as large as largest array size */ 76 } cmd_data[] = { 77 "clri", "F:o:?V", 78 { 79 "[-F FSType] [-V] special inumber ...", 80 NULL 81 }, 82 "mkfs", "F:o:mb:?V", 83 { 84 "[-F FSType] [-V] [-m] [-o specific_options] special ", 85 "[operands]", NULL 86 }, 87 "dcopy", "F:o:?V", 88 { 89 "[-F FSType] [-V] special inumber ...", 90 NULL 91 }, 92 "fsdb", "F:o:z:?V", 93 { 94 "[-F FSType] [-V] [-o specific_options] special", 95 NULL 96 }, 97 "fssnap", "F:dio:?V", 98 { 99 "[-F FSType] [-V] -o special_options /mount/point", 100 "-d [-F FSType] [-V] /mount/point | dev", 101 "-i [-F FSType] [-V] [-o special-options] [/mount/point | dev]", 102 NULL 103 }, 104 "labelit", "F:o:?nV", 105 { 106 "[-F FSType] [-V] [-o specific_options] special [operands]", 107 NULL 108 }, 109 NULL, "F:o:?V", 110 { 111 "[-F FSType] [-V] [-o specific_options] special [operands]", 112 NULL 113 } 114 }; 115 struct commands *c_ptr; 116 117 static void usage(char *cmd, char **usg); 118 static void lookup(void); 119 120 int 121 main(int argc, char *argv[]) 122 { 123 char *ptr; 124 char full_path[PATH_MAX]; 125 char *vfs_path = VFS_PATH; 126 char *alt_path = ALT_PATH; 127 int i; 128 int j; 129 int verbose = 0; /* set if -V is specified */ 130 int F_flg = 0; 131 int mflag = 0; 132 int Nflag = 0; 133 char *oopts = NULL; 134 char *tmpopts = NULL; /* used for in use checking */ 135 int iflag = 0; 136 int usgflag = 0; 137 int arg; /* argument from getopt() */ 138 char *msg; 139 int error; 140 extern char *optarg; /* getopt specific */ 141 extern int optind; 142 extern int opterr; 143 144 (void) setlocale(LC_ALL, ""); 145 146 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 147 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ 148 #endif 149 150 (void) textdomain(TEXT_DOMAIN); 151 152 cbasename = ptr = argv[0]; 153 while (*ptr) { 154 if (*ptr++ == '/') 155 cbasename = ptr; 156 } 157 158 159 if (argc == 1) { 160 for (c_ptr = cmd_data; ((c_ptr->c_basename != NULL) && 161 (strcmp(c_ptr->c_basename, cbasename) != 0)); c_ptr++) 162 ; 163 usage(cbasename, c_ptr->c_usgstr); 164 exit(2); 165 } 166 167 for (c_ptr = cmd_data; ((c_ptr->c_basename != NULL) && 168 (strcmp(c_ptr->c_basename, cbasename) != 0)); c_ptr++) 169 ; 170 while ((arg = getopt(argc, argv, c_ptr->c_optstr)) != -1) { 171 switch (arg) { 172 case 'V': /* echo complete command line */ 173 verbose = 1; 174 break; 175 case 'F': /* FSType specified */ 176 F_flg++; 177 fstype = optarg; 178 break; 179 case 'o': /* FSType specific arguments */ 180 newargv[newargc++] = "-o"; 181 newargv[newargc++] = optarg; 182 oopts = optarg; 183 if (!Nflag) { 184 tmpopts = optarg; 185 Nflag = has_Nflag(tmpopts); 186 } 187 break; 188 case '?': /* print usage message */ 189 newargv[newargc++] = "-?"; 190 usgflag = 1; 191 break; 192 case 'm': /* FSType specific arguments */ 193 mflag = 1; 194 newargv[newargc] = (char *)malloc(3); 195 sprintf(newargv[newargc++], "-%c", arg); 196 if (optarg) 197 newargv[newargc++] = optarg; 198 break; 199 case 'i': /* fssnap only */ 200 iflag = 1; 201 /*FALLTHROUGH*/ 202 default: 203 newargv[newargc] = (char *)malloc(3); 204 sprintf(newargv[newargc++], "-%c", arg); 205 if (optarg) 206 newargv[newargc++] = optarg; 207 break; 208 } 209 optarg = NULL; 210 } 211 if (F_flg > 1) { 212 (void) fprintf(stderr, 213 gettext("%s: more than one FSType specified\n"), 214 cbasename); 215 usage(cbasename, c_ptr->c_usgstr); 216 exit(2); 217 } 218 if (fstype != NULL) { 219 if (strlen(fstype) > FSTYPE_MAX) { 220 (void) fprintf(stderr, 221 gettext("%s: FSType %s exceeds %d characters\n"), 222 cbasename, fstype, FSTYPE_MAX); 223 exit(2); 224 } 225 } 226 227 /* perform a lookup if fstype is not specified */ 228 special = argv[optind]; 229 optind++; 230 231 /* handle -i (fssnap command only) */ 232 if (iflag) { 233 int diff = argc - optind; 234 /* 235 * There is no reason to ever call a file system specific 236 * version since its all in kstats. 237 */ 238 if (diff > 0) /* gave more than one mountpoint or device */ 239 usage(cbasename, c_ptr->c_usgstr); 240 stat_snap(cbasename, diff == 0 ? argv[argc-1] : NULL, oopts); 241 exit(0); 242 } 243 244 if ((special == NULL) && (!usgflag)) { 245 (void) fprintf(stderr, gettext("%s: special not specified\n"), 246 cbasename); 247 usage(cbasename, c_ptr->c_usgstr); 248 exit(2); 249 } 250 251 if ((fstype == NULL) && (usgflag)) 252 usage(cbasename, c_ptr->c_usgstr); 253 if (fstype == NULL) 254 lookup(); 255 if (fstype == NULL) { 256 (void) fprintf(stderr, 257 gettext("%s: FSType cannot be identified\n"), cbasename); 258 usage(cbasename, c_ptr->c_usgstr); 259 exit(2); 260 } 261 newargv[newargc++] = special; 262 for (; optind < argc; optind++) 263 newargv[newargc++] = argv[optind]; 264 265 /* build the full pathname of the fstype dependent command */ 266 sprintf(full_path, "%s/%s/%s", vfs_path, fstype, cbasename); 267 268 newargv[1] = cbasename; 269 270 if (verbose) { 271 printf("%s -F %s ", cbasename, fstype); 272 for (i = 2; newargv[i]; i++) 273 printf("%s ", newargv[i]); 274 printf("\n"); 275 exit(0); 276 } 277 278 /* 279 * Prior to executing the command for mkfs check for device in use. 280 * If the mflag is set, user wants to see command that created 281 * an already existing filesystem. Do not check for in use in this 282 * case. If Nflag is set user wants to see what the parameters 283 * would be to create the filesystem. Do not check for in use in 284 * this case. 285 */ 286 if (strcmp(cbasename, "mkfs") == 0 && !mflag && !Nflag) { 287 if (dm_inuse(special, &msg, DM_WHO_MKFS, &error) || 288 error) { 289 if (error != 0) { 290 (void) fprintf(stderr, gettext("Error occurred" 291 " with device in use checking: %s\n"), 292 strerror(error)); 293 } else { 294 (void) fprintf(stderr, "%s", msg); 295 free(msg); 296 exit(2); 297 } 298 } 299 } 300 301 /* 302 * Execute the FSType specific command. 303 */ 304 execv(full_path, &newargv[1]); 305 if ((errno == ENOENT) || (errno == EACCES)) { 306 /* build the alternate pathname */ 307 sprintf(full_path, "%s/%s/%s", alt_path, fstype, cbasename); 308 if (verbose) { 309 printf("%s -F %s ", cbasename, fstype); 310 for (i = 2; newargv[i]; i++) 311 printf("%s ", newargv[i]); 312 printf("\n"); 313 exit(0); 314 } 315 execv(full_path, &newargv[1]); 316 } 317 if (errno == ENOEXEC) { 318 newargv[0] = "sh"; 319 newargv[1] = full_path; 320 execv("/sbin/sh", &newargv[0]); 321 } 322 if (errno != ENOENT) { 323 perror(cbasename); 324 (void) fprintf(stderr, gettext("%s: cannot execute %s\n"), 325 cbasename, full_path); 326 exit(2); 327 } 328 329 if (sysfs(GETFSIND, fstype) == (-1)) { 330 (void) fprintf(stderr, 331 gettext("%s: FSType %s not installed in the kernel\n"), 332 cbasename, fstype); 333 exit(2); 334 } 335 (void) fprintf(stderr, 336 gettext("%s: Operation not applicable for FSType %s \n"), 337 cbasename, fstype); 338 return (2); 339 } 340 341 static void 342 usage(char *cmd, char **usg) 343 { 344 int i; 345 (void) fprintf(stderr, gettext("Usage:\n")); 346 for (i = 0; usg[i] != NULL; i++) 347 (void) fprintf(stderr, "%s %s\n", gettext(cmd), 348 gettext(usg[i])); 349 exit(2); 350 } 351 352 353 /* 354 * This looks up the /etc/vfstab entry given the device 'special'. 355 * It is called when the fstype is not specified on the command line. 356 * 357 * The following global variables are used: 358 * special, fstype 359 */ 360 361 static void 362 lookup(void) 363 { 364 FILE *fd; 365 int ret; 366 struct vfstab vget, vref; 367 368 if ((fd = fopen(vfstab, "r")) == NULL) { 369 (void) fprintf(stderr, gettext("%s: cannot open vfstab\n"), 370 cbasename); 371 exit(1); 372 } 373 vfsnull(&vref); 374 vref.vfs_special = special; 375 ret = getvfsany(fd, &vget, &vref); 376 if (ret == -1) { 377 rewind(fd); 378 vfsnull(&vref); 379 vref.vfs_fsckdev = special; 380 ret = getvfsany(fd, &vget, &vref); 381 } 382 fclose(fd); 383 384 switch (ret) { 385 case -1: 386 fstype = default_fstype(special); 387 break; 388 case 0: 389 fstype = vget.vfs_fstype; 390 break; 391 case VFS_TOOLONG: 392 (void) fprintf(stderr, 393 gettext("%s: line in vfstab exceeds %d characters\n"), 394 cbasename, VFS_LINE_MAX-2); 395 exit(1); 396 break; 397 case VFS_TOOFEW: 398 (void) fprintf(stderr, 399 gettext("%s: line in vfstab has too few entries\n"), 400 cbasename); 401 exit(1); 402 break; 403 } 404 } 405 406 void 407 stat_snap(char *cmd, char *mountpoint, char *opts) 408 { 409 int fd; /* check mount point if given */ 410 int en; 411 char *errstr; 412 413 if (mountpoint) { 414 if ((fd = open(mountpoint, O_RDONLY)) < 0) { 415 en = errno; 416 errstr = strerror(errno); 417 if (errstr == NULL) 418 errstr = gettext("Unknown error"); 419 420 (void) fprintf(stderr, 421 gettext("%s: %s: error %d: %s\n"), 422 cmd, mountpoint, en, errstr); 423 424 exit(2); 425 } 426 close(fd); 427 } 428 fssnap_show_status(mountpoint, opts, 1, (opts ? 0 : 1)); 429 } 430 static int 431 has_Nflag(char *opts) 432 { 433 while (opts != NULL && *opts != '\0') { 434 if (match(&opts, "N")) { 435 return (1); 436 } 437 if (!opts) 438 break; 439 if (*opts == ',') 440 opts ++; 441 if (*opts == ' ') 442 opts ++; 443 } 444 return (0); 445 } 446 /* 447 * Parses the -o [fs specific options string] to search for the UFS -N flag. 448 * Return the opts string pointing to the next position in the string if 449 * match is not found. A delimiter of , or ' ' can be used depending on the 450 * caller, newfs or mkfs. 451 */ 452 static int 453 match(char **opts, char *s) 454 { 455 char *cs; 456 char *tmp_str; 457 458 cs = *opts; 459 460 while (*cs++ == *s) { 461 if (*s++ == '\0') { 462 goto true; 463 } 464 } 465 if (*s != '\0') { 466 /* 467 * If we cannot find the delimiter it means we 468 * have hit the end of the string. 469 */ 470 tmp_str = strchr(*opts, ','); 471 if (!tmp_str) 472 tmp_str = strchr(*opts, ' '); 473 474 *opts = tmp_str; 475 return (0); 476 } 477 true: 478 cs--; 479 *opts = cs; 480 return (1); 481 } 482