1*da2e3ebdSchin /*********************************************************************** 2*da2e3ebdSchin * * 3*da2e3ebdSchin * This software is part of the ast package * 4*da2e3ebdSchin * Copyright (c) 1992-2007 AT&T Knowledge Ventures * 5*da2e3ebdSchin * and is licensed under the * 6*da2e3ebdSchin * Common Public License, Version 1.0 * 7*da2e3ebdSchin * by AT&T Knowledge Ventures * 8*da2e3ebdSchin * * 9*da2e3ebdSchin * A copy of the License is available at * 10*da2e3ebdSchin * http://www.opensource.org/licenses/cpl1.0.txt * 11*da2e3ebdSchin * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * 12*da2e3ebdSchin * * 13*da2e3ebdSchin * Information and Software Systems Research * 14*da2e3ebdSchin * AT&T Research * 15*da2e3ebdSchin * Florham Park NJ * 16*da2e3ebdSchin * * 17*da2e3ebdSchin * Glenn Fowler <gsf@research.att.com> * 18*da2e3ebdSchin * David Korn <dgk@research.att.com> * 19*da2e3ebdSchin * * 20*da2e3ebdSchin ***********************************************************************/ 21*da2e3ebdSchin #pragma prototyped 22*da2e3ebdSchin /* 23*da2e3ebdSchin * Glenn Fowler 24*da2e3ebdSchin * AT&T Research 25*da2e3ebdSchin * 26*da2e3ebdSchin * rm [-fir] [file ...] 27*da2e3ebdSchin */ 28*da2e3ebdSchin 29*da2e3ebdSchin static const char usage[] = 30*da2e3ebdSchin "[-?\n@(#)$Id: rm (AT&T Research) 2006-11-21 $\n]" 31*da2e3ebdSchin USAGE_LICENSE 32*da2e3ebdSchin "[+NAME?rm - remove files]" 33*da2e3ebdSchin "[+DESCRIPTION?\brm\b removes the named \afile\a arguments. By default it" 34*da2e3ebdSchin " does not remove directories. If a file is unwritable, the" 35*da2e3ebdSchin " standard input is a terminal, and the \b--force\b option is not" 36*da2e3ebdSchin " given, \brm\b prompts the user for whether to remove the file." 37*da2e3ebdSchin " An affirmative response (\by\b or \bY\b) removes the file, a quit" 38*da2e3ebdSchin " response (\bq\b or \bQ\b) causes \brm\b to exit immediately, and" 39*da2e3ebdSchin " all other responses skip the current file.]" 40*da2e3ebdSchin 41*da2e3ebdSchin "[c|F:clear|clobber?Clear the contents of each file before removing by" 42*da2e3ebdSchin " writing a 0 filled buffer the same size as the file, executing" 43*da2e3ebdSchin " \bfsync\b(2) and closing before attempting to remove. Implemented" 44*da2e3ebdSchin " only on systems that support \bfsync\b(2).]" 45*da2e3ebdSchin "[d:directory?\bremove\b(3) (or \bunlink\b(2)) directories rather than" 46*da2e3ebdSchin " \brmdir\b(2), and don't require that they be empty before removal." 47*da2e3ebdSchin " The caller requires sufficient privilege, not to mention a strong" 48*da2e3ebdSchin " constitution, to use this option. Even though the directory must" 49*da2e3ebdSchin " not be empty, \brm\b still attempts to empty it before removal.]" 50*da2e3ebdSchin "[f:force?Ignore nonexistent files and never prompt the user.]" 51*da2e3ebdSchin "[i:interactive|prompt?Prompt whether to remove each file." 52*da2e3ebdSchin " An affirmative response (\by\b or \bY\b) removes the file, a quit" 53*da2e3ebdSchin " response (\bq\b or \bQ\b) causes \brm\b to exit immediately, and" 54*da2e3ebdSchin " all other responses skip the current file.]" 55*da2e3ebdSchin "[r|R:recursive?Remove the contents of directories recursively.]" 56*da2e3ebdSchin "[u:unconditional?If \b--recursive\b and \b--force\b are also enabled then" 57*da2e3ebdSchin " the owner read, write and execute modes are enabled (if not already" 58*da2e3ebdSchin " enabled) for each directory before attempting to remove directory" 59*da2e3ebdSchin " contents.]" 60*da2e3ebdSchin "[v:verbose?Print the name of each file before removing it.]" 61*da2e3ebdSchin 62*da2e3ebdSchin "\n" 63*da2e3ebdSchin "\nfile ...\n" 64*da2e3ebdSchin "\n" 65*da2e3ebdSchin 66*da2e3ebdSchin "[+SEE ALSO?\bmv\b(1), \brmdir\b(2), \bunlink\b(2), \bremove\b(3)]" 67*da2e3ebdSchin ; 68*da2e3ebdSchin 69*da2e3ebdSchin #include <cmd.h> 70*da2e3ebdSchin #include <ls.h> 71*da2e3ebdSchin #include <fts.h> 72*da2e3ebdSchin #include <fs3d.h> 73*da2e3ebdSchin 74*da2e3ebdSchin #define RM_ENTRY 1 75*da2e3ebdSchin 76*da2e3ebdSchin #define beenhere(f) (((f)->fts_number>>1)==(f)->fts_statp->st_nlink) 77*da2e3ebdSchin #define isempty(f) (!((f)->fts_number&RM_ENTRY)) 78*da2e3ebdSchin #define nonempty(f) ((f)->fts_parent->fts_number|=RM_ENTRY) 79*da2e3ebdSchin #define pathchunk(n) roundof(n,1024) 80*da2e3ebdSchin #define retry(f) ((f)->fts_number=((f)->fts_statp->st_nlink<<1)) 81*da2e3ebdSchin 82*da2e3ebdSchin typedef struct State_s /* program state */ 83*da2e3ebdSchin { 84*da2e3ebdSchin int clobber; /* clear out file data first */ 85*da2e3ebdSchin int directory; /* remove(dir) not rmdir(dir) */ 86*da2e3ebdSchin int force; /* force actions */ 87*da2e3ebdSchin int fs3d; /* 3d enabled */ 88*da2e3ebdSchin int interactive; /* prompt for approval */ 89*da2e3ebdSchin int recursive; /* remove subtrees too */ 90*da2e3ebdSchin int terminal; /* attached to terminal */ 91*da2e3ebdSchin int uid; /* caller uid */ 92*da2e3ebdSchin int unconditional; /* enable dir rwx on preorder */ 93*da2e3ebdSchin int verbose; /* display each file */ 94*da2e3ebdSchin #if _lib_fsync 95*da2e3ebdSchin char buf[SF_BUFSIZE];/* clobber buffer */ 96*da2e3ebdSchin #endif 97*da2e3ebdSchin } State_t; 98*da2e3ebdSchin 99*da2e3ebdSchin /* 100*da2e3ebdSchin * remove a single file 101*da2e3ebdSchin */ 102*da2e3ebdSchin 103*da2e3ebdSchin static int 104*da2e3ebdSchin rm(State_t* state, register FTSENT* ent) 105*da2e3ebdSchin { 106*da2e3ebdSchin register char* path; 107*da2e3ebdSchin register int n; 108*da2e3ebdSchin int v; 109*da2e3ebdSchin struct stat st; 110*da2e3ebdSchin 111*da2e3ebdSchin if (cmdquit()) 112*da2e3ebdSchin return -1; 113*da2e3ebdSchin if (ent->fts_info == FTS_NS || ent->fts_info == FTS_ERR || ent->fts_info == FTS_SLNONE) 114*da2e3ebdSchin { 115*da2e3ebdSchin if (!state->force) 116*da2e3ebdSchin error(2, "%s: not found", ent->fts_path); 117*da2e3ebdSchin } 118*da2e3ebdSchin else if (state->fs3d && iview(ent->fts_statp)) 119*da2e3ebdSchin fts_set(NiL, ent, FTS_SKIP); 120*da2e3ebdSchin else switch (ent->fts_info) 121*da2e3ebdSchin { 122*da2e3ebdSchin case FTS_DNR: 123*da2e3ebdSchin case FTS_DNX: 124*da2e3ebdSchin if (state->unconditional) 125*da2e3ebdSchin { 126*da2e3ebdSchin if (!chmod(ent->fts_name, (ent->fts_statp->st_mode & S_IPERM)|S_IRWXU)) 127*da2e3ebdSchin { 128*da2e3ebdSchin fts_set(NiL, ent, FTS_AGAIN); 129*da2e3ebdSchin break; 130*da2e3ebdSchin } 131*da2e3ebdSchin error_info.errors++; 132*da2e3ebdSchin } 133*da2e3ebdSchin else if (!state->force) 134*da2e3ebdSchin error(2, "%s: cannot %s directory", ent->fts_path, (ent->fts_info & FTS_NR) ? "read" : "search"); 135*da2e3ebdSchin else 136*da2e3ebdSchin error_info.errors++; 137*da2e3ebdSchin fts_set(NiL, ent, FTS_SKIP); 138*da2e3ebdSchin nonempty(ent); 139*da2e3ebdSchin break; 140*da2e3ebdSchin case FTS_D: 141*da2e3ebdSchin case FTS_DC: 142*da2e3ebdSchin path = ent->fts_name; 143*da2e3ebdSchin if (path[0] == '.' && (!path[1] || path[1] == '.' && !path[2]) && (ent->fts_level > 0 || path[1])) 144*da2e3ebdSchin { 145*da2e3ebdSchin fts_set(NiL, ent, FTS_SKIP); 146*da2e3ebdSchin if (!state->force) 147*da2e3ebdSchin error(2, "%s: cannot remove", ent->fts_path); 148*da2e3ebdSchin else 149*da2e3ebdSchin error_info.errors++; 150*da2e3ebdSchin break; 151*da2e3ebdSchin } 152*da2e3ebdSchin if (!state->recursive) 153*da2e3ebdSchin { 154*da2e3ebdSchin fts_set(NiL, ent, FTS_SKIP); 155*da2e3ebdSchin error(2, "%s: directory", ent->fts_path); 156*da2e3ebdSchin break; 157*da2e3ebdSchin } 158*da2e3ebdSchin if (!beenhere(ent)) 159*da2e3ebdSchin { 160*da2e3ebdSchin if (state->unconditional && (ent->fts_statp->st_mode ^ S_IRWXU)) 161*da2e3ebdSchin chmod(path, (ent->fts_statp->st_mode & S_IPERM)|S_IRWXU); 162*da2e3ebdSchin if (ent->fts_level > 0) 163*da2e3ebdSchin { 164*da2e3ebdSchin char* s; 165*da2e3ebdSchin 166*da2e3ebdSchin if (ent->fts_accpath == ent->fts_name || !(s = strrchr(ent->fts_accpath, '/'))) 167*da2e3ebdSchin v = !stat(".", &st); 168*da2e3ebdSchin else 169*da2e3ebdSchin { 170*da2e3ebdSchin path = ent->fts_accpath; 171*da2e3ebdSchin *s = 0; 172*da2e3ebdSchin v = !stat(path, &st); 173*da2e3ebdSchin *s = '/'; 174*da2e3ebdSchin } 175*da2e3ebdSchin if (v) 176*da2e3ebdSchin v = st.st_nlink <= 2 || st.st_ino == ent->fts_parent->fts_statp->st_ino && st.st_dev == ent->fts_parent->fts_statp->st_dev || strchr(astconf("PATH_ATTRIBUTES", path, NiL), 'l'); 177*da2e3ebdSchin } 178*da2e3ebdSchin else 179*da2e3ebdSchin v = 1; 180*da2e3ebdSchin if (v) 181*da2e3ebdSchin { 182*da2e3ebdSchin if (state->interactive) 183*da2e3ebdSchin { 184*da2e3ebdSchin if ((v = astquery(-1, "remove directory %s? ", ent->fts_path)) < 0) 185*da2e3ebdSchin return -1; 186*da2e3ebdSchin if (v > 0) 187*da2e3ebdSchin { 188*da2e3ebdSchin fts_set(NiL, ent, FTS_SKIP); 189*da2e3ebdSchin nonempty(ent); 190*da2e3ebdSchin } 191*da2e3ebdSchin } 192*da2e3ebdSchin if (ent->fts_info == FTS_D) 193*da2e3ebdSchin break; 194*da2e3ebdSchin } 195*da2e3ebdSchin else 196*da2e3ebdSchin { 197*da2e3ebdSchin ent->fts_info = FTS_DC; 198*da2e3ebdSchin error(1, "%s: hard link to directory", ent->fts_path); 199*da2e3ebdSchin } 200*da2e3ebdSchin } 201*da2e3ebdSchin else if (ent->fts_info == FTS_D) 202*da2e3ebdSchin break; 203*da2e3ebdSchin /*FALLTHROUGH*/ 204*da2e3ebdSchin case FTS_DP: 205*da2e3ebdSchin if (isempty(ent) || state->directory) 206*da2e3ebdSchin { 207*da2e3ebdSchin path = ent->fts_name; 208*da2e3ebdSchin if (path[0] != '.' || path[1]) 209*da2e3ebdSchin { 210*da2e3ebdSchin path = ent->fts_accpath; 211*da2e3ebdSchin if (state->verbose) 212*da2e3ebdSchin sfputr(sfstdout, ent->fts_path, '\n'); 213*da2e3ebdSchin if ((ent->fts_info == FTS_DC || state->directory) ? remove(path) : rmdir(path)) 214*da2e3ebdSchin switch (errno) 215*da2e3ebdSchin { 216*da2e3ebdSchin case EEXIST: 217*da2e3ebdSchin #if defined(ENOTEMPTY) && (ENOTEMPTY) != (EEXIST) 218*da2e3ebdSchin case ENOTEMPTY: 219*da2e3ebdSchin #endif 220*da2e3ebdSchin if (ent->fts_info == FTS_DP && !beenhere(ent)) 221*da2e3ebdSchin { 222*da2e3ebdSchin retry(ent); 223*da2e3ebdSchin fts_set(NiL, ent, FTS_AGAIN); 224*da2e3ebdSchin break; 225*da2e3ebdSchin } 226*da2e3ebdSchin /*FALLTHROUGH*/ 227*da2e3ebdSchin default: 228*da2e3ebdSchin nonempty(ent); 229*da2e3ebdSchin if (!state->force) 230*da2e3ebdSchin error(ERROR_SYSTEM|2, "%s: directory not removed", ent->fts_path); 231*da2e3ebdSchin else 232*da2e3ebdSchin error_info.errors++; 233*da2e3ebdSchin break; 234*da2e3ebdSchin } 235*da2e3ebdSchin } 236*da2e3ebdSchin else if (!state->force) 237*da2e3ebdSchin error(2, "%s: cannot remove", ent->fts_path); 238*da2e3ebdSchin else 239*da2e3ebdSchin error_info.errors++; 240*da2e3ebdSchin } 241*da2e3ebdSchin else 242*da2e3ebdSchin { 243*da2e3ebdSchin nonempty(ent); 244*da2e3ebdSchin if (!state->force) 245*da2e3ebdSchin error(2, "%s: directory not removed", ent->fts_path); 246*da2e3ebdSchin else 247*da2e3ebdSchin error_info.errors++; 248*da2e3ebdSchin } 249*da2e3ebdSchin break; 250*da2e3ebdSchin default: 251*da2e3ebdSchin path = ent->fts_accpath; 252*da2e3ebdSchin if (state->verbose) 253*da2e3ebdSchin sfputr(sfstdout, ent->fts_path, '\n'); 254*da2e3ebdSchin if (state->interactive) 255*da2e3ebdSchin { 256*da2e3ebdSchin if ((v = astquery(-1, "remove %s? ", ent->fts_path)) < 0) 257*da2e3ebdSchin return -1; 258*da2e3ebdSchin if (v > 0) 259*da2e3ebdSchin { 260*da2e3ebdSchin nonempty(ent); 261*da2e3ebdSchin break; 262*da2e3ebdSchin } 263*da2e3ebdSchin } 264*da2e3ebdSchin else if (!state->force && state->terminal && S_ISREG(ent->fts_statp->st_mode)) 265*da2e3ebdSchin { 266*da2e3ebdSchin if ((n = open(path, O_RDWR)) < 0) 267*da2e3ebdSchin { 268*da2e3ebdSchin if ( 269*da2e3ebdSchin #ifdef ENOENT 270*da2e3ebdSchin errno != ENOENT && 271*da2e3ebdSchin #endif 272*da2e3ebdSchin #ifdef EROFS 273*da2e3ebdSchin errno != EROFS && 274*da2e3ebdSchin #endif 275*da2e3ebdSchin (v = astquery(-1, "override protection %s for %s? ", 276*da2e3ebdSchin #ifdef ETXTBSY 277*da2e3ebdSchin errno == ETXTBSY ? "``running program''" : 278*da2e3ebdSchin #endif 279*da2e3ebdSchin ent->fts_statp->st_uid != state->uid ? "``not owner''" : 280*da2e3ebdSchin fmtmode(ent->fts_statp->st_mode & S_IPERM, 0) + 1, ent->fts_path)) < 0) 281*da2e3ebdSchin return -1; 282*da2e3ebdSchin if (v > 0) 283*da2e3ebdSchin { 284*da2e3ebdSchin nonempty(ent); 285*da2e3ebdSchin break; 286*da2e3ebdSchin } 287*da2e3ebdSchin } 288*da2e3ebdSchin else 289*da2e3ebdSchin close(n); 290*da2e3ebdSchin } 291*da2e3ebdSchin #if _lib_fsync 292*da2e3ebdSchin if (state->clobber && S_ISREG(ent->fts_statp->st_mode) && ent->fts_statp->st_size > 0) 293*da2e3ebdSchin { 294*da2e3ebdSchin if ((n = open(path, O_WRONLY)) < 0) 295*da2e3ebdSchin error(ERROR_SYSTEM|2, "%s: cannot clear data", ent->fts_path); 296*da2e3ebdSchin else 297*da2e3ebdSchin { 298*da2e3ebdSchin off_t c = ent->fts_statp->st_size; 299*da2e3ebdSchin 300*da2e3ebdSchin for (;;) 301*da2e3ebdSchin { 302*da2e3ebdSchin if (write(n, state->buf, sizeof(state->buf)) != sizeof(state->buf)) 303*da2e3ebdSchin { 304*da2e3ebdSchin error(ERROR_SYSTEM|2, "%s: data clear error", ent->fts_path); 305*da2e3ebdSchin break; 306*da2e3ebdSchin } 307*da2e3ebdSchin if (c <= sizeof(state->buf)) 308*da2e3ebdSchin break; 309*da2e3ebdSchin c -= sizeof(state->buf); 310*da2e3ebdSchin } 311*da2e3ebdSchin fsync(n); 312*da2e3ebdSchin close(n); 313*da2e3ebdSchin } 314*da2e3ebdSchin } 315*da2e3ebdSchin #endif 316*da2e3ebdSchin if (remove(path)) 317*da2e3ebdSchin { 318*da2e3ebdSchin nonempty(ent); 319*da2e3ebdSchin if (!state->force || state->interactive) 320*da2e3ebdSchin error(ERROR_SYSTEM|2, "%s: not removed", ent->fts_path); 321*da2e3ebdSchin else 322*da2e3ebdSchin error_info.errors++; 323*da2e3ebdSchin } 324*da2e3ebdSchin break; 325*da2e3ebdSchin } 326*da2e3ebdSchin return 0; 327*da2e3ebdSchin } 328*da2e3ebdSchin 329*da2e3ebdSchin int 330*da2e3ebdSchin b_rm(int argc, register char** argv, void* context) 331*da2e3ebdSchin { 332*da2e3ebdSchin State_t state; 333*da2e3ebdSchin FTS* fts; 334*da2e3ebdSchin FTSENT* ent; 335*da2e3ebdSchin int set3d; 336*da2e3ebdSchin 337*da2e3ebdSchin cmdinit(argc, argv, context, ERROR_CATALOG, ERROR_NOTIFY); 338*da2e3ebdSchin memset(&state, 0, sizeof(state)); 339*da2e3ebdSchin state.fs3d = fs3d(FS3D_TEST); 340*da2e3ebdSchin state.terminal = isatty(0); 341*da2e3ebdSchin for (;;) 342*da2e3ebdSchin { 343*da2e3ebdSchin switch (optget(argv, usage)) 344*da2e3ebdSchin { 345*da2e3ebdSchin case 'd': 346*da2e3ebdSchin state.directory = 1; 347*da2e3ebdSchin continue; 348*da2e3ebdSchin case 'f': 349*da2e3ebdSchin state.force = 1; 350*da2e3ebdSchin state.interactive = 0; 351*da2e3ebdSchin continue; 352*da2e3ebdSchin case 'i': 353*da2e3ebdSchin state.interactive = 1; 354*da2e3ebdSchin state.force = 0; 355*da2e3ebdSchin continue; 356*da2e3ebdSchin case 'r': 357*da2e3ebdSchin case 'R': 358*da2e3ebdSchin state.recursive = 1; 359*da2e3ebdSchin continue; 360*da2e3ebdSchin case 'F': 361*da2e3ebdSchin #if _lib_fsync 362*da2e3ebdSchin state.clobber = 1; 363*da2e3ebdSchin #else 364*da2e3ebdSchin error(1, "%s not implemented on this system", opt_info.name); 365*da2e3ebdSchin #endif 366*da2e3ebdSchin continue; 367*da2e3ebdSchin case 'u': 368*da2e3ebdSchin state.unconditional = 1; 369*da2e3ebdSchin continue; 370*da2e3ebdSchin case 'v': 371*da2e3ebdSchin state.verbose = 1; 372*da2e3ebdSchin continue; 373*da2e3ebdSchin case '?': 374*da2e3ebdSchin error(ERROR_USAGE|4, "%s", opt_info.arg); 375*da2e3ebdSchin continue; 376*da2e3ebdSchin case ':': 377*da2e3ebdSchin error(2, "%s", opt_info.arg); 378*da2e3ebdSchin continue; 379*da2e3ebdSchin } 380*da2e3ebdSchin break; 381*da2e3ebdSchin } 382*da2e3ebdSchin argv += opt_info.index; 383*da2e3ebdSchin if (*argv && streq(*argv, "-") && !streq(*(argv - 1), "--")) 384*da2e3ebdSchin argv++; 385*da2e3ebdSchin if (error_info.errors || !*argv) 386*da2e3ebdSchin error(ERROR_USAGE|4, "%s", optusage(NiL)); 387*da2e3ebdSchin 388*da2e3ebdSchin /* 389*da2e3ebdSchin * do it 390*da2e3ebdSchin */ 391*da2e3ebdSchin 392*da2e3ebdSchin if (state.interactive) 393*da2e3ebdSchin state.verbose = 0; 394*da2e3ebdSchin state.uid = geteuid(); 395*da2e3ebdSchin state.unconditional = state.unconditional && state.recursive && state.force; 396*da2e3ebdSchin if (state.recursive && state.fs3d) 397*da2e3ebdSchin { 398*da2e3ebdSchin set3d = state.fs3d; 399*da2e3ebdSchin state.fs3d = 0; 400*da2e3ebdSchin fs3d(0); 401*da2e3ebdSchin } 402*da2e3ebdSchin else 403*da2e3ebdSchin set3d = 0; 404*da2e3ebdSchin if (fts = fts_open(argv, FTS_PHYSICAL, NiL)) 405*da2e3ebdSchin { 406*da2e3ebdSchin while ((ent = fts_read(fts)) && !rm(&state, ent)); 407*da2e3ebdSchin fts_close(fts); 408*da2e3ebdSchin } 409*da2e3ebdSchin else if (!state.force) 410*da2e3ebdSchin error(ERROR_SYSTEM|2, "%s: cannot remove", argv[0]); 411*da2e3ebdSchin if (set3d) 412*da2e3ebdSchin fs3d(set3d); 413*da2e3ebdSchin return error_info.errors != 0; 414*da2e3ebdSchin } 415