vdu.c (7966B)
1 // $Id$ --*- c -*-- 2 3 // Copyright (C) 2006 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de> 4 // 5 // This program is free software; you can redistribute it and/or modify 6 // it under the terms of the GNU General Public License as published by 7 // the Free Software Foundation; version 2 of the License. 8 // 9 // This program is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with this program; if not, write to the Free Software 16 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 17 18 19 #ifdef HAVE_CONFIG_H 20 # include <config.h> 21 #endif 22 23 #include "util.h" 24 #include <lib/vserver.h> 25 #include <lib/fmt.h> 26 27 #include <stdlib.h> 28 #include <getopt.h> 29 #include <stdint.h> 30 #include <errno.h> 31 #include <sys/stat.h> 32 #include <dirent.h> 33 #include <fcntl.h> 34 35 #define ENSC_WRAPPERS_PREFIX "vdu: " 36 #define ENSC_WRAPPERS_VSERVER 1 37 #define ENSC_WRAPPERS_UNISTD 1 38 #define ENSC_WRAPPERS_DIRENT 1 39 #define ENSC_WRAPPERS_FCNTL 1 40 #define ENSC_WRAPPERS_STAT 1 41 #include <wrappers.h> 42 43 #define CMD_HELP 0x1000 44 #define CMD_VERSION 0x1001 45 #define CMD_XID 0x2000 46 #define CMD_SPACE 0x2001 47 #define CMD_INODES 0x2002 48 #define CMD_SCRIPT 0x2003 49 #define CMD_BLOCKSIZE 0x2005 50 51 int wrapper_exit_code = 1; 52 53 struct option const 54 CMDLINE_OPTIONS[] = { 55 { "help", no_argument, 0, CMD_HELP }, 56 { "version", no_argument, 0, CMD_VERSION }, 57 { "xid", required_argument, 0, CMD_XID }, 58 { "space", no_argument, 0, CMD_SPACE }, 59 { "inodes", no_argument, 0, CMD_INODES }, 60 { "script", no_argument, 0, CMD_SCRIPT }, 61 { "blocksize", required_argument, 0, CMD_BLOCKSIZE }, 62 {0,0,0,0} 63 }; 64 65 struct Arguments { 66 xid_t xid; 67 bool space; 68 bool inodes; 69 bool script; 70 unsigned long blocksize; 71 }; 72 73 struct Result { 74 uint_least64_t blocks; 75 uint_least64_t inodes; 76 }; 77 78 struct TraversalParams { 79 struct Arguments const * const args; 80 struct Result * const result; 81 }; 82 83 static void 84 showHelp(int fd, char const *cmd, int res) 85 { 86 WRITE_MSG(fd, "Usage:\n "); 87 WRITE_STR(fd, cmd); 88 WRITE_MSG(fd, 89 " --xid <xid> (--space|--inodes) [--blocksize <blocksize>] [--script] <directory>*\n" 90 "\n" 91 "Please report bugs to " PACKAGE_BUGREPORT "\n"); 92 93 exit(res); 94 } 95 96 static void 97 showVersion() 98 { 99 WRITE_MSG(1, 100 "vdu " VERSION " -- calculates the size of a directory\n" 101 "This program is part of " PACKAGE_STRING "\n\n" 102 "Copyright (C) 2006 Enrico Scholz\n" 103 VERSION_COPYRIGHT_DISCLAIMER); 104 exit(0); 105 } 106 107 /* basic hash table implementation for inode tracking */ 108 #define HASH_SIZE 103 109 typedef struct hash_entry { 110 struct hash_entry *next; 111 ino_t inode; 112 } hash_entry; 113 114 typedef struct hash_table { 115 hash_entry *entries[HASH_SIZE]; 116 } hash_table; 117 118 static hash_table ht; 119 120 static void 121 hash_init(void) 122 { 123 memset(&ht, 0, sizeof(hash_table)); 124 } 125 126 static void 127 hash_free(void) 128 { 129 int i; 130 hash_entry *e, *p; 131 for (i = 0; i < HASH_SIZE; i++) { 132 for (e = ht.entries[i], p = NULL; e; e = e->next) { 133 free(p); 134 p = e; 135 } 136 free(p); 137 } 138 } 139 140 static int 141 hash_insert(ino_t inode) 142 { 143 hash_entry *e, *p; 144 unsigned int hashval = inode % HASH_SIZE; 145 146 /* no one else here */ 147 if (ht.entries[hashval] == NULL) { 148 ht.entries[hashval] = malloc(sizeof(hash_entry)); 149 ht.entries[hashval]->next = NULL; 150 ht.entries[hashval]->inode = inode; 151 return 0; 152 } 153 154 for (e = ht.entries[hashval], p = NULL; e; e = e->next) { 155 /* already in the hash table */ 156 if (e->inode == inode) 157 return -1; 158 else if (e->inode > inode) { 159 /* we're first */ 160 if (p == NULL) { 161 ht.entries[hashval] = malloc(sizeof(hash_entry)); 162 ht.entries[hashval]->next = e; 163 ht.entries[hashval]->inode = inode; 164 } 165 /* we're in the middle */ 166 else { 167 p->next = malloc(sizeof(hash_entry)); 168 p->next->next = e; 169 p->next->inode = inode; 170 } 171 return 0; 172 } 173 p = e; 174 } 175 /* we're last */ 176 p->next = malloc(sizeof(hash_entry)); 177 p->next->next = NULL; 178 p->next->inode = inode; 179 180 return 0; 181 } 182 183 static void 184 visitDirEntry(char const *name, dev_t const dir_dev, 185 struct TraversalParams *params); 186 187 static void 188 visitDir(char const *name, struct stat const *expected_stat, struct TraversalParams *params) 189 { 190 int fd = Eopen(".", O_RDONLY|O_DIRECTORY, 0); 191 DIR * dir; 192 193 EsafeChdir(name, expected_stat); 194 195 dir = Eopendir("."); 196 197 for (;;) { 198 struct dirent *ent = Ereaddir(dir); 199 if (ent==0) break; 200 201 if (isDotfile(ent->d_name)) continue; 202 visitDirEntry(ent->d_name, expected_stat->st_dev, params); 203 } 204 205 Eclosedir(dir); 206 207 Efchdir(fd); 208 Eclose(fd); 209 } 210 211 static void 212 visitDirEntry(char const *name, dev_t const dir_dev, 213 struct TraversalParams *params) 214 { 215 struct stat st; 216 xid_t xid; 217 218 ElstatD(name, &st); 219 220 xid = vc_getfilecontext(name); 221 if (xid == params->args->xid && 222 (st.st_nlink == 1 || hash_insert(st.st_ino) != -1)) { 223 params->result->blocks += st.st_blocks; 224 params->result->inodes += 1; 225 } 226 227 if (S_ISDIR(st.st_mode) && dir_dev == st.st_dev) 228 visitDir(name, &st, params); 229 } 230 231 static void 232 visitDirStart(char const *name, struct TraversalParams *params) 233 { 234 struct stat st; 235 int fd = Eopen(".", O_RDONLY|O_DIRECTORY, 0); 236 237 Estat(name, &st); 238 Echdir(name); 239 240 visitDirEntry(".", st.st_dev, params); 241 242 Efchdir(fd); 243 Eclose(fd); 244 } 245 246 int main(int argc, char *argv[]) 247 { 248 struct Arguments args = { 249 .xid = VC_NOCTX, 250 .space = false, 251 .inodes = false, 252 .script = false, 253 .blocksize = 1024, 254 }; 255 256 while (1) { 257 int c = getopt_long(argc, argv, "+", CMDLINE_OPTIONS, 0); 258 if (c==-1) break; 259 260 switch (c) { 261 case CMD_HELP : showHelp(1, argv[0], 0); 262 case CMD_VERSION : showVersion(); 263 case CMD_XID : args.xid = Evc_xidopt2xid(optarg,true); break; 264 case CMD_SPACE : args.space = true; break; 265 case CMD_INODES : args.inodes = true; break; 266 case CMD_SCRIPT : args.script = true; break; 267 case CMD_BLOCKSIZE: 268 if (!isNumberUnsigned(optarg, &args.blocksize, false)) { 269 WRITE_MSG(2, "Invalid block size argument: '"); 270 WRITE_STR(2, optarg); 271 WRITE_MSG(2, "'; try '--help' for more information\n"); 272 return EXIT_FAILURE; 273 } 274 break; 275 default : 276 WRITE_MSG(2, "Try '"); 277 WRITE_STR(2, argv[0]); 278 WRITE_MSG(2, " --help' for more information.\n"); 279 return 255; 280 break; 281 } 282 } 283 284 if (args.xid==VC_NOCTX) 285 WRITE_MSG(2, "No xid specified; try '--help' for more information\n"); 286 else if (!args.space && !args.inodes) 287 WRITE_MSG(2, "Must specify --space or --inodes; try '--help' for more information\n"); 288 else if (optind==argc) 289 WRITE_MSG(2, "No directory specified; try '--help' for more information\n"); 290 else { 291 int i; 292 size_t len; 293 struct Result result; 294 struct TraversalParams params = { 295 .args = &args, 296 .result = &result 297 }; 298 299 for (i = optind; i < argc; i++) { 300 uint_least64_t size; 301 char buf[sizeof(size)*3 + 3]; 302 char const * delim = ""; 303 304 result.blocks = 0; 305 result.inodes = 0; 306 307 hash_init(); 308 visitDirStart(argv[i], ¶ms); 309 hash_free(); 310 311 if (!args.script) { 312 WRITE_STR(1, argv[i]); 313 WRITE_MSG(1, " "); 314 } 315 316 if (args.space) { 317 len = utilvserver_fmt_uint64(buf, result.blocks*512 / args.blocksize); 318 if (*delim) WRITE_STR(1, delim); 319 Vwrite(1, buf, len); 320 delim = " "; 321 } 322 if (args.inodes) { 323 len = utilvserver_fmt_uint64(buf, result.inodes); 324 if (*delim) WRITE_STR(1, delim); 325 Vwrite(1, buf, len); 326 delim = " "; 327 } 328 WRITE_MSG(1, "\n"); 329 } 330 return EXIT_SUCCESS; 331 } 332 333 return EXIT_FAILURE; 334 }