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 50 #define FSTYPE_MAX 8 51 #define ARGV_MAX 1024 52 #define VFS_PATH "/usr/lib/fs" 53 #define ALT_PATH "/etc/fs" 54 55 extern char *default_fstype(); 56 void stat_snap(char *, char *, char *); 57 char *special = NULL; /* device special name */ 58 char *fstype = NULL; /* fstype name is filled in here */ 59 char *cbasename; /* name of command */ 60 char *newargv[ARGV_MAX]; /* args for the fstype specific command */ 61 char vfstab[] = VFSTAB; 62 int newargc = 2; 63 64 /* 65 * TRANSLATION_NOTE - the usage strings in the c_usgstr[] of the 66 * following structures should be given a translation; the call to gettext 67 * is in the usage() function. The strings are the ones containing 68 * "[-F FSType]". 69 */ 70 71 struct commands { 72 char *c_basename; 73 char *c_optstr; 74 char *c_usgstr[4]; /* make sure as large as largest array size */ 75 } cmd_data[] = { 76 "clri", "F:o:?V", 77 { 78 "[-F FSType] [-V] special inumber ...", 79 NULL 80 }, 81 "mkfs", "F:o:mb:?V", 82 { 83 "[-F FSType] [-V] [-m] [-o specific_options] special ", 84 "[operands]", NULL 85 }, 86 "dcopy", "F:o:?V", 87 { 88 "[-F FSType] [-V] special inumber ...", 89 NULL 90 }, 91 "fsdb", "F:o:z:?V", 92 { 93 "[-F FSType] [-V] [-o specific_options] special", 94 NULL 95 }, 96 "fssnap", "F:dio:?V", 97 { 98 "[-F FSType] [-V] -o special_options /mount/point", 99 "-d [-F FSType] [-V] /mount/point | dev", 100 "-i [-F FSType] [-V] [-o special-options] [/mount/point | dev]", 101 NULL 102 }, 103 "labelit", "F:o:?nV", 104 { 105 "[-F FSType] [-V] [-o specific_options] special [operands]", 106 NULL 107 }, 108 NULL, "F:o:?V", 109 { 110 "[-F FSType] [-V] [-o specific_options] special [operands]", 111 NULL 112 } 113 }; 114 struct commands *c_ptr; 115 116 main(argc, argv) 117 int argc; 118 char *argv[]; 119 { 120 register char *ptr; 121 char full_path[PATH_MAX]; 122 char *vfs_path = VFS_PATH; 123 char *alt_path = ALT_PATH; 124 int i; 125 int j; 126 int verbose = 0; /* set if -V is specified */ 127 int F_flg = 0; 128 int mflag = 0; 129 int Nflag = 0; 130 char *oopts = NULL; 131 char *tmpopts = NULL; /* used for in use checking */ 132 int iflag = 0; 133 int usgflag = 0; 134 int arg; /* argument from getopt() */ 135 char *msg; 136 int error; 137 extern char *optarg; /* getopt specific */ 138 extern int optind; 139 extern int opterr; 140 141 (void) setlocale(LC_ALL, ""); 142 143 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 144 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ 145 #endif 146 147 (void) textdomain(TEXT_DOMAIN); 148 149 cbasename = ptr = argv[0]; 150 while (*ptr) { 151 if (*ptr++ == '/') 152 cbasename = ptr; 153 } 154 155 156 if (argc == 1) { 157 for (c_ptr = cmd_data; ((c_ptr->c_basename != NULL) && 158 (strcmp(c_ptr->c_basename, cbasename) != 0)); c_ptr++) 159 ; 160 usage(cbasename, c_ptr->c_usgstr); 161 exit(2); 162 } 163 164 for (c_ptr = cmd_data; ((c_ptr->c_basename != NULL) && 165 (strcmp(c_ptr->c_basename, cbasename) != 0)); c_ptr++) 166 ; 167 while ((arg = getopt(argc, argv, c_ptr->c_optstr)) != -1) { 168 switch (arg) { 169 case 'V': /* echo complete command line */ 170 verbose = 1; 171 break; 172 case 'F': /* FSType specified */ 173 F_flg++; 174 fstype = optarg; 175 break; 176 case 'o': /* FSType specific arguments */ 177 newargv[newargc++] = "-o"; 178 newargv[newargc++] = optarg; 179 oopts = optarg; 180 if (!Nflag) { 181 tmpopts = optarg; 182 Nflag = has_Nflag(tmpopts); 183 } 184 break; 185 case '?': /* print usage message */ 186 newargv[newargc++] = "-?"; 187 usgflag = 1; 188 break; 189 case 'm': /* FSType specific arguments */ 190 mflag = 1; 191 newargv[newargc] = (char *)malloc(3); 192 sprintf(newargv[newargc++], "-%c", arg); 193 if (optarg) 194 newargv[newargc++] = optarg; 195 break; 196 case 'i': /* fssnap only */ 197 iflag = 1; 198 /*FALLTHROUGH*/ 199 default: 200 newargv[newargc] = (char *)malloc(3); 201 sprintf(newargv[newargc++], "-%c", arg); 202 if (optarg) 203 newargv[newargc++] = optarg; 204 break; 205 } 206 optarg = NULL; 207 } 208 if (F_flg > 1) { 209 (void) fprintf(stderr, 210 gettext("%s: more than one FSType specified\n"), 211 cbasename); 212 usage(cbasename, c_ptr->c_usgstr); 213 exit(2); 214 } 215 if (fstype != NULL) { 216 if (strlen(fstype) > FSTYPE_MAX) { 217 (void) fprintf(stderr, 218 gettext("%s: FSType %s exceeds %d characters\n"), 219 cbasename, fstype, FSTYPE_MAX); 220 exit(2); 221 } 222 } 223 224 /* perform a lookup if fstype is not specified */ 225 special = argv[optind]; 226 optind++; 227 228 /* handle -i (fssnap command only) */ 229 if (iflag) { 230 int diff = argc - optind; 231 /* 232 * There is no reason to ever call a file system specific 233 * version since its all in kstats. 234 */ 235 if (diff > 0) /* gave more than one mountpoint or device */ 236 usage(cbasename, c_ptr->c_usgstr); 237 stat_snap(cbasename, diff == 0 ? argv[argc-1] : NULL, oopts); 238 exit(0); 239 } 240 241 if ((special == NULL) && (!usgflag)) { 242 (void) fprintf(stderr, gettext("%s: special not specified\n"), 243 cbasename); 244 usage(cbasename, c_ptr->c_usgstr); 245 exit(2); 246 } 247 248 if ((fstype == NULL) && (usgflag)) 249 usage(cbasename, c_ptr->c_usgstr); 250 if (fstype == NULL) 251 lookup(); 252 if (fstype == NULL) { 253 (void) fprintf(stderr, 254 gettext("%s: FSType cannot be identified\n"), cbasename); 255 usage(cbasename, c_ptr->c_usgstr); 256 exit(2); 257 } 258 newargv[newargc++] = special; 259 for (; optind < argc; optind++) 260 newargv[newargc++] = argv[optind]; 261 262 /* build the full pathname of the fstype dependent command */ 263 sprintf(full_path, "%s/%s/%s", vfs_path, fstype, cbasename); 264 265 newargv[1] = cbasename; 266 267 if (verbose) { 268 printf("%s -F %s ", cbasename, fstype); 269 for (i = 2; newargv[i]; i++) 270 printf("%s ", newargv[i]); 271 printf("\n"); 272 exit(0); 273 } 274 275 /* 276 * Prior to executing the command for mkfs check for device in use. 277 * If the mflag is set, user wants to see command that created 278 * an already existing filesystem. Do not check for in use in this 279 * case. If Nflag is set user wants to see what the parameters 280 * would be to create the filesystem. Do not check for in use in 281 * this case. 282 */ 283 if (strcmp(cbasename, "mkfs") == 0 && !mflag && !Nflag) { 284 if (dm_inuse(special, &msg, DM_WHO_MKFS, &error) || 285 error) { 286 if (error != 0) { 287 (void) fprintf(stderr, gettext("Error occurred" 288 " with device in use checking: %s\n"), 289 strerror(error)); 290 } else { 291 (void) fprintf(stderr, "%s", msg); 292 free(msg); 293 exit(2); 294 } 295 } 296 } 297 298 /* 299 * Execute the FSType specific command. 300 */ 301 execv(full_path, &newargv[1]); 302 if ((errno == ENOENT) || (errno == EACCES)) { 303 /* build the alternate pathname */ 304 sprintf(full_path, "%s/%s/%s", alt_path, fstype, cbasename); 305 if (verbose) { 306 printf("%s -F %s ", cbasename, fstype); 307 for (i = 2; newargv[i]; i++) 308 printf("%s ", newargv[i]); 309 printf("\n"); 310 exit(0); 311 } 312 execv(full_path, &newargv[1]); 313 } 314 if (errno == ENOEXEC) { 315 newargv[0] = "sh"; 316 newargv[1] = full_path; 317 execv("/sbin/sh", &newargv[0]); 318 } 319 if (errno != ENOENT) { 320 perror(cbasename); 321 (void) fprintf(stderr, gettext("%s: cannot execute %s\n"), 322 cbasename, full_path); 323 exit(2); 324 } 325 326 if (sysfs(GETFSIND, fstype) == (-1)) { 327 (void) fprintf(stderr, 328 gettext("%s: FSType %s not installed in the kernel\n"), 329 cbasename, fstype); 330 exit(2); 331 } 332 (void) fprintf(stderr, 333 gettext("%s: Operation not applicable for FSType %s \n"), 334 cbasename, fstype); 335 exit(2); 336 } 337 338 usage(cmd, usg) 339 char *cmd; 340 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 lookup() 360 { 361 FILE *fd; 362 int ret; 363 struct vfstab vget, vref; 364 365 if ((fd = fopen(vfstab, "r")) == NULL) { 366 (void) fprintf(stderr, gettext("%s: cannot open vfstab\n"), 367 cbasename); 368 exit(1); 369 } 370 vfsnull(&vref); 371 vref.vfs_special = special; 372 ret = getvfsany(fd, &vget, &vref); 373 if (ret == -1) { 374 rewind(fd); 375 vfsnull(&vref); 376 vref.vfs_fsckdev = special; 377 ret = getvfsany(fd, &vget, &vref); 378 } 379 fclose(fd); 380 381 switch (ret) { 382 case -1: 383 fstype = default_fstype(special); 384 break; 385 case 0: 386 fstype = vget.vfs_fstype; 387 break; 388 case VFS_TOOLONG: 389 (void) fprintf(stderr, 390 gettext("%s: line in vfstab exceeds %d characters\n"), 391 cbasename, VFS_LINE_MAX-2); 392 exit(1); 393 break; 394 case VFS_TOOFEW: 395 (void) fprintf(stderr, 396 gettext("%s: line in vfstab has too few entries\n"), 397 cbasename); 398 exit(1); 399 break; 400 } 401 } 402 403 void 404 stat_snap(cmd, mountpoint, opts) 405 char *cmd; 406 char *mountpoint; 407 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