1/* stat.c : display file or file system status 2 * Copyright 2012 <warior.linux@gmail.com> 3 * Copyright 2013 <anand.sinha85@gmail.com> 4 5USE_STAT(NEWTOY(stat, "<1c:(format)fLt", TOYFLAG_BIN)) 6 7config STAT 8 bool stat 9 default y 10 help 11 usage: stat [-tfL] [-c FORMAT] FILE... 12 13 Display status of files or filesystems. 14 15 -c Output specified FORMAT string instead of default 16 -f Display filesystem status instead of file status 17 -L Follow symlinks 18 -t terse (-c "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o") 19 (with -f = -c "%n %i %l %t %s %S %b %f %a %c %d") 20 21 The valid format escape sequences for files: 22 %a Access bits (octal) |%A Access bits (flags)|%b Size/512 23 %B Bytes per %b (512) |%C Security context |%d Device ID (dec) 24 %D Device ID (hex) |%f All mode bits (hex)|%F File type 25 %g Group ID |%G Group name |%h Hard links 26 %i Inode |%m Mount point |%n Filename 27 %N Long filename |%o I/O block size |%s Size (bytes) 28 %t Devtype major (hex) |%T Devtype minor (hex)|%u User ID 29 %U User name |%x Access time |%X Access unix time 30 %y Modification time |%Y Mod unix time |%z Creation time 31 %Z Creation unix time 32 33 The valid format escape sequences for filesystems: 34 %a Available blocks |%b Total blocks |%c Total inodes 35 %d Free inodes |%f Free blocks |%i File system ID 36 %l Max filename length |%n File name |%s Fragment size 37 %S Best transfer size |%t FS type (hex) |%T FS type (driver name) 38*/ 39 40#define FOR_stat 41#include "toys.h" 42 43GLOBALS( 44 char *c; 45 46 union { 47 struct stat st; 48 struct statfs sf; 49 } stat; 50 char *file, *pattern; 51 int patlen; 52) 53 54// Force numeric output to long long instead of manually typecasting everything 55// and safely parse length prefix 56static void out(char c, long long val) 57{ 58 sprintf(toybuf, "%.*sll%c", TT.patlen, TT.pattern, c); 59 printf(toybuf, val); 60} 61 62// Output string with parsed length prefix 63static void strout(char *val) 64{ 65 sprintf(toybuf, "%.*ss", TT.patlen, TT.pattern); 66 printf(toybuf, val); 67} 68 69static void date_stat_format(struct timespec *ts) 70{ 71 strout(format_iso_time(toybuf+128, sizeof(toybuf)-128, ts)); 72} 73 74static void print_stat(char type) 75{ 76 struct stat *stat = (struct stat *)&TT.stat; 77 78 if (type == 'a') out('o', stat->st_mode&~S_IFMT); 79 else if (type == 'A') { 80 char str[11]; 81 82 mode_to_string(stat->st_mode, str); 83 strout(str); 84 } else if (type == 'b') out('u', stat->st_blocks); 85 else if (type == 'B') out('d', 512); 86 else if (type == 'C') { 87 char *context = NULL; 88 89 strout(lsm_get_context(TT.file, &context) != -1 ? context : "?"); 90 free(context); 91 } else if (type == 'd') out('d', stat->st_dev); 92 else if (type == 'D') out('x', stat->st_dev); 93 else if (type == 'f') out('x', stat->st_mode); 94 else if (type == 'F') { 95 char *t = "character device\0directory\0block device\0" \ 96 "regular file\0symbolic link\0socket\0FIFO (named pipe)"; 97 int i, filetype = stat->st_mode & S_IFMT; 98 99 for (i = 1; filetype != (i*8192) && i < 7; i++) t += strlen(t)+1; 100 if (!stat->st_size && filetype == S_IFREG) t = "regular empty file"; 101 strout(t); 102 } else if (type == 'g') out('u', stat->st_gid); 103 else if (type == 'G') strout(getgroupname(stat->st_gid)); 104 else if (type == 'h') out('u', stat->st_nlink); 105 else if (type == 'i') out('u', stat->st_ino); 106 else if (type == 'm') { 107 struct mtab_list *mt = xgetmountlist(0); 108 dev_t dev = stat->st_rdev ? stat->st_rdev : stat->st_dev; 109 110 // This mount point could exist multiple times, so show oldest. 111 for (dlist_terminate(mt); mt; mt = mt->next) if (mt->stat.st_dev == dev) { 112 strout(mt->dir); 113 break; 114 } 115 llist_traverse(mt, free); 116 } else if (type == 'N') { 117 printf("%s", TT.file); 118 if (S_ISLNK(stat->st_mode)) 119 if (readlink0(TT.file, toybuf, sizeof(toybuf))) 120 printf(" -> '%s'", toybuf); 121 } else if (type == 'o') out('u', stat->st_blksize); 122 else if (type == 's') out('u', stat->st_size); 123 else if (type == 't') out('x', dev_major(stat->st_rdev)); 124 else if (type == 'T') out('x', dev_minor(stat->st_rdev)); 125 else if (type == 'u') out('u', stat->st_uid); 126 else if (type == 'U') strout(getusername(stat->st_uid)); 127 else if (type == 'x') date_stat_format(&stat->st_atim); 128 else if (type == 'X') out('u', stat->st_atime); 129 else if (type == 'y') date_stat_format(&stat->st_mtim); 130 else if (type == 'Y') out('u', stat->st_mtime); 131 else if (type == 'z') date_stat_format(&stat->st_ctim); 132 else if (type == 'Z') out('u', stat->st_ctime); 133 else putchar('?'); 134} 135 136static void print_statfs(char type) { 137 struct statfs *statfs = (struct statfs *)&TT.stat; 138 139 if (type == 'a') out('u', statfs->f_bavail); 140 else if (type == 'b') out('u', statfs->f_blocks); 141 else if (type == 'c') out('u', statfs->f_files); 142 else if (type == 'd') out('u', statfs->f_ffree); 143 else if (type == 'f') out('u', statfs->f_bfree); 144 else if (type == 'l') { 145#ifdef __APPLE__ 146 // TODO: move this into portability.c somehow, or just use this everywhere? 147 // (glibc and bionic will just re-do the statfs and return f_namelen.) 148 out('d', pathconf(TT.file, _PC_NAME_MAX)); 149#else 150 out('d', statfs->f_namelen); 151#endif 152 } else if (type == 't') out('x', statfs->f_type); 153 else if (type == 'T') { 154 char *s = "unknown"; 155 struct {unsigned num; char *name;} nn[] = { 156 {0xADFF, "affs"}, {0x5346544e, "ntfs"}, {0x1Cd1, "devpts"}, 157 {0x137D, "ext"}, {0xEF51, "ext2"}, {0xEF53, "ext3"}, 158 {0x1BADFACE, "bfs"}, {0x9123683E, "btrfs"}, {0x28cd3d45, "cramfs"}, 159 {0x3153464a, "jfs"}, {0x7275, "romfs"}, {0x01021994, "tmpfs"}, 160 {0x3434, "nilfs"}, {0x6969, "nfs"}, {0x9fa0, "proc"}, 161 {0x534F434B, "sockfs"}, {0x62656572, "sysfs"}, {0x517B, "smb"}, 162 {0x4d44, "msdos"}, {0x4006, "fat"}, {0x43415d53, "smackfs"}, 163 {0x73717368, "squashfs"} 164 }; 165 int i; 166 167 for (i=0; i<ARRAY_LEN(nn); i++) 168 if (nn[i].num == statfs->f_type) s = nn[i].name; 169 strout(s); 170 } else if (type == 'i') { 171 int *val = (int *) &statfs->f_fsid; 172 char buf[32]; 173 174 sprintf(buf, "%08x%08x", val[0], val[1]); 175 strout(buf); 176 } else if (type == 's') out('d', statfs->f_frsize); 177 else if (type == 'S') out('d', statfs->f_bsize); 178 else strout("?"); 179} 180 181void stat_main(void) 182{ 183 int flagf = FLAG(f), i; 184 char *format, *f; 185 186 if (FLAG(t)) format = flagf 187 ? "%n %i %l %t %s %S %b %f %a %c %d" 188 : "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o"; 189 else format = flagf 190 ? " File: \"%n\"\n ID: %i Namelen: %l Type: %T\n" 191 "Block Size: %s Fundamental block size: %S\n" 192 "Blocks: Total: %b\tFree: %f\tAvailable: %a\n" 193 "Inodes: Total: %c\tFree: %d" 194 : " File: %N\n Size: %s\t Blocks: %b\t IO Blocks: %B\t %F\n" 195 "Device: %Dh/%dd\t Inode: %i\t Links: %h\t Device type: %t,%T\n" 196 "Access: (%04a/%A)\tUid: (%5u/%8U)\tGid: (%5g/%8G)\n" 197 "Access: %x\nModify: %y\nChange: %z"; 198 199 if (FLAG(c)) format = TT.c; 200 201 // loop through files listed on command line 202 for (i = 0; toys.optargs[i]; i++) { 203 204 // stat the file or filesystem 205 TT.file = toys.optargs[i]; 206 if (flagf && !statfs(TT.file, (void *)&TT.stat)); 207 else if (flagf || (FLAG(L) ? stat : lstat)(TT.file, (void *)&TT.stat)) { 208 perror_msg("'%s'", TT.file); 209 continue; 210 } 211 212 // parse format and print what it says 213 for (f = format; *f; f++) { 214 if (*f != '%' || !f[1]) putchar(*f); 215 else if (f[1]=='%') putchar(*f++); 216 else { 217 f = next_printf(f, &TT.pattern); 218 TT.patlen = f-TT.pattern; 219 if (!*f || TT.patlen>99) error_exit("bad %s", TT.pattern); 220 if (*f == 'n') strout(TT.file); 221 else if (flagf) print_statfs(*f); 222 else print_stat(*f); 223 } 224 } 225 xputc('\n'); 226 } 227} 228