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