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