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 (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 27 /* All Rights Reserved */ 28 29 /* Copyright (c) 1987, 1988 Microsoft Corporation */ 30 /* All Rights Reserved */ 31 32 #pragma ident "%Z%%M% %I% %E% SMI" 33 34 /* 35 * List files or directories 36 */ 37 38 #include <sys/param.h> 39 #include <sys/types.h> 40 #include <sys/mkdev.h> 41 #include <sys/stat.h> 42 #include <sys/acl.h> 43 44 #include <wchar.h> 45 #include <stdio.h> 46 #include <ctype.h> 47 #include <dirent.h> 48 #include <string.h> 49 #include <locale.h> 50 #include <curses.h> 51 #include <termios.h> 52 #include <stdlib.h> 53 #include <widec.h> 54 #include <locale.h> 55 #include <wctype.h> 56 #include <pwd.h> 57 #include <grp.h> 58 #include <limits.h> 59 #include <fcntl.h> 60 #include <unistd.h> 61 #include <libgen.h> 62 #include <errno.h> 63 #include <aclutils.h> 64 #include <libnvpair.h> 65 #include <libcmdutils.h> 66 #include <attr.h> 67 68 #ifndef STANDALONE 69 #define TERMINFO 70 #endif 71 72 /* 73 * -DNOTERMINFO can be defined on the cc command line to prevent 74 * the use of terminfo. This should be done on systems not having 75 * the terminfo feature(pre 6.0 systems ?). 76 * As a result, columnar listings assume 80 columns for output, 77 * unless told otherwise via the COLUMNS environment variable. 78 */ 79 #ifdef NOTERMINFO 80 #undef TERMINFO 81 #endif 82 83 #include <term.h> 84 85 #define BFSIZE 16 86 /* this bit equals 1 in lflags of structure lbuf if *namep is to be used */ 87 #define ISARG 0100000 88 89 /* 90 * this flag has been added to manipulate the display of S instead of 'l' when 91 * the file is not a regular file and when group execution bit is off 92 */ 93 #define LS_NOTREG 010000 94 95 96 /* 97 * Date and time formats 98 * 99 * b --- abbreviated month name 100 * e --- day number 101 * Y --- year in the form ccyy 102 * H --- hour(24-hour version) 103 * M --- minute 104 * F --- yyyy-mm-dd 105 * T --- hh:mm:ss 106 * z --- time zone as hours displacement from UTC 107 * note that %F and %z are from the ISO C99 standard and are 108 * not present in older C libraries 109 */ 110 #define FORMAT1 " %b %e %Y " 111 #define FORMAT2 " %b %e %H:%M " 112 #define FORMAT3 " %b %e %T %Y " 113 #define FORMAT4 " %%F %%T.%.09ld %%z " 114 115 #undef BUFSIZ 116 #define BUFSIZ 4096 117 #define NUMBER_WIDTH 40 118 #define FMTSIZE 50 119 120 struct ditem { 121 dev_t dev; /* directory items device number */ 122 ino_t ino; /* directory items inode number */ 123 struct ditem *parent; /* dir items ptr to its parent's info */ 124 }; 125 /* Holds boolean extended system attributes */ 126 struct attrb { 127 char *name; 128 }; 129 /* Holds timestamp extended system attributes */ 130 struct attrtm { 131 char *name; 132 uint64_t stm; 133 uint64_t nstm; 134 }; 135 136 struct lbuf { 137 union { 138 char lname[MAXNAMLEN]; /* used for filename in a directory */ 139 char *namep; /* for name in ls-command; */ 140 } ln; 141 char ltype; /* filetype */ 142 ino_t lnum; /* inode number of file */ 143 mode_t lflags; /* 0777 bits used as r,w,x permissions */ 144 nlink_t lnl; /* number of links to file */ 145 uid_t luid; 146 gid_t lgid; 147 off_t lsize; /* filesize or major/minor dev numbers */ 148 blkcnt_t lblocks; /* number of file blocks */ 149 timestruc_t lmtime; 150 timestruc_t lat; 151 timestruc_t lct; 152 timestruc_t lmt; 153 char *flinkto; /* symbolic link contents */ 154 char acl; /* indicate there are additional acl entries */ 155 int cycle; /* cycle detected flag */ 156 struct ditem *ancinfo; /* maintains ancestor info */ 157 acl_t *aclp; /* ACL if present */ 158 struct attrb *exttr; /* boolean extended system attributes */ 159 struct attrtm *extm; /* timestamp extended system attributes */ 160 }; 161 162 struct dchain { 163 char *dc_name; /* path name */ 164 int cycle_detected; /* cycle detected visiting this directory */ 165 struct ditem *myancinfo; /* this directory's ancestry info */ 166 struct dchain *dc_next; /* next directory in the chain */ 167 }; 168 169 /* 170 * A numbuf_t is used when converting a number to a string representation 171 */ 172 typedef char numbuf_t[NUMBER_WIDTH]; 173 174 static struct dchain *dfirst; /* start of the dir chain */ 175 static struct dchain *cdfirst; /* start of the current dir chain */ 176 static struct dchain *dtemp; /* temporary - used for linking */ 177 static char *curdir; /* the current directory */ 178 179 static int first = 1; /* true if first line is not yet printed */ 180 static int nfiles = 0; /* number of flist entries in current use */ 181 static int nargs = 0; /* number of flist entries used for arguments */ 182 static int maxfils = 0; /* number of flist/lbuf entries allocated */ 183 static int maxn = 0; /* number of flist entries with lbufs asigned */ 184 static int quantn = 64; /* allocation growth quantum */ 185 186 static struct lbuf *nxtlbf; /* ptr to next lbuf to be assigned */ 187 static struct lbuf **flist; /* ptr to list of lbuf pointers */ 188 static struct lbuf *gstat(char *, int, struct ditem *); 189 static char *getname(uid_t); 190 static char *getgroup(gid_t); 191 static char *makename(char *, char *); 192 static void pentry(struct lbuf *); 193 static void column(void); 194 static void pmode(mode_t aflag); 195 static void selection(int *); 196 static void new_line(void); 197 static void rddir(char *, struct ditem *); 198 static int strcol(unsigned char *); 199 static void pem(struct lbuf **, struct lbuf **, int); 200 static void pdirectory(char *, int, int, int, struct ditem *); 201 static struct cachenode *findincache(struct cachenode **, long); 202 static void csi_pprintf(unsigned char *); 203 static void pprintf(char *, char *); 204 static int compar(struct lbuf **pp1, struct lbuf **pp2); 205 static char *number_to_scaled_string(numbuf_t buf, 206 unsigned long long number, 207 long scale); 208 static void record_ancestry(char *, struct stat *, struct lbuf *, 209 int, struct ditem *); 210 211 static int aflg; 212 static int atflg; 213 static int bflg; 214 static int cflg; 215 static int dflg; 216 static int eflg; 217 static int fflg; 218 static int gflg; 219 static int hflg; 220 static int iflg; 221 static int lflg; 222 static int mflg; 223 static int nflg; 224 static int oflg; 225 static int pflg; 226 static int qflg; 227 static int rflg = 1; /* init to 1 for special use in compar */ 228 static int sflg; 229 static int tflg; 230 static int uflg; 231 static int xflg; 232 static int Aflg; 233 static int Cflg; 234 static int Eflg; 235 static int Fflg; 236 static int Hflg; 237 static int Lflg; 238 static int Rflg; 239 static int Sflg; 240 static int vflg; 241 static int Vflg; 242 static int saflg; /* boolean extended system attr. */ 243 static int sacnt; /* number of extended system attr. */ 244 static int copt; 245 static int vopt; 246 static int tmflg; /* create time ext. system attr. */ 247 static int ctm; 248 static int atm; 249 static int mtm; 250 static int crtm; 251 static int alltm; 252 static long hscale; 253 static mode_t flags; 254 static int err = 0; /* Contains return code */ 255 256 static uid_t lastuid = (uid_t)-1; 257 static gid_t lastgid = (gid_t)-1; 258 static char *lastuname = NULL; 259 static char *lastgname = NULL; 260 261 /* statreq > 0 if any of sflg, (n)lflg, tflg, Sflg are on */ 262 static int statreq; 263 264 static char *dotp = "."; 265 266 static u_longlong_t tblocks; /* number of blocks of files in a directory */ 267 static time_t year, now; 268 269 static int num_cols = 80; 270 static int colwidth; 271 static int filewidth; 272 static int fixedwidth; 273 static int nomocore; 274 static int curcol; 275 276 static struct winsize win; 277 278 static char time_buf[FMTSIZE]; /* array to hold day and time */ 279 280 #define NOTWORKINGDIR(d, l) (((l) < 2) || \ 281 (strcmp((d) + (l) - 2, "/.") != 0)) 282 283 #define NOTPARENTDIR(d, l) (((l) < 3) || \ 284 (strcmp((d) + (l) - 3, "/..") != 0)) 285 /* Extended system attributes support */ 286 static int get_sysxattr(char *, struct lbuf *); 287 static void set_sysattrb_display(char *, boolean_t, struct lbuf *); 288 static void set_sysattrtm_display(char *, struct lbuf *); 289 static void format_time(const char *, time_t); 290 static void format_etime(const char *, time_t, time_t); 291 static void print_time(struct lbuf *); 292 static void format_attrtime(struct lbuf *); 293 static void *xmalloc(size_t, struct lbuf *); 294 static void free_sysattr(struct lbuf *); 295 static nvpair_t *pair; 296 static nvlist_t *response; 297 static int acl_err; 298 299 int 300 main(int argc, char *argv[]) 301 { 302 int c; 303 int i; 304 int width; 305 int amino = 0; 306 int opterr = 0; 307 struct lbuf *ep; 308 struct lbuf lb; 309 struct ditem *myinfo; 310 311 (void) setlocale(LC_ALL, ""); 312 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 313 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ 314 #endif 315 (void) textdomain(TEXT_DOMAIN); 316 #ifdef STANDALONE 317 if (argv[0][0] == '\0') 318 argc = getargv("ls", &argv, 0); 319 #endif 320 321 lb.lmtime.tv_sec = time(NULL); 322 lb.lmtime.tv_nsec = 0; 323 year = lb.lmtime.tv_sec - 6L*30L*24L*60L*60L; /* 6 months ago */ 324 now = lb.lmtime.tv_sec + 60; 325 if (isatty(1)) { 326 Cflg = 1; 327 mflg = 0; 328 } 329 330 while ((c = getopt(argc, argv, 331 "aAbcCdeEfFghHilLmnopqrRsStux1@vV/:%:")) != EOF) 332 switch (c) { 333 case 'a': 334 aflg++; 335 continue; 336 case 'A': 337 Aflg++; 338 continue; 339 case 'b': 340 bflg = 1; 341 qflg = 0; 342 continue; 343 case 'c': 344 uflg = 0; 345 atm = 0; 346 ctm = 0; 347 mtm = 0; 348 crtm = 0; 349 cflg++; 350 continue; 351 case 'C': 352 Cflg = 1; 353 mflg = 0; 354 #ifdef XPG4 355 lflg = 0; 356 #endif 357 continue; 358 case 'd': 359 dflg++; 360 continue; 361 case 'e': 362 eflg++; 363 lflg++; 364 statreq++; 365 Eflg = 0; 366 continue; 367 case 'E': 368 Eflg++; 369 lflg++; 370 statreq++; 371 eflg = 0; 372 continue; 373 case 'f': 374 fflg++; 375 continue; 376 case 'F': 377 Fflg++; 378 statreq++; 379 continue; 380 case 'g': 381 gflg++; 382 lflg++; 383 statreq++; 384 continue; 385 case 'h': 386 hflg++; 387 hscale = 1024; 388 continue; 389 case 'H': 390 Hflg++; 391 /* -H and -L are mutually exclusive */ 392 Lflg = 0; 393 continue; 394 case 'i': 395 iflg++; 396 continue; 397 case 'l': 398 lflg++; 399 statreq++; 400 Cflg = 0; 401 xflg = 0; 402 mflg = 0; 403 atflg = 0; 404 continue; 405 case 'L': 406 Lflg++; 407 /* -H and -L are mutually exclusive */ 408 Hflg = 0; 409 continue; 410 case 'm': 411 Cflg = 0; 412 mflg = 1; 413 #ifdef XPG4 414 lflg = 0; 415 #endif 416 continue; 417 case 'n': 418 nflg++; 419 lflg++; 420 statreq++; 421 Cflg = 0; 422 xflg = 0; 423 mflg = 0; 424 atflg = 0; 425 continue; 426 case 'o': 427 oflg++; 428 lflg++; 429 statreq++; 430 continue; 431 case 'p': 432 pflg++; 433 statreq++; 434 continue; 435 case 'q': 436 qflg = 1; 437 bflg = 0; 438 continue; 439 case 'r': 440 rflg = -1; 441 continue; 442 case 'R': 443 Rflg++; 444 statreq++; 445 continue; 446 case 's': 447 sflg++; 448 statreq++; 449 continue; 450 case 'S': 451 tflg = 0; 452 Sflg++; 453 statreq++; 454 continue; 455 case 't': 456 Sflg = 0; 457 tflg++; 458 statreq++; 459 continue; 460 case 'u': 461 cflg = 0; 462 atm = 0; 463 ctm = 0; 464 mtm = 0; 465 crtm = 0; 466 uflg++; 467 continue; 468 case 'V': 469 Vflg++; 470 /*FALLTHROUGH*/ 471 case 'v': 472 vflg++; 473 #if !defined(XPG4) 474 if (lflg) 475 continue; 476 #endif 477 lflg++; 478 statreq++; 479 Cflg = 0; 480 xflg = 0; 481 mflg = 0; 482 continue; 483 case 'x': 484 xflg = 1; 485 Cflg = 1; 486 mflg = 0; 487 #ifdef XPG4 488 lflg = 0; 489 #endif 490 continue; 491 case '1': 492 Cflg = 0; 493 continue; 494 case '@': 495 #if !defined(XPG4) 496 /* 497 * -l has precedence over -@ 498 */ 499 if (lflg) 500 continue; 501 #endif 502 atflg++; 503 lflg++; 504 statreq++; 505 Cflg = 0; 506 xflg = 0; 507 mflg = 0; 508 continue; 509 case '/': 510 saflg++; 511 if (optarg != NULL) { 512 if (strcmp(optarg, "c") == 0) { 513 copt++; 514 vopt = 0; 515 } else if (strcmp(optarg, "v") == 0) { 516 vopt++; 517 copt = 0; 518 } else 519 opterr++; 520 } else 521 opterr++; 522 lflg++; 523 statreq++; 524 Cflg = 0; 525 xflg = 0; 526 mflg = 0; 527 continue; 528 case '%': 529 tmflg++; 530 if (optarg != NULL) { 531 if (strcmp(optarg, "ctime") == 0) { 532 ctm++; 533 atm = 0; 534 mtm = 0; 535 crtm = 0; 536 } else if (strcmp(optarg, "atime") == 0) { 537 atm++; 538 ctm = 0; 539 mtm = 0; 540 crtm = 0; 541 uflg = 0; 542 cflg = 0; 543 } else if (strcmp(optarg, "mtime") == 0) { 544 mtm++; 545 atm = 0; 546 ctm = 0; 547 crtm = 0; 548 uflg = 0; 549 cflg = 0; 550 } else if (strcmp(optarg, "crtime") == 0) { 551 crtm++; 552 atm = 0; 553 ctm = 0; 554 mtm = 0; 555 uflg = 0; 556 cflg = 0; 557 } else if (strcmp(optarg, "all") == 0) { 558 alltm++; 559 atm = 0; 560 ctm = 0; 561 mtm = 0; 562 crtm = 0; 563 } else 564 opterr++; 565 } else 566 opterr++; 567 568 Sflg = 0; 569 statreq++; 570 mflg = 0; 571 continue; 572 case '?': 573 opterr++; 574 continue; 575 } 576 if (opterr) { 577 (void) fprintf(stderr, gettext( 578 "usage: ls -aAbcCdeEfFghHilLmnopqrRsStuxvV1@/%[c | v]" 579 "%%[atime | crtime | ctime | mtime | all]" 580 " [files]\n")); 581 exit(2); 582 } 583 584 if (fflg) { 585 aflg++; 586 lflg = 0; 587 sflg = 0; 588 tflg = 0; 589 Sflg = 0; 590 statreq = 0; 591 } 592 593 fixedwidth = 2; 594 if (pflg || Fflg) 595 fixedwidth++; 596 if (iflg) 597 fixedwidth += 11; 598 if (sflg) 599 fixedwidth += 5; 600 601 if (lflg) { 602 if (!gflg && !oflg) 603 gflg = oflg = 1; 604 else 605 if (gflg && oflg) 606 gflg = oflg = 0; 607 Cflg = mflg = 0; 608 } 609 610 if (Cflg || mflg) { 611 char *clptr; 612 if ((clptr = getenv("COLUMNS")) != NULL) 613 num_cols = atoi(clptr); 614 #ifdef TERMINFO 615 else { 616 if (ioctl(1, TIOCGWINSZ, &win) != -1) 617 num_cols = (win.ws_col == 0 ? 80 : win.ws_col); 618 } 619 #endif 620 if (num_cols < 20 || num_cols > 1000) 621 /* assume it is an error */ 622 num_cols = 80; 623 } 624 625 /* allocate space for flist and the associated */ 626 /* data structures (lbufs) */ 627 maxfils = quantn; 628 if (((flist = malloc(maxfils * sizeof (struct lbuf *))) == NULL) || 629 ((nxtlbf = malloc(quantn * sizeof (struct lbuf))) == NULL)) { 630 perror("ls"); 631 exit(2); 632 } 633 if ((amino = (argc-optind)) == 0) { 634 /* 635 * case when no names are given 636 * in ls-command and current 637 * directory is to be used 638 */ 639 argv[optind] = dotp; 640 } 641 642 for (i = 0; i < (amino ? amino : 1); i++) { 643 644 /* 645 * If we are recursing, we need to make sure we don't 646 * get into an endless loop. To keep track of the inodes 647 * (actually, just the directories) visited, we 648 * maintain a directory ancestry list for a file 649 * hierarchy. As we go deeper into the hierarchy, 650 * a parent directory passes its directory list 651 * info (device id, inode number, and a pointer to 652 * its parent) to each of its children. As we 653 * process a child that is a directory, we save 654 * its own personal directory list info. We then 655 * check to see if the child has already been 656 * processed by comparing its device id and inode 657 * number from its own personal directory list info 658 * to that of each of its ancestors. If there is a 659 * match, then we know we've detected a cycle. 660 */ 661 if (Rflg) { 662 /* 663 * This is the first parent in this lineage 664 * (first in a directory hierarchy), so 665 * this parent's parent doesn't exist. We 666 * only initialize myinfo when we are 667 * recursing, otherwise it's not used. 668 */ 669 if ((myinfo = (struct ditem *)malloc( 670 sizeof (struct ditem))) == NULL) { 671 perror("ls"); 672 exit(2); 673 } else { 674 myinfo->dev = 0; 675 myinfo->ino = 0; 676 myinfo->parent = NULL; 677 } 678 } 679 680 if (Cflg || mflg) { 681 width = strcol((unsigned char *)argv[optind]); 682 if (width > filewidth) 683 filewidth = width; 684 } 685 if ((ep = gstat((*argv[optind] ? argv[optind] : dotp), 686 1, myinfo)) == NULL) { 687 if (nomocore) 688 exit(2); 689 err = 2; 690 optind++; 691 continue; 692 } 693 ep->ln.namep = (*argv[optind] ? argv[optind] : dotp); 694 ep->lflags |= ISARG; 695 optind++; 696 nargs++; /* count good arguments stored in flist */ 697 if (acl_err) 698 err = 2; 699 } 700 colwidth = fixedwidth + filewidth; 701 qsort(flist, (unsigned)nargs, sizeof (struct lbuf *), 702 (int (*)(const void *, const void *))compar); 703 for (i = 0; i < nargs; i++) { 704 if (flist[i]->ltype == 'd' && dflg == 0 || fflg) 705 break; 706 } 707 pem(&flist[0], &flist[i], 0); 708 for (; i < nargs; i++) { 709 pdirectory(flist[i]->ln.namep, Rflg || 710 (amino > 1), nargs, 0, flist[i]->ancinfo); 711 if (nomocore) 712 exit(2); 713 /* -R: print subdirectories found */ 714 while (dfirst || cdfirst) { 715 /* Place direct subdirs on front in right order */ 716 while (cdfirst) { 717 /* reverse cdfirst onto front of dfirst */ 718 dtemp = cdfirst; 719 cdfirst = cdfirst -> dc_next; 720 dtemp -> dc_next = dfirst; 721 dfirst = dtemp; 722 } 723 /* take off first dir on dfirst & print it */ 724 dtemp = dfirst; 725 dfirst = dfirst->dc_next; 726 pdirectory(dtemp->dc_name, 1, nargs, 727 dtemp->cycle_detected, dtemp->myancinfo); 728 if (nomocore) 729 exit(2); 730 free(dtemp->dc_name); 731 free(dtemp); 732 } 733 } 734 return (err); 735 } 736 737 /* 738 * pdirectory: print the directory name, labelling it if title is 739 * nonzero, using lp as the place to start reading in the dir. 740 */ 741 static void 742 pdirectory(char *name, int title, int lp, int cdetect, struct ditem *myinfo) 743 { 744 struct dchain *dp; 745 struct lbuf *ap; 746 char *pname; 747 int j; 748 749 filewidth = 0; 750 curdir = name; 751 if (title) { 752 if (!first) 753 (void) putc('\n', stdout); 754 pprintf(name, ":"); 755 new_line(); 756 } 757 /* 758 * If there was a cycle detected, then notify and don't report 759 * further. 760 */ 761 if (cdetect) { 762 if (lflg || sflg) { 763 curcol += printf(gettext("total %d"), 0); 764 new_line(); 765 } 766 (void) fprintf(stderr, gettext( 767 "ls: cycle detected for %s\n"), name); 768 return; 769 } 770 771 nfiles = lp; 772 rddir(name, myinfo); 773 if (nomocore) 774 return; 775 if (fflg == 0) 776 qsort(&flist[lp], (unsigned)(nfiles - lp), 777 sizeof (struct lbuf *), 778 (int (*)(const void *, const void *))compar); 779 if (Rflg) { 780 for (j = nfiles - 1; j >= lp; j--) { 781 ap = flist[j]; 782 if (ap->ltype == 'd' && strcmp(ap->ln.lname, ".") && 783 strcmp(ap->ln.lname, "..")) { 784 dp = malloc(sizeof (struct dchain)); 785 if (dp == NULL) { 786 perror("ls"); 787 exit(2); 788 } 789 pname = makename(curdir, ap->ln.lname); 790 if ((dp->dc_name = strdup(pname)) == NULL) { 791 perror("ls"); 792 exit(2); 793 } 794 dp->cycle_detected = ap->cycle; 795 dp->myancinfo = ap->ancinfo; 796 dp->dc_next = dfirst; 797 dfirst = dp; 798 } 799 } 800 } 801 if (lflg || sflg) { 802 curcol += printf(gettext("total %llu"), tblocks); 803 new_line(); 804 } 805 pem(&flist[lp], &flist[nfiles], lflg||sflg); 806 } 807 808 /* 809 * pem: print 'em. Print a list of files (e.g. a directory) bounded 810 * by slp and lp. 811 */ 812 static void 813 pem(struct lbuf **slp, struct lbuf **lp, int tot_flag) 814 { 815 long row, nrows, i; 816 int col, ncols; 817 struct lbuf **ep; 818 819 if (Cflg || mflg) { 820 if (colwidth > num_cols) { 821 ncols = 1; 822 } else { 823 ncols = num_cols / colwidth; 824 } 825 } 826 827 if (ncols == 1 || mflg || xflg || !Cflg) { 828 for (ep = slp; ep < lp; ep++) 829 pentry(*ep); 830 new_line(); 831 return; 832 } 833 /* otherwise print -C columns */ 834 if (tot_flag) { 835 slp--; 836 row = 1; 837 } 838 else 839 row = 0; 840 841 nrows = (lp - slp - 1) / ncols + 1; 842 for (i = 0; i < nrows; i++, row++) { 843 for (col = 0; col < ncols; col++) { 844 ep = slp + (nrows * col) + row; 845 if (ep < lp) 846 pentry(*ep); 847 } 848 new_line(); 849 } 850 } 851 852 /* 853 * print one output entry; 854 * if uid/gid is not found in the appropriate 855 * file(passwd/group), then print uid/gid instead of 856 * user/group name; 857 */ 858 static void 859 pentry(struct lbuf *ap) 860 { 861 struct lbuf *p; 862 numbuf_t hbuf; 863 char buf[BUFSIZ]; 864 char *dmark = ""; /* Used if -p or -F option active */ 865 char *cp; 866 867 p = ap; 868 column(); 869 if (iflg) 870 if (mflg && !lflg) 871 curcol += printf("%llu ", (long long)p->lnum); 872 else 873 curcol += printf("%10llu ", (long long)p->lnum); 874 if (sflg) 875 curcol += printf((mflg && !lflg) ? "%lld " : 876 (p->lblocks < 10000) ? "%4lld " : "%lld ", 877 (p->ltype != 'b' && p->ltype != 'c') ? 878 p->lblocks : 0LL); 879 if (lflg) { 880 (void) putchar(p->ltype); 881 curcol++; 882 pmode(p->lflags); 883 884 /* ACL: additional access mode flag */ 885 (void) putchar(p->acl); 886 curcol++; 887 888 curcol += printf("%3lu ", (ulong_t)p->lnl); 889 if (oflg) 890 if (!nflg) { 891 cp = getname(p->luid); 892 curcol += printf("%-8s ", cp); 893 } else 894 curcol += printf("%-8lu ", (ulong_t)p->luid); 895 if (gflg) 896 if (!nflg) { 897 cp = getgroup(p->lgid); 898 curcol += printf("%-8s ", cp); 899 } else 900 curcol += printf("%-8lu ", (ulong_t)p->lgid); 901 if (p->ltype == 'b' || p->ltype == 'c') { 902 curcol += printf("%3u, %2u", 903 (uint_t)major((dev_t)p->lsize), 904 (uint_t)minor((dev_t)p->lsize)); 905 } else if (hflg && (p->lsize >= hscale)) { 906 curcol += printf("%7s", 907 number_to_scaled_string(hbuf, p->lsize, hscale)); 908 } else { 909 curcol += printf((p->lsize < (off_t)10000000) ? 910 "%7lld" : "%lld", p->lsize); 911 } 912 if (eflg) 913 format_time(FORMAT3, p->lmtime.tv_sec); 914 else if (Eflg) 915 /* fill in nanoseconds first */ 916 format_etime(FORMAT4, p->lmtime.tv_sec, 917 p->lmtime.tv_nsec); 918 else { 919 if ((p->lmtime.tv_sec < year) || 920 (p->lmtime.tv_sec > now)) 921 format_time(FORMAT1, p->lmtime.tv_sec); 922 else 923 format_time(FORMAT2, p->lmtime.tv_sec); 924 } 925 /* format extended system attribute time */ 926 if (tmflg && crtm) 927 format_attrtime(p); 928 929 curcol += printf("%s", time_buf); 930 931 } 932 /* 933 * prevent both "->" and trailing marks 934 * from appearing 935 */ 936 937 if (pflg && p->ltype == 'd') 938 dmark = "/"; 939 940 if (Fflg && !(lflg && p->flinkto)) { 941 if (p->ltype == 'd') 942 dmark = "/"; 943 else if (p->ltype == 'D') 944 dmark = ">"; 945 else if (p->ltype == 'p') 946 dmark = "|"; 947 else if (p->ltype == 'l') 948 dmark = "@"; 949 else if (p->ltype == 's') 950 dmark = "="; 951 else if (p->lflags & (S_IXUSR|S_IXGRP|S_IXOTH)) 952 dmark = "*"; 953 else 954 dmark = ""; 955 } 956 957 if (lflg && p->flinkto) { 958 (void) strncpy(buf, " -> ", 4); 959 (void) strcpy(buf + 4, p->flinkto); 960 dmark = buf; 961 } 962 if (p->lflags & ISARG) { 963 if (qflg || bflg) 964 pprintf(p->ln.namep, dmark); 965 else { 966 (void) printf("%s%s", p->ln.namep, dmark); 967 curcol += strcol((unsigned char *)p->ln.namep); 968 curcol += strcol((unsigned char *)dmark); 969 } 970 } else { 971 if (qflg || bflg) 972 pprintf(p->ln.lname, dmark); 973 else { 974 (void) printf("%s%s", p->ln.lname, dmark); 975 curcol += strcol((unsigned char *)p->ln.lname); 976 curcol += strcol((unsigned char *)dmark); 977 } 978 } 979 980 /* Display extended system attributes */ 981 if (saflg) { 982 int i; 983 984 new_line(); 985 (void) printf(" \t{"); 986 if (p->exttr != NULL) { 987 int k = 0; 988 for (i = 0; i < sacnt; i++) { 989 if (p->exttr[i].name != NULL) 990 k++; 991 } 992 for (i = 0; i < sacnt; i++) { 993 if (p->exttr[i].name != NULL) { 994 (void) printf("%s", p->exttr[i].name); 995 k--; 996 if (vopt && (k != 0)) 997 (void) printf(","); 998 } 999 } 1000 } 1001 (void) printf("}\n"); 1002 } 1003 /* Display file timestamps and extended system attribute timestamps */ 1004 if (tmflg && alltm) { 1005 new_line(); 1006 print_time(p); 1007 new_line(); 1008 } 1009 if (vflg) { 1010 new_line(); 1011 if (p->aclp) { 1012 acl_printacl(p->aclp, num_cols, Vflg); 1013 } 1014 } 1015 /* Free extended system attribute lists */ 1016 if (saflg || tmflg) 1017 free_sysattr(p); 1018 } 1019 1020 /* print various r,w,x permissions */ 1021 static void 1022 pmode(mode_t aflag) 1023 { 1024 /* these arrays are declared static to allow initializations */ 1025 static int m0[] = { 1, S_IRUSR, 'r', '-' }; 1026 static int m1[] = { 1, S_IWUSR, 'w', '-' }; 1027 static int m2[] = { 3, S_ISUID|S_IXUSR, 's', S_IXUSR, 1028 'x', S_ISUID, 'S', '-' }; 1029 static int m3[] = { 1, S_IRGRP, 'r', '-' }; 1030 static int m4[] = { 1, S_IWGRP, 'w', '-' }; 1031 static int m5[] = { 4, S_ISGID|S_IXGRP, 's', S_IXGRP, 1032 'x', S_ISGID|LS_NOTREG, 'S', 1033 #ifdef XPG4 1034 S_ISGID, 'L', '-'}; 1035 #else 1036 S_ISGID, 'l', '-'}; 1037 #endif 1038 static int m6[] = { 1, S_IROTH, 'r', '-' }; 1039 static int m7[] = { 1, S_IWOTH, 'w', '-' }; 1040 static int m8[] = { 3, S_ISVTX|S_IXOTH, 't', S_IXOTH, 1041 'x', S_ISVTX, 'T', '-'}; 1042 1043 static int *m[] = { m0, m1, m2, m3, m4, m5, m6, m7, m8}; 1044 1045 int **mp; 1046 1047 flags = aflag; 1048 for (mp = &m[0]; mp < &m[sizeof (m) / sizeof (m[0])]; mp++) 1049 selection(*mp); 1050 } 1051 1052 static void 1053 selection(int *pairp) 1054 { 1055 int n; 1056 1057 n = *pairp++; 1058 while (n-->0) { 1059 if ((flags & *pairp) == *pairp) { 1060 pairp++; 1061 break; 1062 } else { 1063 pairp += 2; 1064 } 1065 } 1066 (void) putchar(*pairp); 1067 curcol++; 1068 } 1069 1070 /* 1071 * column: get to the beginning of the next column. 1072 */ 1073 static void 1074 column(void) 1075 { 1076 if (curcol == 0) 1077 return; 1078 if (mflg) { 1079 (void) putc(',', stdout); 1080 curcol++; 1081 if (curcol + colwidth + 2 > num_cols) { 1082 (void) putc('\n', stdout); 1083 curcol = 0; 1084 return; 1085 } 1086 (void) putc(' ', stdout); 1087 curcol++; 1088 return; 1089 } 1090 if (Cflg == 0) { 1091 (void) putc('\n', stdout); 1092 curcol = 0; 1093 return; 1094 } 1095 if ((curcol / colwidth + 2) * colwidth > num_cols) { 1096 (void) putc('\n', stdout); 1097 curcol = 0; 1098 return; 1099 } 1100 do { 1101 (void) putc(' ', stdout); 1102 curcol++; 1103 } while (curcol % colwidth); 1104 } 1105 1106 static void 1107 new_line(void) 1108 { 1109 if (curcol) { 1110 first = 0; 1111 (void) putc('\n', stdout); 1112 curcol = 0; 1113 } 1114 } 1115 1116 /* 1117 * read each filename in directory dir and store its 1118 * status in flist[nfiles] 1119 * use makename() to form pathname dir/filename; 1120 */ 1121 static void 1122 rddir(char *dir, struct ditem *myinfo) 1123 { 1124 struct dirent *dentry; 1125 DIR *dirf; 1126 int j; 1127 struct lbuf *ep; 1128 int width; 1129 1130 if ((dirf = opendir(dir)) == NULL) { 1131 (void) fflush(stdout); 1132 perror(dir); 1133 err = 2; 1134 return; 1135 } else { 1136 tblocks = 0; 1137 for (;;) { 1138 errno = 0; 1139 if ((dentry = readdir(dirf)) == NULL) 1140 break; 1141 if (aflg == 0 && dentry->d_name[0] == '.' && 1142 (Aflg == 0 || 1143 dentry->d_name[1] == '\0' || 1144 dentry->d_name[1] == '.' && 1145 dentry->d_name[2] == '\0')) 1146 /* 1147 * check for directory items '.', '..', 1148 * and items without valid inode-number; 1149 */ 1150 continue; 1151 1152 if (Cflg || mflg) { 1153 width = strcol((unsigned char *)dentry->d_name); 1154 if (width > filewidth) 1155 filewidth = width; 1156 } 1157 ep = gstat(makename(dir, dentry->d_name), 0, myinfo); 1158 if (ep == NULL) { 1159 if (nomocore) 1160 exit(2); 1161 continue; 1162 } else { 1163 ep->lnum = dentry->d_ino; 1164 for (j = 0; dentry->d_name[j] != '\0'; j++) 1165 ep->ln.lname[j] = dentry->d_name[j]; 1166 ep->ln.lname[j] = '\0'; 1167 } 1168 } 1169 if (errno) { 1170 int sav_errno = errno; 1171 1172 (void) fprintf(stderr, 1173 gettext("ls: error reading directory %s: %s\n"), 1174 dir, strerror(sav_errno)); 1175 } 1176 (void) closedir(dirf); 1177 colwidth = fixedwidth + filewidth; 1178 } 1179 } 1180 1181 /* 1182 * Attaching a link to an inode's ancestors. Search 1183 * through the ancestors to check for cycles (an inode which 1184 * we have already tracked in this inodes ancestry). If a cycle 1185 * is detected, set the exit code and record the fact so that 1186 * it is reported at the right time when printing the directory. 1187 * In addition, set the exit code. Note: If the -a flag was 1188 * specified, we don't want to check for cycles for directories 1189 * ending in '/.' or '/..' unless they were specified on the 1190 * command line. 1191 */ 1192 static void 1193 record_ancestry(char *file, struct stat *pstatb, struct lbuf *rep, 1194 int argfl, struct ditem *myparent) 1195 { 1196 size_t file_len; 1197 struct ditem *myinfo; 1198 struct ditem *tptr; 1199 1200 file_len = strlen(file); 1201 if (!aflg || argfl || (NOTWORKINGDIR(file, file_len) && 1202 NOTPARENTDIR(file, file_len))) { 1203 /* 1204 * Add this inode's ancestry 1205 * info and insert it into the 1206 * ancestry list by pointing 1207 * back to its parent. We save 1208 * it (in rep) with the other info 1209 * we're gathering for this inode. 1210 */ 1211 if ((myinfo = malloc( 1212 sizeof (struct ditem))) == NULL) { 1213 perror("ls"); 1214 exit(2); 1215 } 1216 myinfo->dev = pstatb->st_dev; 1217 myinfo->ino = pstatb->st_ino; 1218 myinfo->parent = myparent; 1219 rep->ancinfo = myinfo; 1220 1221 /* 1222 * If this node has the same device id and 1223 * inode number of one of its ancestors, 1224 * then we've detected a cycle. 1225 */ 1226 if (myparent != NULL) { 1227 for (tptr = myparent; tptr->parent != NULL; 1228 tptr = tptr->parent) { 1229 if ((tptr->dev == pstatb->st_dev) && 1230 (tptr->ino == pstatb->st_ino)) { 1231 /* 1232 * Cycle detected for this 1233 * directory. Record the fact 1234 * it is a cycle so we don't 1235 * try to process this 1236 * directory as we are 1237 * walking through the 1238 * list of directories. 1239 */ 1240 rep->cycle = 1; 1241 err = 2; 1242 break; 1243 } 1244 } 1245 } 1246 } 1247 } 1248 1249 /* 1250 * Do re-calculate the mode for group for ACE_T type of acls. 1251 * This is because, if the server's FS happens to be UFS, supporting 1252 * POSIX ACL's, then it does a special calculation of group mode 1253 * to be the bitwise OR of CLASS_OBJ and GROUP_OBJ (see PSARC/2001/717.) 1254 * 1255 * This algorithm is from the NFSv4 ACL Draft. Here a part of that 1256 * algorithm is used for the group mode calculation only. 1257 * What is modified here from the algorithm is that only the 1258 * entries with flags ACE_GROUP are considered. For each entry 1259 * with ACE_GROUP flag, the first occurance of a specific access 1260 * is checked if it is allowed. 1261 * We are not interested in perms for user and other, as they 1262 * were taken from st_mode value. 1263 * We are not interested in a_who field of ACE, as we need just 1264 * unix mode bits for the group. 1265 */ 1266 1267 #define OWNED_GROUP (ACE_GROUP | ACE_IDENTIFIER_GROUP) 1268 #define IS_TYPE_ALLOWED(type) ((type) == ACE_ACCESS_ALLOWED_ACE_TYPE) 1269 1270 int 1271 grp_mask_to_mode(acl_t *acep) 1272 { 1273 int mode = 0, seen = 0; 1274 int acecnt; 1275 int flags; 1276 ace_t *ap; 1277 1278 acecnt = acl_cnt(acep); 1279 for (ap = (ace_t *)acl_data(acep); acecnt--; ap++) { 1280 1281 if (ap->a_type != ACE_ACCESS_ALLOWED_ACE_TYPE && 1282 ap->a_type != ACE_ACCESS_DENIED_ACE_TYPE) 1283 continue; 1284 1285 if (ap->a_flags & ACE_INHERIT_ONLY_ACE) 1286 continue; 1287 1288 /* 1289 * if it is first group@ or first everyone@ 1290 * for each of read, write and execute, then 1291 * that will be the group mode bit. 1292 */ 1293 flags = ap->a_flags & ACE_TYPE_FLAGS; 1294 if (flags == OWNED_GROUP || flags == ACE_EVERYONE) { 1295 if (ap->a_access_mask & ACE_READ_DATA) { 1296 if (!(seen & S_IRGRP)) { 1297 seen |= S_IRGRP; 1298 if (IS_TYPE_ALLOWED(ap->a_type)) 1299 mode |= S_IRGRP; 1300 } 1301 } 1302 if (ap->a_access_mask & ACE_WRITE_DATA) { 1303 if (!(seen & S_IWGRP)) { 1304 seen |= S_IWGRP; 1305 if (IS_TYPE_ALLOWED(ap->a_type)) 1306 mode |= S_IWGRP; 1307 } 1308 } 1309 if (ap->a_access_mask & ACE_EXECUTE) { 1310 if (!(seen & S_IXGRP)) { 1311 seen |= S_IXGRP; 1312 if (IS_TYPE_ALLOWED(ap->a_type)) 1313 mode |= S_IXGRP; 1314 } 1315 } 1316 } 1317 } 1318 return (mode); 1319 } 1320 1321 /* 1322 * get status of file and recomputes tblocks; 1323 * argfl = 1 if file is a name in ls-command and = 0 1324 * for filename in a directory whose name is an 1325 * argument in the command; 1326 * stores a pointer in flist[nfiles] and 1327 * returns that pointer; 1328 * returns NULL if failed; 1329 */ 1330 static struct lbuf * 1331 gstat(char *file, int argfl, struct ditem *myparent) 1332 { 1333 struct stat statb, statb1; 1334 struct lbuf *rep; 1335 char buf[BUFSIZ]; 1336 ssize_t cc; 1337 int (*statf)() = ((Lflg) || (Hflg && argfl)) ? stat : lstat; 1338 int aclcnt; 1339 int error; 1340 aclent_t *tp; 1341 o_mode_t groupperm, mask; 1342 int grouppermfound, maskfound; 1343 1344 if (nomocore) 1345 return (NULL); 1346 1347 if (nfiles >= maxfils) { 1348 /* 1349 * all flist/lbuf pair assigned files, time to get some 1350 * more space 1351 */ 1352 maxfils += quantn; 1353 if (((flist = realloc(flist, 1354 maxfils * sizeof (struct lbuf *))) == NULL) || 1355 ((nxtlbf = malloc(quantn * 1356 sizeof (struct lbuf))) == NULL)) { 1357 perror("ls"); 1358 nomocore = 1; 1359 return (NULL); 1360 } 1361 } 1362 1363 /* 1364 * nfiles is reset to nargs for each directory 1365 * that is given as an argument maxn is checked 1366 * to prevent the assignment of an lbuf to a flist entry 1367 * that already has one assigned. 1368 */ 1369 if (nfiles >= maxn) { 1370 rep = nxtlbf++; 1371 flist[nfiles++] = rep; 1372 maxn = nfiles; 1373 } else { 1374 rep = flist[nfiles++]; 1375 } 1376 1377 /* Initialize */ 1378 1379 rep->lflags = (mode_t)0; 1380 rep->flinkto = NULL; 1381 rep->cycle = 0; 1382 rep->lat.tv_sec = time(NULL); 1383 rep->lat.tv_nsec = 0; 1384 rep->lct.tv_sec = time(NULL); 1385 rep->lct.tv_nsec = 0; 1386 rep->lmt.tv_sec = time(NULL); 1387 rep->lmt.tv_nsec = 0; 1388 rep->exttr = NULL; 1389 rep->extm = NULL; 1390 1391 if (argfl || statreq) { 1392 int doacl; 1393 1394 if (lflg) 1395 doacl = 1; 1396 else 1397 doacl = 0; 1398 1399 if ((*statf)(file, &statb) < 0) { 1400 if (argfl || errno != ENOENT || 1401 (Lflg && lstat(file, &statb) == 0)) { 1402 /* 1403 * Avoid race between readdir and lstat. 1404 * Print error message in case of dangling link. 1405 */ 1406 perror(file); 1407 } 1408 nfiles--; 1409 return (NULL); 1410 } 1411 1412 /* 1413 * If -H was specified, and the file linked to was 1414 * not a directory, then we need to get the info 1415 * for the symlink itself. 1416 */ 1417 if ((Hflg) && (argfl) && 1418 ((statb.st_mode & S_IFMT) != S_IFDIR)) { 1419 if (lstat(file, &statb) < 0) { 1420 perror(file); 1421 } 1422 } 1423 1424 rep->lnum = statb.st_ino; 1425 rep->lsize = statb.st_size; 1426 rep->lblocks = statb.st_blocks; 1427 switch (statb.st_mode & S_IFMT) { 1428 case S_IFDIR: 1429 rep->ltype = 'd'; 1430 if (Rflg) { 1431 record_ancestry(file, &statb, rep, 1432 argfl, myparent); 1433 } 1434 break; 1435 case S_IFBLK: 1436 rep->ltype = 'b'; 1437 rep->lsize = (off_t)statb.st_rdev; 1438 break; 1439 case S_IFCHR: 1440 rep->ltype = 'c'; 1441 rep->lsize = (off_t)statb.st_rdev; 1442 break; 1443 case S_IFIFO: 1444 rep->ltype = 'p'; 1445 break; 1446 case S_IFSOCK: 1447 rep->ltype = 's'; 1448 rep->lsize = 0; 1449 break; 1450 case S_IFLNK: 1451 /* symbolic links may not have ACLs, so elide acl() */ 1452 if ((Lflg == 0) || (Hflg == 0) || 1453 ((Hflg) && (!argfl))) { 1454 doacl = 0; 1455 } 1456 rep->ltype = 'l'; 1457 if (lflg) { 1458 cc = readlink(file, buf, BUFSIZ); 1459 if (cc >= 0) { 1460 1461 /* 1462 * follow the symbolic link 1463 * to generate the appropriate 1464 * Fflg marker for the object 1465 * eg, /bin -> /sym/bin/ 1466 */ 1467 if ((Fflg || pflg) && 1468 (stat(file, &statb1) >= 0)) { 1469 switch (statb1.st_mode & 1470 S_IFMT) { 1471 case S_IFDIR: 1472 buf[cc++] = '/'; 1473 break; 1474 case S_IFSOCK: 1475 buf[cc++] = '='; 1476 break; 1477 case S_IFDOOR: 1478 buf[cc++] = '>'; 1479 break; 1480 case S_IFIFO: 1481 buf[cc++] = '|'; 1482 break; 1483 default: 1484 if ((statb1.st_mode & 1485 ~S_IFMT) & 1486 (S_IXUSR|S_IXGRP| 1487 S_IXOTH)) 1488 buf[cc++] = '*'; 1489 break; 1490 } 1491 } 1492 buf[cc] = '\0'; 1493 rep->flinkto = strdup(buf); 1494 } 1495 break; 1496 } 1497 1498 /* 1499 * ls /sym behaves differently from ls /sym/ 1500 * when /sym is a symbolic link. This is fixed 1501 * when explicit arguments are specified. 1502 */ 1503 1504 #ifdef XPG6 1505 /* Do not follow a symlink when -F is specified */ 1506 if ((!argfl) || (argfl && Fflg) || 1507 (stat(file, &statb1) < 0)) 1508 #else 1509 /* Follow a symlink when -F is specified */ 1510 if (!argfl || stat(file, &statb1) < 0) 1511 #endif /* XPG6 */ 1512 break; 1513 if ((statb1.st_mode & S_IFMT) == S_IFDIR) { 1514 statb = statb1; 1515 rep->ltype = 'd'; 1516 rep->lsize = statb1.st_size; 1517 if (Rflg) { 1518 record_ancestry(file, &statb, rep, 1519 argfl, myparent); 1520 } 1521 } 1522 break; 1523 case S_IFDOOR: 1524 rep->ltype = 'D'; 1525 break; 1526 case S_IFREG: 1527 rep->ltype = '-'; 1528 break; 1529 case S_IFPORT: 1530 rep->ltype = 'P'; 1531 break; 1532 default: 1533 rep->ltype = '?'; 1534 break; 1535 } 1536 rep->lflags = statb.st_mode & ~S_IFMT; 1537 1538 if (!S_ISREG(statb.st_mode)) 1539 rep->lflags |= LS_NOTREG; 1540 1541 rep->luid = statb.st_uid; 1542 rep->lgid = statb.st_gid; 1543 rep->lnl = statb.st_nlink; 1544 if (uflg || (tmflg && atm)) 1545 rep->lmtime = statb.st_atim; 1546 else if (cflg || (tmflg && ctm)) 1547 rep->lmtime = statb.st_ctim; 1548 else 1549 rep->lmtime = statb.st_mtim; 1550 rep->lat = statb.st_atim; 1551 rep->lct = statb.st_ctim; 1552 rep->lmt = statb.st_mtim; 1553 1554 /* ACL: check acl entries count */ 1555 if (doacl) { 1556 1557 error = acl_get(file, 0, &rep->aclp); 1558 if (error) { 1559 (void) fprintf(stderr, 1560 gettext("ls: can't read ACL on %s: %s\n"), 1561 file, acl_strerror(error)); 1562 rep->acl = ' '; 1563 acl_err++; 1564 return (rep); 1565 } 1566 1567 rep->acl = ' '; 1568 1569 if (rep->aclp && 1570 ((acl_flags(rep->aclp) & ACL_IS_TRIVIAL) == 0)) { 1571 rep->acl = '+'; 1572 /* 1573 * Special handling for ufs aka aclent_t ACL's 1574 */ 1575 if (acl_type(rep->aclp) == ACLENT_T) { 1576 /* 1577 * For files with non-trivial acls, the 1578 * effective group permissions are the 1579 * intersection of the GROUP_OBJ value 1580 * and the CLASS_OBJ (acl mask) value. 1581 * Determine both the GROUP_OBJ and 1582 * CLASS_OBJ for this file and insert 1583 * the logical AND of those two values 1584 * in the group permissions field 1585 * of the lflags value for this file. 1586 */ 1587 1588 /* 1589 * Until found in acl list, assume 1590 * maximum permissions for both group 1591 * a nd mask. (Just in case the acl 1592 * lacks either value for some reason.) 1593 */ 1594 groupperm = 07; 1595 mask = 07; 1596 grouppermfound = 0; 1597 maskfound = 0; 1598 aclcnt = acl_cnt(rep->aclp); 1599 for (tp = 1600 (aclent_t *)acl_data(rep->aclp); 1601 aclcnt--; tp++) { 1602 if (tp->a_type == GROUP_OBJ) { 1603 groupperm = tp->a_perm; 1604 grouppermfound = 1; 1605 continue; 1606 } 1607 if (tp->a_type == CLASS_OBJ) { 1608 mask = tp->a_perm; 1609 maskfound = 1; 1610 } 1611 if (grouppermfound && maskfound) 1612 break; 1613 } 1614 1615 1616 /* reset all the group bits */ 1617 rep->lflags &= ~S_IRWXG; 1618 1619 /* 1620 * Now set them to the logical AND of 1621 * the GROUP_OBJ permissions and the 1622 * acl mask. 1623 */ 1624 1625 rep->lflags |= (groupperm & mask) << 3; 1626 1627 } else if (acl_type(rep->aclp) == ACE_T) { 1628 int mode; 1629 mode = grp_mask_to_mode(rep->aclp); 1630 rep->lflags &= ~S_IRWXG; 1631 rep->lflags |= mode; 1632 } 1633 } 1634 1635 if (!vflg && !Vflg && rep->aclp) { 1636 acl_free(rep->aclp); 1637 rep->aclp = NULL; 1638 } 1639 1640 if (atflg && pathconf(file, _PC_XATTR_EXISTS) == 1) 1641 rep->acl = '@'; 1642 1643 } else 1644 rep->acl = ' '; 1645 1646 /* mask ISARG and other file-type bits */ 1647 1648 if (rep->ltype != 'b' && rep->ltype != 'c') 1649 tblocks += rep->lblocks; 1650 1651 /* Get extended system attributes */ 1652 1653 if ((saflg || (tmflg && crtm) || (tmflg && alltm)) && 1654 (sysattr_support(file, _PC_SATTR_EXISTS) == 1)) { 1655 int i; 1656 1657 sacnt = attr_count(); 1658 /* 1659 * Allocate 'sacnt' size array to hold extended 1660 * system attribute name (verbose) or respective 1661 * symbol represenation (compact). 1662 */ 1663 rep->exttr = xmalloc(sacnt * sizeof (struct attrb), 1664 rep); 1665 1666 /* initialize boolean attribute list */ 1667 for (i = 0; i < sacnt; i++) 1668 rep->exttr[i].name = NULL; 1669 if (get_sysxattr(file, rep) != 0) { 1670 (void) fprintf(stderr, 1671 gettext("ls:Failed to retrieve " 1672 "extended system attribute from " 1673 "%s\n"), file); 1674 rep->exttr[0].name = xmalloc(2, rep); 1675 (void) strlcpy(rep->exttr[0].name, "?", 2); 1676 } 1677 } 1678 } 1679 return (rep); 1680 } 1681 1682 /* 1683 * returns pathname of the form dir/file; 1684 * dir and file are null-terminated strings. 1685 */ 1686 static char * 1687 makename(char *dir, char *file) 1688 { 1689 /* 1690 * PATH_MAX is the maximum length of a path name. 1691 * MAXNAMLEN is the maximum length of any path name component. 1692 * Allocate space for both, plus the '/' in the middle 1693 * and the null character at the end. 1694 * dfile is static as this is returned by makename(). 1695 */ 1696 static char dfile[PATH_MAX + 1 + MAXNAMLEN + 1]; 1697 char *dp, *fp; 1698 1699 dp = dfile; 1700 fp = dir; 1701 while (*fp) 1702 *dp++ = *fp++; 1703 if (dp > dfile && *(dp - 1) != '/') 1704 *dp++ = '/'; 1705 fp = file; 1706 while (*fp) 1707 *dp++ = *fp++; 1708 *dp = '\0'; 1709 return (dfile); 1710 } 1711 1712 1713 #include <pwd.h> 1714 #include <grp.h> 1715 #include <utmpx.h> 1716 1717 struct utmpx utmp; 1718 1719 #define NMAX (sizeof (utmp.ut_name)) 1720 #define SCPYN(a, b) (void) strncpy(a, b, NMAX) 1721 1722 1723 struct cachenode { /* this struct must be zeroed before using */ 1724 struct cachenode *lesschild; /* subtree whose entries < val */ 1725 struct cachenode *grtrchild; /* subtree whose entries > val */ 1726 long val; /* the uid or gid of this entry */ 1727 int initted; /* name has been filled in */ 1728 char name[NMAX+1]; /* the string that val maps to */ 1729 }; 1730 static struct cachenode *names, *groups; 1731 1732 static struct cachenode * 1733 findincache(struct cachenode **head, long val) 1734 { 1735 struct cachenode **parent = head; 1736 struct cachenode *c = *parent; 1737 1738 while (c != NULL) { 1739 if (val == c->val) { 1740 /* found it */ 1741 return (c); 1742 } else if (val < c->val) { 1743 parent = &c->lesschild; 1744 c = c->lesschild; 1745 } else { 1746 parent = &c->grtrchild; 1747 c = c->grtrchild; 1748 } 1749 } 1750 1751 /* not in the cache, make a new entry for it */ 1752 c = calloc(1, sizeof (struct cachenode)); 1753 if (c == NULL) { 1754 perror("ls"); 1755 exit(2); 1756 } 1757 *parent = c; 1758 c->val = val; 1759 return (c); 1760 } 1761 1762 /* 1763 * get name from cache, or passwd file for a given uid; 1764 * lastuid is set to uid. 1765 */ 1766 static char * 1767 getname(uid_t uid) 1768 { 1769 struct passwd *pwent; 1770 struct cachenode *c; 1771 1772 if ((uid == lastuid) && lastuname) 1773 return (lastuname); 1774 1775 c = findincache(&names, uid); 1776 if (c->initted == 0) { 1777 if ((pwent = getpwuid(uid)) != NULL) { 1778 SCPYN(&c->name[0], pwent->pw_name); 1779 } else { 1780 (void) sprintf(&c->name[0], "%-8u", (int)uid); 1781 } 1782 c->initted = 1; 1783 } 1784 lastuid = uid; 1785 lastuname = &c->name[0]; 1786 return (lastuname); 1787 } 1788 1789 /* 1790 * get name from cache, or group file for a given gid; 1791 * lastgid is set to gid. 1792 */ 1793 static char * 1794 getgroup(gid_t gid) 1795 { 1796 struct group *grent; 1797 struct cachenode *c; 1798 1799 if ((gid == lastgid) && lastgname) 1800 return (lastgname); 1801 1802 c = findincache(&groups, gid); 1803 if (c->initted == 0) { 1804 if ((grent = getgrgid(gid)) != NULL) { 1805 SCPYN(&c->name[0], grent->gr_name); 1806 } else { 1807 (void) sprintf(&c->name[0], "%-8u", (int)gid); 1808 } 1809 c->initted = 1; 1810 } 1811 lastgid = gid; 1812 lastgname = &c->name[0]; 1813 return (lastgname); 1814 } 1815 1816 /* return >0 if item pointed by pp2 should appear first */ 1817 static int 1818 compar(struct lbuf **pp1, struct lbuf **pp2) 1819 { 1820 struct lbuf *p1, *p2; 1821 1822 p1 = *pp1; 1823 p2 = *pp2; 1824 if (dflg == 0) { 1825 /* 1826 * compare two names in ls-command one of which is file 1827 * and the other is a directory; 1828 * this portion is not used for comparing files within 1829 * a directory name of ls-command; 1830 */ 1831 if (p1->lflags&ISARG && p1->ltype == 'd') { 1832 if (!(p2->lflags&ISARG && p2->ltype == 'd')) 1833 return (1); 1834 } else { 1835 if (p2->lflags&ISARG && p2->ltype == 'd') 1836 return (-1); 1837 } 1838 } 1839 if (tflg) { 1840 if (p2->lmtime.tv_sec > p1->lmtime.tv_sec) 1841 return (rflg); 1842 else if (p2->lmtime.tv_sec < p1->lmtime.tv_sec) 1843 return (-rflg); 1844 /* times are equal to the sec, check nsec */ 1845 if (p2->lmtime.tv_nsec > p1->lmtime.tv_nsec) 1846 return (rflg); 1847 else if (p2->lmtime.tv_nsec < p1->lmtime.tv_nsec) 1848 return (-rflg); 1849 /* if times are equal, fall through and sort by name */ 1850 } else if (Sflg) { 1851 /* 1852 * The size stored in lsize can be either the 1853 * size or the major minor number (in the case of 1854 * block and character special devices). If it's 1855 * a major minor number, then the size is considered 1856 * to be zero and we want to fall through and sort 1857 * by name. In addition, if the size of p2 is equal 1858 * to the size of p1 we want to fall through and 1859 * sort by name. 1860 */ 1861 off_t p1size = (p1->ltype == 'b') || 1862 (p1->ltype == 'c') ? 0 : p1->lsize; 1863 off_t p2size = (p2->ltype == 'b') || 1864 (p2->ltype == 'c') ? 0 : p2->lsize; 1865 if (p2size > p1size) { 1866 return (rflg); 1867 } else if (p2size < p1size) { 1868 return (-rflg); 1869 } 1870 /* Sizes are equal, fall through and sort by name. */ 1871 } 1872 return (rflg * strcoll( 1873 p1->lflags & ISARG ? p1->ln.namep : p1->ln.lname, 1874 p2->lflags&ISARG ? p2->ln.namep : p2->ln.lname)); 1875 } 1876 1877 static void 1878 pprintf(char *s1, char *s2) 1879 { 1880 csi_pprintf((unsigned char *)s1); 1881 csi_pprintf((unsigned char *)s2); 1882 } 1883 1884 static void 1885 csi_pprintf(unsigned char *s) 1886 { 1887 unsigned char *cp; 1888 char c; 1889 int i; 1890 int c_len; 1891 int p_col; 1892 wchar_t pcode; 1893 1894 if (!qflg && !bflg) { 1895 for (cp = s; *cp != '\0'; cp++) { 1896 (void) putchar(*cp); 1897 curcol++; 1898 } 1899 return; 1900 } 1901 1902 for (cp = s; *cp; ) { 1903 if (isascii(c = *cp)) { 1904 if (!isprint(c)) { 1905 if (qflg) { 1906 c = '?'; 1907 } else { 1908 curcol += 3; 1909 (void) putc('\\', stdout); 1910 c = '0' + ((*cp >> 6) & 07); 1911 (void) putc(c, stdout); 1912 c = '0' + ((*cp >> 3) & 07); 1913 (void) putc(c, stdout); 1914 c = '0' + (*cp & 07); 1915 } 1916 } 1917 curcol++; 1918 cp++; 1919 (void) putc(c, stdout); 1920 continue; 1921 } 1922 1923 if ((c_len = mbtowc(&pcode, (char *)cp, MB_LEN_MAX)) <= 0) { 1924 c_len = 1; 1925 goto not_print; 1926 } 1927 1928 if ((p_col = wcwidth(pcode)) > 0) { 1929 (void) putwchar(pcode); 1930 cp += c_len; 1931 curcol += p_col; 1932 continue; 1933 } 1934 1935 not_print: 1936 for (i = 0; i < c_len; i++) { 1937 if (qflg) { 1938 c = '?'; 1939 } else { 1940 curcol += 3; 1941 (void) putc('\\', stdout); 1942 c = '0' + ((*cp >> 6) & 07); 1943 (void) putc(c, stdout); 1944 c = '0' + ((*cp >> 3) & 07); 1945 (void) putc(c, stdout); 1946 c = '0' + (*cp & 07); 1947 } 1948 curcol++; 1949 (void) putc(c, stdout); 1950 cp++; 1951 } 1952 } 1953 } 1954 1955 static int 1956 strcol(unsigned char *s1) 1957 { 1958 int w; 1959 int w_col; 1960 int len; 1961 wchar_t wc; 1962 1963 w = 0; 1964 while (*s1) { 1965 if (isascii(*s1)) { 1966 w++; 1967 s1++; 1968 continue; 1969 } 1970 1971 if ((len = mbtowc(&wc, (char *)s1, MB_LEN_MAX)) <= 0) { 1972 w++; 1973 s1++; 1974 continue; 1975 } 1976 1977 if ((w_col = wcwidth(wc)) < 0) 1978 w_col = len; 1979 s1 += len; 1980 w += w_col; 1981 } 1982 return (w); 1983 } 1984 1985 /* 1986 * Convert an unsigned long long to a string representation and place the 1987 * result in the caller-supplied buffer. 1988 * 1989 * The number provided is a size in bytes. The number is first 1990 * converted to an integral multiple of 'scale' bytes. This new 1991 * number is then scaled down until it is small enough to be in a good 1992 * human readable format, i.e. in the range 0 thru scale-1. If the 1993 * number used to derive the final number is not a multiple of scale, and 1994 * the final number has only a single significant digit, we compute 1995 * tenths of units to provide a second significant digit. 1996 * 1997 * The value "(unsigned long long)-1" is a special case and is always 1998 * converted to "-1". 1999 * 2000 * A pointer to the caller-supplied buffer is returned. 2001 */ 2002 static char * 2003 number_to_scaled_string( 2004 numbuf_t buf, /* put the result here */ 2005 unsigned long long number, /* convert this number */ 2006 long scale) 2007 { 2008 unsigned long long save; 2009 /* Measurement: kilo, mega, giga, tera, peta, exa */ 2010 char *uom = "KMGTPE"; 2011 2012 if ((long long)number == (long long)-1) { 2013 (void) strlcpy(buf, "-1", sizeof (numbuf_t)); 2014 return (buf); 2015 } 2016 2017 save = number; 2018 number = number / scale; 2019 2020 /* 2021 * Now we have number as a count of scale units. 2022 * If no further scaling is necessary, we round up as appropriate. 2023 * 2024 * The largest value number could have had entering the routine is 2025 * 16 Exabytes, so running off the end of the uom array should 2026 * never happen. We check for that, though, as a guard against 2027 * a breakdown elsewhere in the algorithm. 2028 */ 2029 if (number < (unsigned long long)scale) { 2030 if ((save % scale) >= (unsigned long long)(scale / 2)) { 2031 if (++number == (unsigned long long)scale) { 2032 uom++; 2033 number = 1; 2034 } 2035 } 2036 } else { 2037 while ((number >= (unsigned long long)scale) && (*uom != 'E')) { 2038 uom++; /* next unit of measurement */ 2039 save = number; 2040 /* 2041 * If we're over half way to the next unit of 2042 * 'scale' bytes (which means we should round 2043 * up), then adding half of 'scale' prior to 2044 * the division will push us into that next 2045 * unit of scale when we perform the division 2046 */ 2047 number = (number + (scale / 2)) / scale; 2048 } 2049 } 2050 2051 /* check if we should output a decimal place after the point */ 2052 if ((save / scale) < 10) { 2053 /* snprintf() will round for us */ 2054 float fnum = (float)save / scale; 2055 (void) snprintf(buf, sizeof (numbuf_t), "%2.1f%c", 2056 fnum, *uom); 2057 } else { 2058 (void) snprintf(buf, sizeof (numbuf_t), "%4llu%c", 2059 number, *uom); 2060 } 2061 return (buf); 2062 } 2063 2064 /* Get extended system attributes and set the display */ 2065 2066 int 2067 get_sysxattr(char *fname, struct lbuf *rep) 2068 { 2069 boolean_t value; 2070 data_type_t type; 2071 int error; 2072 char *name; 2073 int i; 2074 2075 if ((error = getattrat(AT_FDCWD, XATTR_VIEW_READWRITE, fname, 2076 &response)) != 0) { 2077 perror("ls:getattrat"); 2078 return (error); 2079 } 2080 2081 /* 2082 * Allocate 'sacnt' size array to hold extended timestamp 2083 * system attributes and initialize the array. 2084 */ 2085 rep->extm = xmalloc(sacnt * sizeof (struct attrtm), rep); 2086 for (i = 0; i < sacnt; i++) { 2087 rep->extm[i].stm = 0; 2088 rep->extm[i].nstm = 0; 2089 rep->extm[i].name = NULL; 2090 } 2091 while ((pair = nvlist_next_nvpair(response, pair)) != NULL) { 2092 name = nvpair_name(pair); 2093 type = nvpair_type(pair); 2094 if (type == DATA_TYPE_BOOLEAN_VALUE) { 2095 error = nvpair_value_boolean_value(pair, &value); 2096 if (error) { 2097 (void) fprintf(stderr, 2098 gettext("nvpair_value_boolean_value " 2099 "failed: error = %d\n"), error); 2100 continue; 2101 } 2102 if (name != NULL) 2103 set_sysattrb_display(name, value, rep); 2104 continue; 2105 } else if (type == DATA_TYPE_UINT64_ARRAY) { 2106 if (name != NULL) 2107 set_sysattrtm_display(name, rep); 2108 continue; 2109 } 2110 } 2111 nvlist_free(response); 2112 return (0); 2113 } 2114 2115 /* Set extended system attribute boolean display */ 2116 2117 void 2118 set_sysattrb_display(char *name, boolean_t val, struct lbuf *rep) 2119 { 2120 f_attr_t fattr; 2121 const char *opt; 2122 size_t len; 2123 2124 fattr = name_to_attr(name); 2125 if (fattr != F_ATTR_INVAL && fattr < sacnt) { 2126 if (vopt) { 2127 len = strlen(name); 2128 if (val) { 2129 rep->exttr[fattr].name = xmalloc(len + 1, rep); 2130 (void) strlcpy(rep->exttr[fattr].name, name, 2131 len + 1); 2132 } else { 2133 rep->exttr[fattr].name = xmalloc(len + 3, rep); 2134 (void) snprintf(rep->exttr[fattr].name, len + 3, 2135 "no%s", name); 2136 } 2137 } else { 2138 opt = attr_to_option(fattr); 2139 if (opt != NULL) { 2140 len = strlen(opt); 2141 rep->exttr[fattr].name = xmalloc(len + 1, rep); 2142 if (val) 2143 (void) strlcpy(rep->exttr[fattr].name, 2144 opt, len + 1); 2145 else 2146 (void) strlcpy(rep->exttr[fattr].name, 2147 "-", len + 1); 2148 } 2149 } 2150 } 2151 } 2152 2153 /* Set extended system attribute timestamp display */ 2154 2155 void 2156 set_sysattrtm_display(char *name, struct lbuf *rep) 2157 { 2158 uint_t nelem; 2159 uint64_t *value; 2160 int i; 2161 size_t len; 2162 2163 if (nvpair_value_uint64_array(pair, &value, &nelem) == 0) { 2164 if (*value != NULL) { 2165 len = strlen(name); 2166 i = 0; 2167 while (rep->extm[i].stm != 0 && i < sacnt) 2168 i++; 2169 rep->extm[i].stm = value[0]; 2170 rep->extm[i].nstm = value[1]; 2171 rep->extm[i].name = xmalloc(len + 1, rep); 2172 (void) strlcpy(rep->extm[i].name, name, len + 1); 2173 } 2174 } 2175 } 2176 2177 void 2178 format_time(const char *format, time_t sec) 2179 { 2180 2181 (void) strftime(time_buf, sizeof (time_buf), 2182 dcgettext(NULL, format, LC_TIME), 2183 localtime(&sec)); 2184 } 2185 2186 void 2187 format_etime(const char *format, time_t sec, time_t nsec) 2188 { 2189 char fmt_buf[FMTSIZE]; 2190 2191 (void) snprintf(fmt_buf, FMTSIZE, 2192 format, nsec); 2193 (void) strftime(time_buf, sizeof (time_buf), 2194 fmt_buf, localtime(&sec)); 2195 } 2196 2197 /* Format timestamp extended system attributes */ 2198 2199 void 2200 format_attrtime(struct lbuf *p) 2201 { 2202 int tmattr = 0; 2203 int i; 2204 2205 if (p->extm != NULL) { 2206 for (i = 0; i < sacnt; i++) { 2207 if (p->extm[i].name != NULL) { 2208 tmattr = 1; 2209 break; 2210 } 2211 } 2212 } 2213 if (tmattr) { 2214 if (Eflg) 2215 format_etime(FORMAT4, (time_t)p->extm[i].stm, 2216 (time_t)p->extm[i].nstm); 2217 else { 2218 if ((p->lmtime.tv_sec < year) || 2219 (p->lmtime.tv_sec > now)) 2220 format_time(FORMAT1, 2221 (time_t)p->extm[i].stm); 2222 else 2223 format_time(FORMAT2, 2224 (time_t)p->extm[i].stm); 2225 } 2226 } 2227 } 2228 2229 void 2230 print_time(struct lbuf *p) 2231 { 2232 int i = 0; 2233 2234 new_line(); 2235 if (Eflg) { 2236 format_etime(FORMAT4, p->lat.tv_sec, p->lat.tv_nsec); 2237 (void) printf(" timestamp: atime %s\n", 2238 time_buf); 2239 format_etime(FORMAT4, p->lct.tv_sec, p->lct.tv_nsec); 2240 (void) printf(" timestamp: ctime %s\n", 2241 time_buf); 2242 format_etime(FORMAT4, p->lmt.tv_sec, p->lmt.tv_nsec); 2243 (void) printf(" timestamp: mtime %s\n", 2244 time_buf); 2245 if (p->extm != NULL) { 2246 while (p->extm[i].nstm != 0 && i < sacnt) { 2247 format_etime(FORMAT4, p->extm[i].stm, 2248 p->extm[i].nstm); 2249 if (p->extm[i].name != NULL) { 2250 (void) printf(" timestamp:" 2251 " %s %s\n", 2252 p->extm[i].name, time_buf); 2253 } 2254 i++; 2255 } 2256 } 2257 } else { 2258 format_time(FORMAT3, p->lat.tv_sec); 2259 (void) printf(" timestamp: atime %s\n", 2260 time_buf); 2261 format_time(FORMAT3, p->lct.tv_sec); 2262 (void) printf(" timestamp: ctime %s\n", 2263 time_buf); 2264 format_time(FORMAT3, p->lmt.tv_sec); 2265 (void) printf(" timestamp: mtime %s\n", 2266 time_buf); 2267 if (p->extm != NULL) { 2268 while (p->extm[i].stm != 0 && i < sacnt) { 2269 format_time(FORMAT3, p->extm[i].stm); 2270 if (p->extm[i].name != NULL) { 2271 (void) printf(" timestamp:" 2272 " %s %s\n", 2273 p->extm[i].name, time_buf); 2274 } 2275 i++; 2276 } 2277 } 2278 } 2279 } 2280 2281 /* Free extended system attribute lists */ 2282 2283 void 2284 free_sysattr(struct lbuf *p) 2285 { 2286 int i; 2287 2288 if (p->exttr != NULL) { 2289 for (i = 0; i < sacnt; i++) { 2290 if (p->exttr[i].name != NULL) 2291 free(p->exttr[i].name); 2292 } 2293 free(p->exttr); 2294 } 2295 if (p->extm != NULL) { 2296 for (i = 0; i < sacnt; i++) { 2297 if (p->extm[i].name != NULL) 2298 free(p->extm[i].name); 2299 } 2300 free(p->extm); 2301 } 2302 } 2303 2304 /* Allocate extended system attribute list */ 2305 2306 void * 2307 xmalloc(size_t size, struct lbuf *p) 2308 { 2309 if ((p = malloc(size)) == NULL) { 2310 perror("ls"); 2311 free_sysattr(p); 2312 nvlist_free(response); 2313 exit(2); 2314 } 2315 return (p); 2316 } 2317