Changeset 1550 in kBuild for trunk/src/kmk/kmkbuiltin/rm.c
- Timestamp:
- Apr 22, 2008 11:40:38 PM (17 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/kmk/kmkbuiltin/rm.c
r1328 r1550 54 54 #include <errno.h> 55 55 #include <fcntl.h> 56 #ifdef DO_RMTREE 57 # include <fts.h> 58 #endif 56 #include <fts.h> 59 57 #include <grp.h> 60 58 #include <pwd.h> … … 70 68 #include "kmkbuiltin.h" 71 69 70 #if defined(__EMX__) || defined(_MSC_VER) 71 # define IS_SLASH(ch) ( (ch) == '/' || (ch) == '\\' ) 72 # define HAVE_DOS_PATHS 1 73 # define DEFAULT_REQUIRED_R_DEPTH 2 74 #else 75 # define IS_SLASH(ch) ( (ch) == '/' ) 76 # undef HAVE_DOS_PATHS 77 # define DEFAULT_REQUIRED_R_DEPTH 3 78 #endif 72 79 73 80 #ifdef __EMX__ … … 85 92 #endif 86 93 94 static int protectionflag; 87 95 static int dflag, eval, fflag, iflag, Pflag, vflag, Wflag, stdin_ok; 88 96 static uid_t uid; … … 94 102 { "help", no_argument, 0, 261 }, 95 103 { "version", no_argument, 0, 262 }, 104 { "disable-protection", no_argument, 0, 263 }, 96 105 { 0, 0, 0, 0 }, 97 106 }; … … 102 111 static void rm_file(char **); 103 112 static int rm_overwrite(char *, struct stat *); 104 #ifdef DO_RMTREE 105 static void rm_tree(char **); 106 #endif 113 static void rm_tree(char **, int); 114 static int count_path_components(const char *); 107 115 static int usage(FILE *); 108 116 … … 124 132 argv0 = argv[0]; 125 133 dflag = eval = fflag = iflag = Pflag = vflag = Wflag = stdin_ok = 0; 134 protectionflag = 1; 126 135 uid = 0; 127 136 … … 151 160 break; 152 161 case 'R': 162 #if 0 153 163 case 'r': /* Compatibility. */ 154 # ifdef DO_RMTREE164 #endif 155 165 rflag = 1; 156 166 break; 157 #else158 errno = EINVAL;159 return err(1, "Recursion is not supported!");160 #endif161 167 case 'v': 162 168 vflag = 1; … … 172 178 case 262: 173 179 return kbuild_version(argv[0]); 180 case 263: 181 protectionflag = 0; 182 break; 174 183 case '?': 175 184 default: … … 190 199 if (*argv) { 191 200 stdin_ok = isatty(STDIN_FILENO); 192 #ifdef DO_RMTREE 193 if (rflag) 194 rm_tree(argv); 195 else 196 #endif 201 if (rflag) { 202 /* 203 * Get options from the environment before doing rm_tree(). 204 */ 205 int required_r_depth = DEFAULT_REQUIRED_R_DEPTH; 206 int i; 207 for (i = 0; envp[i]; i++) { 208 if (!strncmp(envp[i], "KMK_RM_=", sizeof("KMK_RM_=") - 1)) { 209 if (!strncmp(envp[i], "KMK_RM_PROTECTION_DEPTH=", sizeof("KMK_RM_PROTECTION_DEPTH=") - 1)) { 210 char *ignore; 211 const char *val = envp[i] + sizeof("KMK_RM_PROTECTION_DEPTH=") - 1; 212 required_r_depth = isdigit(*val) ? strtol(val, &ignore, 0) : count_path_components(val); 213 if (required_r_depth < 1) 214 required_r_depth = DEFAULT_REQUIRED_R_DEPTH; 215 } else if (!strcmp(envp[i], "KMK_RM_DISABLE_PROTECTION=1")) { 216 protectionflag = 0; 217 } 218 } 219 } 220 221 if (!eval) 222 rm_tree(argv, required_r_depth); 223 } else { 197 224 rm_file(argv); 225 } 198 226 } 199 227 … … 201 229 } 202 230 203 #ifdef DO_RMTREE 231 /** 232 * Counts the components in the specified sub path. 233 * This is a helper for count_path_components. 234 * 235 * etc = 1 236 * etc/ = 1 237 * etc/x11 = 2 238 * and so and and so forth. 239 */ 240 static int 241 count_sub_path_components(const char *path, int depth) 242 { 243 for (;;) { 244 const char *end; 245 size_t len; 246 247 /* skip slashes. */ 248 while (IS_SLASH(*path)) 249 path++; 250 if (!*path) 251 break; 252 253 /* find end of component. */ 254 end = path; 255 while (!IS_SLASH(*end) && *end) 256 end++; 257 258 /* count it, checking for '..' and '.'. */ 259 len = end - path; 260 if (len == 2 && path[0] == '.' && path[1] == '.') { 261 if (depth > 0) 262 depth--; 263 } else if (len != 1 || path[0] != '.') { 264 depth++; 265 } 266 267 /* advance */ 268 if (!*end) 269 break; 270 path = end + 1; 271 } 272 return depth; 273 } 274 275 /** 276 * Parses the specified path counting the number of components 277 * relative to root. 278 * 279 * We don't check symbolic links and such, just some simple and cheap 280 * path parsing. 281 * 282 * @param path The path to process. 283 * 284 * @returns 0 or higher on success. 285 * On failure an error is printed, eval is set and -1 is returned. 286 */ 287 static int 288 count_path_components(const char *path) 289 { 290 int components = 0; 291 292 /* 293 * Deal with root, UNC, drive letter. 294 */ 295 #if defined(_MSC_VER) || defined(__OS2__) 296 if (IS_SLASH(path[0]) && IS_SLASH(path[1]) && !IS_SLASH(path[2])) { 297 /* skip the root - UNC */ 298 path += 3; 299 while (!IS_SLASH(*path) && *path) /* server name */ 300 path++; 301 while (IS_SLASH(*path)) 302 path++; 303 while (!IS_SLASH(*path) && *path) /* share name */ 304 path++; 305 while (IS_SLASH(*path)) 306 path++; 307 } else { 308 unsigned drive_letter = (unsigned)toupper(path[0]) - (unsigned)'A'; 309 if (drive_letter <= (unsigned)('Z' - 'A') && path[1] == ':') { 310 drive_letter++; /* A == 1 */ 311 } else { 312 drive_letter = 0; /* 0 == default */ 313 } 314 315 if (IS_SLASH(path[drive_letter ? 2 : 0])) { 316 /* 317 * Relative path, must count cwd depth first. 318 */ 319 char *tmp = _getdcwd(drive_letter, NULL, 32); 320 if (!tmp) { 321 eval = err(1, "_getdcwd"); 322 return -1; 323 } 324 325 if (IS_SLASH(cwd[0]) && IS_SLASH(cwd[1])) { 326 /* skip the root - UNC */ 327 tmp = &cwd[2]; 328 while (!IS_SLASH(*tmp) && *tmp) /* server name */ 329 tmp++; 330 while (IS_SLASH(*tmp)) 331 tmp++; 332 while (!IS_SLASH(*tmp) && *tmp) /* share name */ 333 tmp++; 334 } else { 335 /* skip the drive letter and while we're at it, the root slash too. */ 336 tmp = &cwd[1 + (cwd[1] == ':')]; 337 } 338 components = count_sub_path_components(tmp, 0); 339 free(tmp); 340 } else { 341 /* skip the drive letter and while we're at it, the root slash too. */ 342 path += drive_letter ? 3 : 1; 343 } 344 } 345 #else 346 if (!IS_SLASH(path[0])) { 347 /* 348 * Relative path, must count cwd depth first. 349 */ 350 char cwd[4096]; 351 if (!getcwd(cwd, sizeof(cwd))) { 352 eval = err(1, "getcwd"); 353 return -1; 354 } 355 components = count_sub_path_components(cwd, 0); 356 } 357 #endif 358 359 /* 360 * We're now past any UNC or drive letter crap, possibly positioned 361 * at the root slash or at the start of a path component at the 362 * given depth. Count the remainder. 363 */ 364 return count_sub_path_components(path, components); 365 } 366 367 368 /** 369 * Protect the upper layers of the file system against accidental 370 * or malicious deletetion attempt from within a makefile. 371 * 372 * @param path The path to check. 373 * @param required_depth The minimum number of components in the 374 * path counting from the root. 375 * 376 * @returns 0 on success. 377 * On failure an error is printed, eval is set and -1 is returned. 378 */ 379 static int 380 enforce_protection(const char *path, unsigned required_depth) 381 { 382 int components; 383 384 /* 385 * Count the path and compare it with the required depth. 386 */ 387 components = count_path_components(path); 388 if (components < 0) 389 return -1; 390 if (components < required_depth) { 391 eval = errx(1, "%s: protected", path); 392 return -1; 393 } 394 return 0; 395 } 396 204 397 static void 205 rm_tree(char **argv )398 rm_tree(char **argv, int required_r_depth) 206 399 { 207 400 FTS *fts; … … 210 403 int flags; 211 404 int rval; 405 406 /* 407 * Check up front before anything is deleted. This will not catch 408 * everything, but we'll check the individual items later. 409 */ 410 if (protectionflag) { 411 int i; 412 for (i = 0; argv[i]; i++) { 413 if (enforce_protection(argv[i], required_r_depth)) 414 return; 415 } 416 } 212 417 213 418 /* … … 245 450 case FTS_ERR: 246 451 eval = errx(1, "%s: %s", p->fts_path, strerror(p->fts_errno)); 247 return; 452 fts_close(fts); 453 return; 248 454 case FTS_NS: 249 455 /* … … 284 490 !check(p->fts_path, p->fts_accpath, p->fts_statp)) 285 491 continue; 492 } 493 494 /* 495 * Protect against deleting root files and directories. 496 */ 497 if (protectionflag && enforce_protection(p->fts_accpath, required_r_depth)) { 498 fts_close(fts); 499 return; 286 500 } 287 501 … … 358 572 if (errno) { 359 573 fprintf(stderr, "%s: fts_read: %s\n", argv0, strerror(errno)); 360 361 362 } 363 #endif /* DO_RMTREE */ 574 eval = 1; 575 } 576 fts_close(fts); 577 } 364 578 365 579 static void … … 577 791 complained = 0; 578 792 for (t = argv; *t;) { 793 #ifdef HAVE_DOS_PATHS 794 const char *tmp = p = *t; 795 while (*tmp) { 796 switch (*tmp) { 797 case '/': 798 case '\\': 799 case ':': 800 p = (char *)tmp + 1; 801 break; 802 } 803 tmp++; 804 } 805 #else 579 806 if ((p = strrchr(*t, '/')) != NULL) 580 807 ++p; 581 808 else 582 809 p = *t; 810 #endif 583 811 if (ISDOT(p)) { 584 812 if (!complained++) … … 596 824 usage(FILE *pf) 597 825 { 598 fprintf(pf, "usage: %s [-f | -i] [-dPRrvW] file ...\n" 599 " or: %s --help\n" 600 " or: %s --version\n", 601 g_progname, g_progname, g_progname); 826 fprintf(pf, 827 "usage: %s [-f | -i] [-dPRvW] [--disable-protection] file ...\n" 828 " or: %s --help\n" 829 " or: %s --version\n" 830 "\n" 831 "Options:\n" 832 " -f\n" 833 " Attempt to remove files without prompting, regardless of the file\n" 834 " permission. Ignore non-existing files. Overrides previous -i's.\n" 835 " -i\n" 836 " Prompt for each file. Always.\n" 837 " -d\n" 838 " Attempt to remove directories as well as other kinds of files.\n" 839 " -P\n" 840 " Overwrite regular files before deleting; three passes: ff,0,ff\n" 841 " -R\n" 842 " Attempt to remove the file hierachy rooted in each file argument.\n" 843 " This option implies -d and file protection.\n" 844 " -v\n" 845 " Be verbose, show files as they are removed.\n" 846 " -W\n" 847 " Undelete without files.\n" 848 " --disable-protection\n" 849 " Will disable the protection file protection applied by -R.\n" 850 "\n" 851 "Environment:\n" 852 " KMK_RM_DISABLE_PROTECTION\n" 853 " Same as --disable-protection.\n" 854 " KMK_RM_PROTECTION_DEPTH\n" 855 " Changes the protection depth, path or number. Default: %d\n" 856 "\n" 857 "The file protection of the top %d layers of the file hierarchy is there\n" 858 "to try prevent makefiles from doing bad things to your system. This\n" 859 "protection is not bulletproof, but should help prevent you from shooting\n" 860 "yourself in the foot. Not that it does NOT apply to normal file removal.\n" 861 , 862 g_progname, g_progname, g_progname, 863 DEFAULT_REQUIRED_R_DEPTH, DEFAULT_REQUIRED_R_DEPTH); 602 864 return EX_USAGE; 603 865 }
Note:
See TracChangeset
for help on using the changeset viewer.