10f66f451Sopenharmony_ci/* stat.c : display file or file system status 20f66f451Sopenharmony_ci * Copyright 2012 <warior.linux@gmail.com> 30f66f451Sopenharmony_ci * Copyright 2013 <anand.sinha85@gmail.com> 40f66f451Sopenharmony_ci 50f66f451Sopenharmony_ciUSE_STAT(NEWTOY(stat, "<1c:(format)fLt", TOYFLAG_BIN)) 60f66f451Sopenharmony_ci 70f66f451Sopenharmony_ciconfig STAT 80f66f451Sopenharmony_ci bool stat 90f66f451Sopenharmony_ci default y 100f66f451Sopenharmony_ci help 110f66f451Sopenharmony_ci usage: stat [-tfL] [-c FORMAT] FILE... 120f66f451Sopenharmony_ci 130f66f451Sopenharmony_ci Display status of files or filesystems. 140f66f451Sopenharmony_ci 150f66f451Sopenharmony_ci -c Output specified FORMAT string instead of default 160f66f451Sopenharmony_ci -f Display filesystem status instead of file status 170f66f451Sopenharmony_ci -L Follow symlinks 180f66f451Sopenharmony_ci -t terse (-c "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o") 190f66f451Sopenharmony_ci (with -f = -c "%n %i %l %t %s %S %b %f %a %c %d") 200f66f451Sopenharmony_ci 210f66f451Sopenharmony_ci The valid format escape sequences for files: 220f66f451Sopenharmony_ci %a Access bits (octal) |%A Access bits (flags)|%b Size/512 230f66f451Sopenharmony_ci %B Bytes per %b (512) |%C Security context |%d Device ID (dec) 240f66f451Sopenharmony_ci %D Device ID (hex) |%f All mode bits (hex)|%F File type 250f66f451Sopenharmony_ci %g Group ID |%G Group name |%h Hard links 260f66f451Sopenharmony_ci %i Inode |%m Mount point |%n Filename 270f66f451Sopenharmony_ci %N Long filename |%o I/O block size |%s Size (bytes) 280f66f451Sopenharmony_ci %t Devtype major (hex) |%T Devtype minor (hex)|%u User ID 290f66f451Sopenharmony_ci %U User name |%x Access time |%X Access unix time 300f66f451Sopenharmony_ci %y Modification time |%Y Mod unix time |%z Creation time 310f66f451Sopenharmony_ci %Z Creation unix time 320f66f451Sopenharmony_ci 330f66f451Sopenharmony_ci The valid format escape sequences for filesystems: 340f66f451Sopenharmony_ci %a Available blocks |%b Total blocks |%c Total inodes 350f66f451Sopenharmony_ci %d Free inodes |%f Free blocks |%i File system ID 360f66f451Sopenharmony_ci %l Max filename length |%n File name |%s Fragment size 370f66f451Sopenharmony_ci %S Best transfer size |%t FS type (hex) |%T FS type (driver name) 380f66f451Sopenharmony_ci*/ 390f66f451Sopenharmony_ci 400f66f451Sopenharmony_ci#define FOR_stat 410f66f451Sopenharmony_ci#include "toys.h" 420f66f451Sopenharmony_ci 430f66f451Sopenharmony_ciGLOBALS( 440f66f451Sopenharmony_ci char *c; 450f66f451Sopenharmony_ci 460f66f451Sopenharmony_ci union { 470f66f451Sopenharmony_ci struct stat st; 480f66f451Sopenharmony_ci struct statfs sf; 490f66f451Sopenharmony_ci } stat; 500f66f451Sopenharmony_ci char *file, *pattern; 510f66f451Sopenharmony_ci int patlen; 520f66f451Sopenharmony_ci) 530f66f451Sopenharmony_ci 540f66f451Sopenharmony_ci// Force numeric output to long long instead of manually typecasting everything 550f66f451Sopenharmony_ci// and safely parse length prefix 560f66f451Sopenharmony_cistatic void out(char c, long long val) 570f66f451Sopenharmony_ci{ 580f66f451Sopenharmony_ci sprintf(toybuf, "%.*sll%c", TT.patlen, TT.pattern, c); 590f66f451Sopenharmony_ci printf(toybuf, val); 600f66f451Sopenharmony_ci} 610f66f451Sopenharmony_ci 620f66f451Sopenharmony_ci// Output string with parsed length prefix 630f66f451Sopenharmony_cistatic void strout(char *val) 640f66f451Sopenharmony_ci{ 650f66f451Sopenharmony_ci sprintf(toybuf, "%.*ss", TT.patlen, TT.pattern); 660f66f451Sopenharmony_ci printf(toybuf, val); 670f66f451Sopenharmony_ci} 680f66f451Sopenharmony_ci 690f66f451Sopenharmony_cistatic void date_stat_format(struct timespec *ts) 700f66f451Sopenharmony_ci{ 710f66f451Sopenharmony_ci strout(format_iso_time(toybuf+128, sizeof(toybuf)-128, ts)); 720f66f451Sopenharmony_ci} 730f66f451Sopenharmony_ci 740f66f451Sopenharmony_cistatic void print_stat(char type) 750f66f451Sopenharmony_ci{ 760f66f451Sopenharmony_ci struct stat *stat = (struct stat *)&TT.stat; 770f66f451Sopenharmony_ci 780f66f451Sopenharmony_ci if (type == 'a') out('o', stat->st_mode&~S_IFMT); 790f66f451Sopenharmony_ci else if (type == 'A') { 800f66f451Sopenharmony_ci char str[11]; 810f66f451Sopenharmony_ci 820f66f451Sopenharmony_ci mode_to_string(stat->st_mode, str); 830f66f451Sopenharmony_ci strout(str); 840f66f451Sopenharmony_ci } else if (type == 'b') out('u', stat->st_blocks); 850f66f451Sopenharmony_ci else if (type == 'B') out('d', 512); 860f66f451Sopenharmony_ci else if (type == 'C') { 870f66f451Sopenharmony_ci char *context = NULL; 880f66f451Sopenharmony_ci 890f66f451Sopenharmony_ci strout(lsm_get_context(TT.file, &context) != -1 ? context : "?"); 900f66f451Sopenharmony_ci free(context); 910f66f451Sopenharmony_ci } else if (type == 'd') out('d', stat->st_dev); 920f66f451Sopenharmony_ci else if (type == 'D') out('x', stat->st_dev); 930f66f451Sopenharmony_ci else if (type == 'f') out('x', stat->st_mode); 940f66f451Sopenharmony_ci else if (type == 'F') { 950f66f451Sopenharmony_ci char *t = "character device\0directory\0block device\0" \ 960f66f451Sopenharmony_ci "regular file\0symbolic link\0socket\0FIFO (named pipe)"; 970f66f451Sopenharmony_ci int i, filetype = stat->st_mode & S_IFMT; 980f66f451Sopenharmony_ci 990f66f451Sopenharmony_ci for (i = 1; filetype != (i*8192) && i < 7; i++) t += strlen(t)+1; 1000f66f451Sopenharmony_ci if (!stat->st_size && filetype == S_IFREG) t = "regular empty file"; 1010f66f451Sopenharmony_ci strout(t); 1020f66f451Sopenharmony_ci } else if (type == 'g') out('u', stat->st_gid); 1030f66f451Sopenharmony_ci else if (type == 'G') strout(getgroupname(stat->st_gid)); 1040f66f451Sopenharmony_ci else if (type == 'h') out('u', stat->st_nlink); 1050f66f451Sopenharmony_ci else if (type == 'i') out('u', stat->st_ino); 1060f66f451Sopenharmony_ci else if (type == 'm') { 1070f66f451Sopenharmony_ci struct mtab_list *mt = xgetmountlist(0); 1080f66f451Sopenharmony_ci dev_t dev = stat->st_rdev ? stat->st_rdev : stat->st_dev; 1090f66f451Sopenharmony_ci 1100f66f451Sopenharmony_ci // This mount point could exist multiple times, so show oldest. 1110f66f451Sopenharmony_ci for (dlist_terminate(mt); mt; mt = mt->next) if (mt->stat.st_dev == dev) { 1120f66f451Sopenharmony_ci strout(mt->dir); 1130f66f451Sopenharmony_ci break; 1140f66f451Sopenharmony_ci } 1150f66f451Sopenharmony_ci llist_traverse(mt, free); 1160f66f451Sopenharmony_ci } else if (type == 'N') { 1170f66f451Sopenharmony_ci printf("%s", TT.file); 1180f66f451Sopenharmony_ci if (S_ISLNK(stat->st_mode)) 1190f66f451Sopenharmony_ci if (readlink0(TT.file, toybuf, sizeof(toybuf))) 1200f66f451Sopenharmony_ci printf(" -> '%s'", toybuf); 1210f66f451Sopenharmony_ci } else if (type == 'o') out('u', stat->st_blksize); 1220f66f451Sopenharmony_ci else if (type == 's') out('u', stat->st_size); 1230f66f451Sopenharmony_ci else if (type == 't') out('x', dev_major(stat->st_rdev)); 1240f66f451Sopenharmony_ci else if (type == 'T') out('x', dev_minor(stat->st_rdev)); 1250f66f451Sopenharmony_ci else if (type == 'u') out('u', stat->st_uid); 1260f66f451Sopenharmony_ci else if (type == 'U') strout(getusername(stat->st_uid)); 1270f66f451Sopenharmony_ci else if (type == 'x') date_stat_format(&stat->st_atim); 1280f66f451Sopenharmony_ci else if (type == 'X') out('u', stat->st_atime); 1290f66f451Sopenharmony_ci else if (type == 'y') date_stat_format(&stat->st_mtim); 1300f66f451Sopenharmony_ci else if (type == 'Y') out('u', stat->st_mtime); 1310f66f451Sopenharmony_ci else if (type == 'z') date_stat_format(&stat->st_ctim); 1320f66f451Sopenharmony_ci else if (type == 'Z') out('u', stat->st_ctime); 1330f66f451Sopenharmony_ci else putchar('?'); 1340f66f451Sopenharmony_ci} 1350f66f451Sopenharmony_ci 1360f66f451Sopenharmony_cistatic void print_statfs(char type) { 1370f66f451Sopenharmony_ci struct statfs *statfs = (struct statfs *)&TT.stat; 1380f66f451Sopenharmony_ci 1390f66f451Sopenharmony_ci if (type == 'a') out('u', statfs->f_bavail); 1400f66f451Sopenharmony_ci else if (type == 'b') out('u', statfs->f_blocks); 1410f66f451Sopenharmony_ci else if (type == 'c') out('u', statfs->f_files); 1420f66f451Sopenharmony_ci else if (type == 'd') out('u', statfs->f_ffree); 1430f66f451Sopenharmony_ci else if (type == 'f') out('u', statfs->f_bfree); 1440f66f451Sopenharmony_ci else if (type == 'l') { 1450f66f451Sopenharmony_ci#ifdef __APPLE__ 1460f66f451Sopenharmony_ci // TODO: move this into portability.c somehow, or just use this everywhere? 1470f66f451Sopenharmony_ci // (glibc and bionic will just re-do the statfs and return f_namelen.) 1480f66f451Sopenharmony_ci out('d', pathconf(TT.file, _PC_NAME_MAX)); 1490f66f451Sopenharmony_ci#else 1500f66f451Sopenharmony_ci out('d', statfs->f_namelen); 1510f66f451Sopenharmony_ci#endif 1520f66f451Sopenharmony_ci } else if (type == 't') out('x', statfs->f_type); 1530f66f451Sopenharmony_ci else if (type == 'T') { 1540f66f451Sopenharmony_ci char *s = "unknown"; 1550f66f451Sopenharmony_ci struct {unsigned num; char *name;} nn[] = { 1560f66f451Sopenharmony_ci {0xADFF, "affs"}, {0x5346544e, "ntfs"}, {0x1Cd1, "devpts"}, 1570f66f451Sopenharmony_ci {0x137D, "ext"}, {0xEF51, "ext2"}, {0xEF53, "ext3"}, 1580f66f451Sopenharmony_ci {0x1BADFACE, "bfs"}, {0x9123683E, "btrfs"}, {0x28cd3d45, "cramfs"}, 1590f66f451Sopenharmony_ci {0x3153464a, "jfs"}, {0x7275, "romfs"}, {0x01021994, "tmpfs"}, 1600f66f451Sopenharmony_ci {0x3434, "nilfs"}, {0x6969, "nfs"}, {0x9fa0, "proc"}, 1610f66f451Sopenharmony_ci {0x534F434B, "sockfs"}, {0x62656572, "sysfs"}, {0x517B, "smb"}, 1620f66f451Sopenharmony_ci {0x4d44, "msdos"}, {0x4006, "fat"}, {0x43415d53, "smackfs"}, 1630f66f451Sopenharmony_ci {0x73717368, "squashfs"} 1640f66f451Sopenharmony_ci }; 1650f66f451Sopenharmony_ci int i; 1660f66f451Sopenharmony_ci 1670f66f451Sopenharmony_ci for (i=0; i<ARRAY_LEN(nn); i++) 1680f66f451Sopenharmony_ci if (nn[i].num == statfs->f_type) s = nn[i].name; 1690f66f451Sopenharmony_ci strout(s); 1700f66f451Sopenharmony_ci } else if (type == 'i') { 1710f66f451Sopenharmony_ci int *val = (int *) &statfs->f_fsid; 1720f66f451Sopenharmony_ci char buf[32]; 1730f66f451Sopenharmony_ci 1740f66f451Sopenharmony_ci sprintf(buf, "%08x%08x", val[0], val[1]); 1750f66f451Sopenharmony_ci strout(buf); 1760f66f451Sopenharmony_ci } else if (type == 's') out('d', statfs->f_frsize); 1770f66f451Sopenharmony_ci else if (type == 'S') out('d', statfs->f_bsize); 1780f66f451Sopenharmony_ci else strout("?"); 1790f66f451Sopenharmony_ci} 1800f66f451Sopenharmony_ci 1810f66f451Sopenharmony_civoid stat_main(void) 1820f66f451Sopenharmony_ci{ 1830f66f451Sopenharmony_ci int flagf = FLAG(f), i; 1840f66f451Sopenharmony_ci char *format, *f; 1850f66f451Sopenharmony_ci 1860f66f451Sopenharmony_ci if (FLAG(t)) format = flagf 1870f66f451Sopenharmony_ci ? "%n %i %l %t %s %S %b %f %a %c %d" 1880f66f451Sopenharmony_ci : "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o"; 1890f66f451Sopenharmony_ci else format = flagf 1900f66f451Sopenharmony_ci ? " File: \"%n\"\n ID: %i Namelen: %l Type: %T\n" 1910f66f451Sopenharmony_ci "Block Size: %s Fundamental block size: %S\n" 1920f66f451Sopenharmony_ci "Blocks: Total: %b\tFree: %f\tAvailable: %a\n" 1930f66f451Sopenharmony_ci "Inodes: Total: %c\tFree: %d" 1940f66f451Sopenharmony_ci : " File: %N\n Size: %s\t Blocks: %b\t IO Blocks: %B\t %F\n" 1950f66f451Sopenharmony_ci "Device: %Dh/%dd\t Inode: %i\t Links: %h\t Device type: %t,%T\n" 1960f66f451Sopenharmony_ci "Access: (%04a/%A)\tUid: (%5u/%8U)\tGid: (%5g/%8G)\n" 1970f66f451Sopenharmony_ci "Access: %x\nModify: %y\nChange: %z"; 1980f66f451Sopenharmony_ci 1990f66f451Sopenharmony_ci if (FLAG(c)) format = TT.c; 2000f66f451Sopenharmony_ci 2010f66f451Sopenharmony_ci // loop through files listed on command line 2020f66f451Sopenharmony_ci for (i = 0; toys.optargs[i]; i++) { 2030f66f451Sopenharmony_ci 2040f66f451Sopenharmony_ci // stat the file or filesystem 2050f66f451Sopenharmony_ci TT.file = toys.optargs[i]; 2060f66f451Sopenharmony_ci if (flagf && !statfs(TT.file, (void *)&TT.stat)); 2070f66f451Sopenharmony_ci else if (flagf || (FLAG(L) ? stat : lstat)(TT.file, (void *)&TT.stat)) { 2080f66f451Sopenharmony_ci perror_msg("'%s'", TT.file); 2090f66f451Sopenharmony_ci continue; 2100f66f451Sopenharmony_ci } 2110f66f451Sopenharmony_ci 2120f66f451Sopenharmony_ci // parse format and print what it says 2130f66f451Sopenharmony_ci for (f = format; *f; f++) { 2140f66f451Sopenharmony_ci if (*f != '%' || !f[1]) putchar(*f); 2150f66f451Sopenharmony_ci else if (f[1]=='%') putchar(*f++); 2160f66f451Sopenharmony_ci else { 2170f66f451Sopenharmony_ci f = next_printf(f, &TT.pattern); 2180f66f451Sopenharmony_ci TT.patlen = f-TT.pattern; 2190f66f451Sopenharmony_ci if (!*f || TT.patlen>99) error_exit("bad %s", TT.pattern); 2200f66f451Sopenharmony_ci if (*f == 'n') strout(TT.file); 2210f66f451Sopenharmony_ci else if (flagf) print_statfs(*f); 2220f66f451Sopenharmony_ci else print_stat(*f); 2230f66f451Sopenharmony_ci } 2240f66f451Sopenharmony_ci } 2250f66f451Sopenharmony_ci xputc('\n'); 2260f66f451Sopenharmony_ci } 2270f66f451Sopenharmony_ci} 228