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