1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2002 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Andrew Brown. 9 * 10 * Copyright (c) 2025-2026 Klara, Inc. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 23 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 25 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 * POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 #include <sys/param.h> 35 #include <sys/mount.h> 36 #include <sys/stat.h> 37 38 #include <ctype.h> 39 #include <err.h> 40 #include <errno.h> 41 #include <fcntl.h> 42 #include <grp.h> 43 #include <limits.h> 44 #include <locale.h> 45 #include <paths.h> 46 #include <pwd.h> 47 #include <stdbool.h> 48 #include <stdio.h> 49 #include <stdlib.h> 50 #include <string.h> 51 #include <time.h> 52 #include <unistd.h> 53 54 #define DEF_FORMAT \ 55 "%d %i %Sp %l %Su %Sg %r %z \"%Sa\" \"%Sm\" \"%Sc\" \"%SB\" " \ 56 "%k %b %#Xf %N" 57 #define RAW_FORMAT "%d %i %#p %l %u %g %r %z %a %m %c %B " \ 58 "%k %b %f %N" 59 #define LS_FORMAT "%Sp %l %Su %Sg %Z %Sm %N%SY" 60 #define LSF_FORMAT "%Sp %l %Su %Sg %Z %Sm %N%T%SY" 61 #define SHELL_FORMAT \ 62 "st_dev=%d st_ino=%i st_mode=%#p st_nlink=%l " \ 63 "st_uid=%u st_gid=%g st_rdev=%r st_size=%z " \ 64 "st_atime=%a st_mtime=%m st_ctime=%c st_birthtime=%B " \ 65 "st_blksize=%k st_blocks=%b st_flags=%f" 66 #define LINUX_FORMAT \ 67 " File: \"%N\"%n" \ 68 " Size: %-11z FileType: %HT%n" \ 69 " Mode: (%OMp%03OLp/%.10Sp) Uid: (%5u/%8Su) Gid: (%5g/%8Sg)%n" \ 70 "Device: %Hd,%Ld Inode: %i Links: %l%n" \ 71 "Access: %Sa%n" \ 72 "Modify: %Sm%n" \ 73 "Change: %Sc%n" \ 74 " Birth: %SB" 75 76 #define TIME_FORMAT "%b %e %T %Y" 77 78 #define FLAG_POUND 0x01 79 #define FLAG_SPACE 0x02 80 #define FLAG_PLUS 0x04 81 #define FLAG_ZERO 0x08 82 #define FLAG_MINUS 0x10 83 84 /* 85 * These format characters must all be unique, except the magic one. 86 */ 87 #define FMT_MAGIC '%' 88 #define FMT_DOT '.' 89 90 #define SIMPLE_NEWLINE 'n' 91 #define SIMPLE_TAB 't' 92 #define SIMPLE_PERCENT '%' 93 #define SIMPLE_NUMBER '@' 94 95 #define FMT_POUND '#' 96 #define FMT_SPACE ' ' 97 #define FMT_PLUS '+' 98 #define FMT_ZERO '0' 99 #define FMT_MINUS '-' 100 101 #define FMT_DECIMAL 'D' 102 #define FMT_OCTAL 'O' 103 #define FMT_UNSIGNED 'U' 104 #define FMT_HEX 'X' 105 #define FMT_FLOAT 'F' 106 #define FMT_STRING 'S' 107 108 #define FMTF_DECIMAL 0x01 109 #define FMTF_OCTAL 0x02 110 #define FMTF_UNSIGNED 0x04 111 #define FMTF_HEX 0x08 112 #define FMTF_FLOAT 0x10 113 #define FMTF_STRING 0x20 114 115 #define HIGH_PIECE 'H' 116 #define MIDDLE_PIECE 'M' 117 #define LOW_PIECE 'L' 118 119 #define SHOW_realpath 'R' 120 #define SHOW_st_dev 'd' 121 #define SHOW_st_ino 'i' 122 #define SHOW_st_mode 'p' 123 #define SHOW_st_nlink 'l' 124 #define SHOW_st_uid 'u' 125 #define SHOW_st_gid 'g' 126 #define SHOW_st_rdev 'r' 127 #define SHOW_st_atime 'a' 128 #define SHOW_st_mtime 'm' 129 #define SHOW_st_ctime 'c' 130 #define SHOW_st_btime 'B' 131 #define SHOW_st_size 'z' 132 #define SHOW_st_blocks 'b' 133 #define SHOW_st_blksize 'k' 134 #define SHOW_st_flags 'f' 135 #define SHOW_st_gen 'v' 136 #define SHOW_symlink 'Y' 137 #define SHOW_filetype 'T' 138 #define SHOW_filename 'N' 139 #define SHOW_sizerdev 'Z' 140 141 static void usage(const char *); 142 static int mountpoint_main(int, char *[]); 143 static void mountpoint_usage(void) __dead2; 144 static void output(const struct stat *, const char *, const char *, int); 145 static int format1(const struct stat *, /* stat info */ 146 const char *, /* the file name */ 147 const char *, int, /* the format string itself */ 148 char *, size_t, /* a place to put the output */ 149 int, int, int, int, /* the parsed format */ 150 int, int); 151 static int hex2byte(const char [2]); 152 static char *xfflagstostr(unsigned long); 153 static int fdlistholes(int, const char *); 154 static int listholes(const char *); 155 156 static const char *timefmt; 157 static int linkfail; 158 static bool nonl; 159 160 #define addchar(s, c, nl) \ 161 do { \ 162 (void)fputc((c), (s)); \ 163 (*nl) = ((c) == '\n'); \ 164 } while (0/*CONSTCOND*/) 165 166 int 167 main(int argc, char *argv[]) 168 { 169 struct stat st; 170 char dname[sizeof(_PATH_DEV) + SPECNAMELEN] = _PATH_DEV; 171 const char *statfmt, *options, *synopsis; 172 const char *file; 173 fhandle_t fhnd; 174 int ch, rc, errs, am_readlink, fn, fmtchar; 175 bool lsF, holes, usestat, nfs_handle, quiet; 176 177 am_readlink = 0; 178 errs = 0; 179 lsF = false; 180 fmtchar = '\0'; 181 holes = false; 182 usestat = false; 183 nfs_handle = false; 184 nonl = false; 185 quiet = false; 186 linkfail = 0; 187 statfmt = NULL; 188 timefmt = NULL; 189 190 if (strcmp(getprogname(), "mountpoint") == 0) 191 return (mountpoint_main(argc, argv)); 192 193 if (strcmp(getprogname(), "readlink") == 0) { 194 am_readlink = 1; 195 options = "fn"; 196 synopsis = "[-fn] [file ...]"; 197 statfmt = "%Y"; 198 fmtchar = 'f'; 199 quiet = 1; 200 } else { 201 options = "Ff:HhLlnqrst:x"; 202 synopsis = "[-FHhLnq] [-f format | -l | -r | -s | -x] " 203 "[-t timefmt] [file|handle ...]"; 204 } 205 206 while ((ch = getopt(argc, argv, options)) != -1) 207 switch (ch) { 208 case 'F': 209 lsF = true; 210 break; 211 case 'H': 212 nfs_handle = true; 213 break; 214 case 'h': 215 holes = true; 216 break; 217 case 'L': 218 usestat = true; 219 break; 220 case 'n': 221 nonl = true; 222 break; 223 case 't': 224 timefmt = optarg; 225 break; 226 case 'q': 227 quiet = true; 228 break; 229 /* remaining cases are purposefully out of order */ 230 case 'f': 231 if (am_readlink) { 232 statfmt = "%R"; 233 break; 234 } 235 statfmt = optarg; 236 /* FALLTHROUGH */ 237 case 'l': 238 case 'r': 239 case 's': 240 case 'x': 241 if (fmtchar != 0) 242 errx(1, "can't use format '%c' with '%c'", 243 fmtchar, ch); 244 fmtchar = ch; 245 break; 246 default: 247 usage(synopsis); 248 } 249 250 argc -= optind; 251 argv += optind; 252 fn = 1; 253 254 if (holes) { 255 if (fmtchar || lsF || nfs_handle || usestat || timefmt) 256 usage(synopsis); 257 if (argc > 0) { 258 while (argc-- > 0) { 259 if (listholes(*argv) != 0) { 260 if (!quiet) 261 warn("%s", *argv); 262 errs++; 263 } 264 argv++; 265 } 266 } else { 267 if (fdlistholes(STDIN_FILENO, "stdin") != 0) { 268 if (!quiet) 269 warn("stdin"); 270 errs++; 271 } 272 } 273 exit(errs ? 1 : 0); 274 } 275 276 if (fmtchar == '\0') { 277 if (lsF) 278 fmtchar = 'l'; 279 else { 280 fmtchar = 'f'; 281 statfmt = DEF_FORMAT; 282 } 283 } 284 285 if (lsF && fmtchar != 'l') 286 errx(1, "can't use format '%c' with -F", fmtchar); 287 288 switch (fmtchar) { 289 case 'f': 290 /* statfmt already set */ 291 break; 292 case 'l': 293 statfmt = lsF ? LSF_FORMAT : LS_FORMAT; 294 break; 295 case 'r': 296 statfmt = RAW_FORMAT; 297 break; 298 case 's': 299 statfmt = SHELL_FORMAT; 300 break; 301 case 'x': 302 statfmt = LINUX_FORMAT; 303 if (timefmt == NULL) 304 timefmt = "%c"; 305 break; 306 default: 307 usage(synopsis); 308 /*NOTREACHED*/ 309 } 310 311 if (timefmt == NULL) 312 timefmt = TIME_FORMAT; 313 314 do { 315 if (argc == 0) { 316 if (fdevname_r(STDIN_FILENO, dname + 317 sizeof _PATH_DEV - 1, SPECNAMELEN) != NULL) 318 file = dname; 319 else 320 file = "(stdin)"; 321 rc = fstat(STDIN_FILENO, &st); 322 } else { 323 int j; 324 325 file = argv[0]; 326 if (nfs_handle) { 327 rc = 0; 328 bzero(&fhnd, sizeof(fhnd)); 329 j = MIN(2 * sizeof(fhnd), strlen(file)); 330 if ((j & 1) != 0) { 331 rc = -1; 332 } else { 333 while (j) { 334 rc = hex2byte(&file[j - 2]); 335 if (rc == -1) 336 break; 337 ((char*) &fhnd)[j / 2 - 1] = rc; 338 j -= 2; 339 } 340 } 341 if (rc == -1) 342 errno = EINVAL; 343 else 344 rc = fhstat(&fhnd, &st); 345 346 } else if (usestat) { 347 /* 348 * Try stat() and if it fails, fall back to 349 * lstat() just in case we're examining a 350 * broken symlink. 351 */ 352 if ((rc = stat(file, &st)) == -1 && 353 errno == ENOENT && 354 (rc = lstat(file, &st)) == -1) 355 errno = ENOENT; 356 } else 357 rc = lstat(file, &st); 358 } 359 360 if (rc == -1) { 361 errs = 1; 362 linkfail = 1; 363 if (!quiet) 364 warn("%s", file); 365 } else 366 output(&st, file, statfmt, fn); 367 368 argv++; 369 argc--; 370 fn++; 371 } while (argc > 0); 372 373 return (am_readlink ? linkfail : errs); 374 } 375 376 /* 377 * fflagstostr() wrapper that leaks only once 378 */ 379 static char * 380 xfflagstostr(unsigned long fflags) 381 { 382 static char *str = NULL; 383 384 if (str != NULL) 385 free(str); 386 387 str = fflagstostr(fflags); 388 if (str == NULL) 389 err(1, "fflagstostr"); 390 return (str); 391 } 392 393 static void 394 usage(const char *synopsis) 395 { 396 (void)fprintf(stderr, "usage: %s %s\n", getprogname(), synopsis); 397 exit(1); 398 } 399 400 /* 401 * When invoked as mountpoint, report whether the file is the root of a 402 * mounted file system. Exit with 0 if it is, 1 if it is not and 403 * 2 on error. 404 */ 405 static int 406 mountpoint_main(int argc, char *argv[]) 407 { 408 struct stat st; 409 int ch, mounted, quiet; 410 411 quiet = 0; 412 while ((ch = getopt(argc, argv, "q")) != -1) 413 switch (ch) { 414 case 'q': 415 quiet = 1; 416 break; 417 default: 418 mountpoint_usage(); 419 } 420 argc -= optind; 421 argv += optind; 422 if (argc != 1) 423 mountpoint_usage(); 424 425 if (stat(argv[0], &st) == -1) 426 err(2, "%s", argv[0]); 427 428 mounted = (st.st_bsdflags & SFBSD_MNTPOINT) != 0; 429 if (!quiet) 430 printf("%s is %sa mount point\n", argv[0], 431 mounted ? "" : "not "); 432 return (mounted ? 0 : 1); 433 } 434 435 static void 436 mountpoint_usage(void) 437 { 438 439 (void)fprintf(stderr, "usage: %s [-q] file\n", getprogname()); 440 exit(2); 441 } 442 443 /* 444 * Parses a format string. 445 */ 446 static void 447 output(const struct stat *st, const char *file, const char *statfmt, int fn) 448 { 449 int flags, size, prec, ofmt, hilo, what; 450 char buf[PATH_MAX + 4 + 1]; 451 const char *subfmt; 452 int nl, t, i; 453 454 nl = 1; 455 while (*statfmt != '\0') { 456 457 /* 458 * Non-format characters go straight out. 459 */ 460 if (*statfmt != FMT_MAGIC) { 461 addchar(stdout, *statfmt, &nl); 462 statfmt++; 463 continue; 464 } 465 466 /* 467 * The current format "substring" starts here, 468 * and then we skip the magic. 469 */ 470 subfmt = statfmt; 471 statfmt++; 472 473 /* 474 * Some simple one-character "formats". 475 */ 476 switch (*statfmt) { 477 case SIMPLE_NEWLINE: 478 addchar(stdout, '\n', &nl); 479 statfmt++; 480 continue; 481 case SIMPLE_TAB: 482 addchar(stdout, '\t', &nl); 483 statfmt++; 484 continue; 485 case SIMPLE_PERCENT: 486 addchar(stdout, '%', &nl); 487 statfmt++; 488 continue; 489 case SIMPLE_NUMBER: { 490 char num[12], *p; 491 492 snprintf(num, sizeof(num), "%d", fn); 493 for (p = &num[0]; *p; p++) 494 addchar(stdout, *p, &nl); 495 statfmt++; 496 continue; 497 } 498 } 499 500 /* 501 * This must be an actual format string. Format strings are 502 * similar to printf(3) formats up to a point, and are of 503 * the form: 504 * 505 * % required start of format 506 * [-# +0] opt. format characters 507 * size opt. field width 508 * . opt. decimal separator, followed by 509 * prec opt. precision 510 * fmt opt. output specifier (string, numeric, etc.) 511 * sub opt. sub field specifier (high, middle, low) 512 * datum required field specifier (size, mode, etc) 513 * 514 * Only the % and the datum selector are required. All data 515 * have reasonable default output forms. The "sub" specifier 516 * only applies to certain data (mode, dev, rdev, filetype). 517 * The symlink output defaults to STRING, yet will only emit 518 * the leading " -> " if STRING is explicitly specified. The 519 * sizerdev datum will generate rdev output for character or 520 * block devices, and size output for all others. 521 */ 522 flags = 0; 523 do { 524 if (*statfmt == FMT_POUND) 525 flags |= FLAG_POUND; 526 else if (*statfmt == FMT_SPACE) 527 flags |= FLAG_SPACE; 528 else if (*statfmt == FMT_PLUS) 529 flags |= FLAG_PLUS; 530 else if (*statfmt == FMT_ZERO) 531 flags |= FLAG_ZERO; 532 else if (*statfmt == FMT_MINUS) 533 flags |= FLAG_MINUS; 534 else 535 break; 536 statfmt++; 537 } while (1/*CONSTCOND*/); 538 539 size = -1; 540 if (isdigit((unsigned)*statfmt)) { 541 size = 0; 542 while (isdigit((unsigned)*statfmt)) { 543 size = (size * 10) + (*statfmt - '0'); 544 statfmt++; 545 if (size < 0) 546 goto badfmt; 547 } 548 } 549 550 prec = -1; 551 if (*statfmt == FMT_DOT) { 552 statfmt++; 553 554 prec = 0; 555 while (isdigit((unsigned)*statfmt)) { 556 prec = (prec * 10) + (*statfmt - '0'); 557 statfmt++; 558 if (prec < 0) 559 goto badfmt; 560 } 561 } 562 563 #define fmtcase(x, y) case (y): (x) = (y); statfmt++; break 564 #define fmtcasef(x, y, z) case (y): (x) = (z); statfmt++; break 565 switch (*statfmt) { 566 fmtcasef(ofmt, FMT_DECIMAL, FMTF_DECIMAL); 567 fmtcasef(ofmt, FMT_OCTAL, FMTF_OCTAL); 568 fmtcasef(ofmt, FMT_UNSIGNED, FMTF_UNSIGNED); 569 fmtcasef(ofmt, FMT_HEX, FMTF_HEX); 570 fmtcasef(ofmt, FMT_FLOAT, FMTF_FLOAT); 571 fmtcasef(ofmt, FMT_STRING, FMTF_STRING); 572 default: 573 ofmt = 0; 574 break; 575 } 576 577 switch (*statfmt) { 578 fmtcase(hilo, HIGH_PIECE); 579 fmtcase(hilo, MIDDLE_PIECE); 580 fmtcase(hilo, LOW_PIECE); 581 default: 582 hilo = 0; 583 break; 584 } 585 586 switch (*statfmt) { 587 fmtcase(what, SHOW_realpath); 588 fmtcase(what, SHOW_st_dev); 589 fmtcase(what, SHOW_st_ino); 590 fmtcase(what, SHOW_st_mode); 591 fmtcase(what, SHOW_st_nlink); 592 fmtcase(what, SHOW_st_uid); 593 fmtcase(what, SHOW_st_gid); 594 fmtcase(what, SHOW_st_rdev); 595 fmtcase(what, SHOW_st_atime); 596 fmtcase(what, SHOW_st_mtime); 597 fmtcase(what, SHOW_st_ctime); 598 fmtcase(what, SHOW_st_btime); 599 fmtcase(what, SHOW_st_size); 600 fmtcase(what, SHOW_st_blocks); 601 fmtcase(what, SHOW_st_blksize); 602 fmtcase(what, SHOW_st_flags); 603 fmtcase(what, SHOW_st_gen); 604 fmtcase(what, SHOW_symlink); 605 fmtcase(what, SHOW_filetype); 606 fmtcase(what, SHOW_filename); 607 fmtcase(what, SHOW_sizerdev); 608 default: 609 goto badfmt; 610 } 611 #undef fmtcasef 612 #undef fmtcase 613 614 t = format1(st, 615 file, 616 subfmt, statfmt - subfmt, 617 buf, sizeof(buf), 618 flags, size, prec, ofmt, hilo, what); 619 620 for (i = 0; i < t && i < (int)(sizeof(buf) - 1); i++) 621 addchar(stdout, buf[i], &nl); 622 623 continue; 624 625 badfmt: 626 errx(1, "%.*s: bad format", 627 (int)(statfmt - subfmt + 1), subfmt); 628 } 629 630 if (!nl && !nonl) 631 (void)fputc('\n', stdout); 632 (void)fflush(stdout); 633 } 634 635 /* 636 * Arranges output according to a single parsed format substring. 637 */ 638 static int 639 format1(const struct stat *st, 640 const char *file, 641 const char *fmt, int flen, 642 char *buf, size_t blen, 643 int flags, int size, int prec, int ofmt, 644 int hilo, int what) 645 { 646 u_int64_t data; 647 char *stmp, lfmt[24], tmp[20]; 648 const char *sdata; 649 char smode[12], sid[12], path[PATH_MAX + 4]; 650 const struct timespec *tsp; 651 struct timespec ts; 652 struct tm *tm; 653 int l, small, formats; 654 mode_t dtype; 655 656 tsp = NULL; 657 formats = 0; 658 small = 0; 659 660 /* 661 * First, pick out the data and tweak it based on hilo or 662 * specified output format (symlink output only). 663 */ 664 switch (what) { 665 case SHOW_st_dev: 666 case SHOW_st_rdev: 667 small = (sizeof(st->st_dev) == 4); 668 switch (what) { 669 case SHOW_st_dev: 670 data = st->st_dev; 671 dtype = S_IFCHR; 672 break; 673 case SHOW_st_rdev: 674 data = st->st_rdev; 675 dtype = st->st_mode & (S_IFCHR | S_IFBLK); 676 break; 677 } 678 sdata = devname(data, dtype); 679 if (hilo == HIGH_PIECE) { 680 data = major(data); 681 hilo = 0; 682 } 683 else if (hilo == LOW_PIECE) { 684 data = minor((unsigned)data); 685 hilo = 0; 686 } 687 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX | 688 FMTF_STRING; 689 if (ofmt == 0) 690 ofmt = FMTF_UNSIGNED; 691 break; 692 case SHOW_st_ino: 693 small = (sizeof(st->st_ino) == 4); 694 data = st->st_ino; 695 sdata = NULL; 696 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; 697 if (ofmt == 0) 698 ofmt = FMTF_UNSIGNED; 699 break; 700 case SHOW_st_mode: 701 small = (sizeof(st->st_mode) == 4); 702 data = st->st_mode; 703 strmode(st->st_mode, smode); 704 stmp = smode; 705 l = strlen(stmp); 706 if (stmp[l - 1] == ' ') 707 stmp[--l] = '\0'; 708 if (hilo == HIGH_PIECE) { 709 data >>= 12; 710 stmp += 1; 711 stmp[3] = '\0'; 712 hilo = 0; 713 } 714 else if (hilo == MIDDLE_PIECE) { 715 data = (data >> 9) & 07; 716 stmp += 4; 717 stmp[3] = '\0'; 718 hilo = 0; 719 } 720 else if (hilo == LOW_PIECE) { 721 data &= 0777; 722 stmp += 7; 723 stmp[3] = '\0'; 724 hilo = 0; 725 } 726 sdata = stmp; 727 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX | 728 FMTF_STRING; 729 if (ofmt == 0) 730 ofmt = FMTF_OCTAL; 731 break; 732 case SHOW_st_nlink: 733 small = (sizeof(st->st_dev) == 4); 734 data = st->st_nlink; 735 sdata = NULL; 736 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; 737 if (ofmt == 0) 738 ofmt = FMTF_UNSIGNED; 739 break; 740 case SHOW_st_uid: 741 small = (sizeof(st->st_uid) == 4); 742 data = st->st_uid; 743 sdata = user_from_uid(st->st_uid, 1); 744 if (sdata == NULL) { 745 snprintf(sid, sizeof(sid), "(%ld)", (long)st->st_uid); 746 sdata = sid; 747 } 748 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX | 749 FMTF_STRING; 750 if (ofmt == 0) 751 ofmt = FMTF_UNSIGNED; 752 break; 753 case SHOW_st_gid: 754 small = (sizeof(st->st_gid) == 4); 755 data = st->st_gid; 756 sdata = group_from_gid(st->st_gid, 1); 757 if (sdata == NULL) { 758 snprintf(sid, sizeof(sid), "(%ld)", (long)st->st_gid); 759 sdata = sid; 760 } 761 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX | 762 FMTF_STRING; 763 if (ofmt == 0) 764 ofmt = FMTF_UNSIGNED; 765 break; 766 case SHOW_st_atime: 767 tsp = &st->st_atimespec; 768 /* FALLTHROUGH */ 769 case SHOW_st_mtime: 770 if (tsp == NULL) 771 tsp = &st->st_mtimespec; 772 /* FALLTHROUGH */ 773 case SHOW_st_ctime: 774 if (tsp == NULL) 775 tsp = &st->st_ctimespec; 776 /* FALLTHROUGH */ 777 case SHOW_st_btime: 778 if (tsp == NULL) 779 tsp = &st->st_birthtimespec; 780 ts = *tsp; /* copy so we can muck with it */ 781 small = (sizeof(ts.tv_sec) == 4); 782 data = ts.tv_sec; 783 tm = localtime(&ts.tv_sec); 784 if (tm == NULL) { 785 ts.tv_sec = 0; 786 tm = localtime(&ts.tv_sec); 787 } 788 (void)setlocale(LC_TIME, ""); 789 (void)strftime(path, sizeof(path), timefmt, tm); 790 sdata = path; 791 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX | 792 FMTF_FLOAT | FMTF_STRING; 793 if (ofmt == 0) 794 ofmt = FMTF_DECIMAL; 795 break; 796 case SHOW_st_size: 797 small = (sizeof(st->st_size) == 4); 798 data = st->st_size; 799 sdata = NULL; 800 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; 801 if (ofmt == 0) 802 ofmt = FMTF_UNSIGNED; 803 break; 804 case SHOW_st_blocks: 805 small = (sizeof(st->st_blocks) == 4); 806 data = st->st_blocks; 807 sdata = NULL; 808 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; 809 if (ofmt == 0) 810 ofmt = FMTF_UNSIGNED; 811 break; 812 case SHOW_st_blksize: 813 small = (sizeof(st->st_blksize) == 4); 814 data = st->st_blksize; 815 sdata = NULL; 816 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; 817 if (ofmt == 0) 818 ofmt = FMTF_UNSIGNED; 819 break; 820 case SHOW_st_flags: 821 small = (sizeof(st->st_flags) == 4); 822 data = st->st_flags; 823 sdata = xfflagstostr(st->st_flags); 824 if (*sdata == '\0') 825 sdata = "-"; 826 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX | 827 FMTF_STRING; 828 if (ofmt == 0) 829 ofmt = FMTF_UNSIGNED; 830 break; 831 case SHOW_st_gen: 832 small = (sizeof(st->st_gen) == 4); 833 data = st->st_gen; 834 sdata = NULL; 835 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; 836 if (ofmt == 0) 837 ofmt = FMTF_UNSIGNED; 838 break; 839 case SHOW_realpath: 840 small = 0; 841 data = 0; 842 if (file == NULL) { 843 (void)strlcpy(path, "(stdin)", sizeof(path)); 844 sdata = path; 845 } else { 846 snprintf(path, sizeof(path), " -> "); 847 if (realpath(file, path + 4) == NULL) { 848 linkfail = 1; 849 l = 0; 850 path[0] = '\0'; 851 } 852 sdata = path + (ofmt == FMTF_STRING ? 0 : 4); 853 } 854 855 formats = FMTF_STRING; 856 if (ofmt == 0) 857 ofmt = FMTF_STRING; 858 break; 859 case SHOW_symlink: 860 small = 0; 861 data = 0; 862 if (S_ISLNK(st->st_mode)) { 863 snprintf(path, sizeof(path), " -> "); 864 l = readlink(file, path + 4, sizeof(path) - 4 - 1); 865 if (l == -1) { 866 linkfail = 1; 867 l = 0; 868 path[0] = '\0'; 869 } 870 path[l + 4] = '\0'; 871 sdata = path + (ofmt == FMTF_STRING ? 0 : 4); 872 } 873 else { 874 linkfail = 1; 875 sdata = ""; 876 } 877 formats = FMTF_STRING; 878 if (ofmt == 0) 879 ofmt = FMTF_STRING; 880 break; 881 case SHOW_filetype: 882 small = 0; 883 data = 0; 884 sdata = ""; 885 if (hilo == 0 || hilo == LOW_PIECE) { 886 switch (st->st_mode & S_IFMT) { 887 case S_IFIFO: sdata = "|"; break; 888 case S_IFDIR: sdata = "/"; break; 889 case S_IFREG: 890 if (st->st_mode & 891 (S_IXUSR | S_IXGRP | S_IXOTH)) 892 sdata = "*"; 893 break; 894 case S_IFLNK: sdata = "@"; break; 895 case S_IFSOCK: sdata = "="; break; 896 #ifdef S_IFWHT 897 case S_IFWHT: sdata = "%"; break; 898 #endif /* S_IFWHT */ 899 #ifdef S_IFDOOR 900 case S_IFDOOR: sdata = ">"; break; 901 #endif /* S_IFDOOR */ 902 } 903 hilo = 0; 904 } 905 else if (hilo == HIGH_PIECE) { 906 switch (st->st_mode & S_IFMT) { 907 case S_IFIFO: sdata = "Fifo File"; break; 908 case S_IFCHR: sdata = "Character Device"; break; 909 case S_IFDIR: sdata = "Directory"; break; 910 case S_IFBLK: sdata = "Block Device"; break; 911 case S_IFREG: sdata = "Regular File"; break; 912 case S_IFLNK: sdata = "Symbolic Link"; break; 913 case S_IFSOCK: sdata = "Socket"; break; 914 #ifdef S_IFWHT 915 case S_IFWHT: sdata = "Whiteout File"; break; 916 #endif /* S_IFWHT */ 917 #ifdef S_IFDOOR 918 case S_IFDOOR: sdata = "Door"; break; 919 #endif /* S_IFDOOR */ 920 default: sdata = "???"; break; 921 } 922 hilo = 0; 923 } 924 formats = FMTF_STRING; 925 if (ofmt == 0) 926 ofmt = FMTF_STRING; 927 break; 928 case SHOW_filename: 929 small = 0; 930 data = 0; 931 (void)strlcpy(path, file, sizeof(path)); 932 sdata = path; 933 formats = FMTF_STRING; 934 if (ofmt == 0) 935 ofmt = FMTF_STRING; 936 break; 937 case SHOW_sizerdev: 938 if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) { 939 char majdev[20], mindev[20]; 940 int l1, l2; 941 942 l1 = format1(st, 943 file, 944 fmt, flen, 945 majdev, sizeof(majdev), 946 flags, size, prec, 947 ofmt, HIGH_PIECE, SHOW_st_rdev); 948 l2 = format1(st, 949 file, 950 fmt, flen, 951 mindev, sizeof(mindev), 952 flags, size, prec, 953 ofmt, LOW_PIECE, SHOW_st_rdev); 954 return (snprintf(buf, blen, "%.*s,%.*s", 955 l1, majdev, l2, mindev)); 956 } 957 else { 958 return (format1(st, 959 file, 960 fmt, flen, 961 buf, blen, 962 flags, size, prec, 963 ofmt, 0, SHOW_st_size)); 964 } 965 /*NOTREACHED*/ 966 default: 967 errx(1, "%.*s: bad format", (int)flen, fmt); 968 } 969 970 /* 971 * If a subdatum was specified but not supported, or an output 972 * format was selected that is not supported, that's an error. 973 */ 974 if (hilo != 0 || (ofmt & formats) == 0) 975 errx(1, "%.*s: bad format", (int)flen, fmt); 976 977 /* 978 * Assemble the format string for passing to printf(3). 979 */ 980 lfmt[0] = '\0'; 981 (void)strcat(lfmt, "%"); 982 if (flags & FLAG_POUND) 983 (void)strcat(lfmt, "#"); 984 if (flags & FLAG_SPACE) 985 (void)strcat(lfmt, " "); 986 if (flags & FLAG_PLUS) 987 (void)strcat(lfmt, "+"); 988 if (flags & FLAG_MINUS) 989 (void)strcat(lfmt, "-"); 990 if (flags & FLAG_ZERO) 991 (void)strcat(lfmt, "0"); 992 993 /* 994 * Only the timespecs support the FLOAT output format, and that 995 * requires work that differs from the other formats. 996 */ 997 if (ofmt == FMTF_FLOAT) { 998 /* 999 * Nothing after the decimal point, so just print seconds. 1000 */ 1001 if (prec == 0) { 1002 if (size != -1) { 1003 (void)snprintf(tmp, sizeof(tmp), "%d", size); 1004 (void)strcat(lfmt, tmp); 1005 } 1006 (void)strcat(lfmt, "lld"); 1007 return (snprintf(buf, blen, lfmt, 1008 (long long)ts.tv_sec)); 1009 } 1010 1011 /* 1012 * Unspecified precision gets all the precision we have: 1013 * 9 digits. 1014 */ 1015 if (prec == -1) 1016 prec = 9; 1017 1018 /* 1019 * Adjust the size for the decimal point and the digits 1020 * that will follow. 1021 */ 1022 size -= prec + 1; 1023 1024 /* 1025 * Any leftover size that's legitimate will be used. 1026 */ 1027 if (size > 0) { 1028 (void)snprintf(tmp, sizeof(tmp), "%d", size); 1029 (void)strcat(lfmt, tmp); 1030 } 1031 /* Seconds: time_t cast to long long. */ 1032 (void)strcat(lfmt, "lld"); 1033 1034 /* 1035 * The stuff after the decimal point always needs zero 1036 * filling. 1037 */ 1038 (void)strcat(lfmt, ".%0"); 1039 1040 /* 1041 * We can "print" at most nine digits of precision. The 1042 * rest we will pad on at the end. 1043 * 1044 * Nanoseconds: long. 1045 */ 1046 (void)snprintf(tmp, sizeof(tmp), "%dld", MIN(prec, 9)); 1047 (void)strcat(lfmt, tmp); 1048 1049 /* 1050 * For precision of less than nine digits, trim off the 1051 * less significant figures. 1052 */ 1053 for (; prec < 9; prec++) 1054 ts.tv_nsec /= 10; 1055 1056 /* 1057 * Use the format, and then tack on any zeroes that 1058 * might be required to make up the requested precision. 1059 */ 1060 l = snprintf(buf, blen, lfmt, (long long)ts.tv_sec, ts.tv_nsec); 1061 for (; prec > 9 && l < (int)blen; prec--, l++) 1062 (void)strcat(buf, "0"); 1063 return (l); 1064 } 1065 1066 /* 1067 * Add on size and precision, if specified, to the format. 1068 */ 1069 if (size != -1) { 1070 (void)snprintf(tmp, sizeof(tmp), "%d", size); 1071 (void)strcat(lfmt, tmp); 1072 } 1073 if (prec != -1) { 1074 (void)snprintf(tmp, sizeof(tmp), ".%d", prec); 1075 (void)strcat(lfmt, tmp); 1076 } 1077 1078 /* 1079 * String output uses the temporary sdata. 1080 */ 1081 if (ofmt == FMTF_STRING) { 1082 if (sdata == NULL) 1083 errx(1, "%.*s: bad format", (int)flen, fmt); 1084 (void)strcat(lfmt, "s"); 1085 return (snprintf(buf, blen, lfmt, sdata)); 1086 } 1087 1088 /* 1089 * Ensure that sign extension does not cause bad looking output 1090 * for some forms. 1091 */ 1092 if (small && ofmt != FMTF_DECIMAL) 1093 data = (u_int32_t)data; 1094 1095 /* 1096 * The four "numeric" output forms. 1097 */ 1098 (void)strcat(lfmt, "ll"); 1099 switch (ofmt) { 1100 case FMTF_DECIMAL: (void)strcat(lfmt, "d"); break; 1101 case FMTF_OCTAL: (void)strcat(lfmt, "o"); break; 1102 case FMTF_UNSIGNED: (void)strcat(lfmt, "u"); break; 1103 case FMTF_HEX: (void)strcat(lfmt, "x"); break; 1104 } 1105 1106 return (snprintf(buf, blen, lfmt, data)); 1107 } 1108 1109 1110 #define hex2nibble(c) (c <= '9' ? c - '0' : toupper(c) - 'A' + 10) 1111 static int 1112 hex2byte(const char c[2]) { 1113 if (!(ishexnumber(c[0]) && ishexnumber(c[1]))) 1114 return -1; 1115 return (hex2nibble(c[0]) << 4) + hex2nibble(c[1]); 1116 } 1117 1118 static int 1119 fdlistholes(int fd, const char *fn) 1120 { 1121 struct stat sb; 1122 off_t pos = 0, off; 1123 long l; 1124 1125 if (fstat(fd, &sb) < 0) 1126 return (-1); 1127 if (S_ISDIR(sb.st_mode)) { 1128 if ((l = fpathconf(fd, _PC_MIN_HOLE_SIZE)) < 0) 1129 return (-1); 1130 printf("%ld", l); 1131 } else if (!S_ISREG(sb.st_mode)) { 1132 errno = ESPIPE; 1133 return (-1); 1134 } else { 1135 for (;;) { 1136 if ((off = lseek(fd, pos, SEEK_HOLE)) < 0) { 1137 if (errno != ENXIO) 1138 return (-1); 1139 /* 1140 * This can only happen if the file was 1141 * truncated while we were scanning it, or 1142 * on the initial seek if the file is 1143 * empty. Report the virtual hole at the 1144 * end of the file at this position. 1145 */ 1146 off = pos; 1147 } 1148 printf("%jd", (intmax_t)off); 1149 pos = off; 1150 if ((off = lseek(fd, pos, SEEK_DATA)) < 0) { 1151 if (errno != ENXIO) 1152 return (-1); 1153 /* 1154 * There are no more data regions in the 1155 * file, or it got truncated. However, we 1156 * may not be at the end yet. 1157 */ 1158 if ((off = lseek(fd, 0, SEEK_END)) > pos) 1159 printf("-%jd", (intmax_t)off - 1); 1160 break; 1161 } 1162 printf("-%jd,", (intmax_t)off - 1); 1163 pos = off; 1164 } 1165 } 1166 printf(" %s", fn); 1167 if (!nonl) 1168 printf("\n"); 1169 return (0); 1170 } 1171 1172 static int 1173 listholes(const char *fn) 1174 { 1175 int fd, ret; 1176 1177 if ((fd = open(fn, O_RDONLY)) < 0) 1178 return (-1); 1179 ret = fdlistholes(fd, fn); 1180 close(fd); 1181 return (ret); 1182 } 1183