14887Schin /***********************************************************************
24887Schin * *
34887Schin * This software is part of the ast package *
4*12068SRoger.Faulkner@Oracle.COM * Copyright (c) 1992-2010 AT&T Intellectual Property *
54887Schin * and is licensed under the *
64887Schin * Common Public License, Version 1.0 *
78462SApril.Chin@Sun.COM * by AT&T Intellectual Property *
84887Schin * *
94887Schin * A copy of the License is available at *
104887Schin * http://www.opensource.org/licenses/cpl1.0.txt *
114887Schin * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
124887Schin * *
134887Schin * Information and Software Systems Research *
144887Schin * AT&T Research *
154887Schin * Florham Park NJ *
164887Schin * *
174887Schin * Glenn Fowler <gsf@research.att.com> *
184887Schin * David Korn <dgk@research.att.com> *
194887Schin * *
204887Schin ***********************************************************************/
214887Schin #pragma prototyped
224887Schin /*
234887Schin * Glenn Fowler
244887Schin * AT&T Research
254887Schin *
264887Schin * rm [-fir] [file ...]
274887Schin */
284887Schin
294887Schin static const char usage[] =
3010898Sroland.mainz@nrubsig.org "[-?\n@(#)$Id: rm (AT&T Research) 2009-06-18 $\n]"
314887Schin USAGE_LICENSE
324887Schin "[+NAME?rm - remove files]"
334887Schin "[+DESCRIPTION?\brm\b removes the named \afile\a arguments. By default it"
344887Schin " does not remove directories. If a file is unwritable, the"
354887Schin " standard input is a terminal, and the \b--force\b option is not"
364887Schin " given, \brm\b prompts the user for whether to remove the file."
374887Schin " An affirmative response (\by\b or \bY\b) removes the file, a quit"
384887Schin " response (\bq\b or \bQ\b) causes \brm\b to exit immediately, and"
394887Schin " all other responses skip the current file.]"
404887Schin
414887Schin "[c|F:clear|clobber?Clear the contents of each file before removing by"
424887Schin " writing a 0 filled buffer the same size as the file, executing"
434887Schin " \bfsync\b(2) and closing before attempting to remove. Implemented"
444887Schin " only on systems that support \bfsync\b(2).]"
454887Schin "[d:directory?\bremove\b(3) (or \bunlink\b(2)) directories rather than"
464887Schin " \brmdir\b(2), and don't require that they be empty before removal."
474887Schin " The caller requires sufficient privilege, not to mention a strong"
484887Schin " constitution, to use this option. Even though the directory must"
494887Schin " not be empty, \brm\b still attempts to empty it before removal.]"
504887Schin "[f:force?Ignore nonexistent files and never prompt the user.]"
514887Schin "[i:interactive|prompt?Prompt whether to remove each file."
524887Schin " An affirmative response (\by\b or \bY\b) removes the file, a quit"
534887Schin " response (\bq\b or \bQ\b) causes \brm\b to exit immediately, and"
544887Schin " all other responses skip the current file.]"
554887Schin "[r|R:recursive?Remove the contents of directories recursively.]"
564887Schin "[u:unconditional?If \b--recursive\b and \b--force\b are also enabled then"
574887Schin " the owner read, write and execute modes are enabled (if not already"
584887Schin " enabled) for each directory before attempting to remove directory"
594887Schin " contents.]"
604887Schin "[v:verbose?Print the name of each file before removing it.]"
614887Schin
624887Schin "\n"
634887Schin "\nfile ...\n"
644887Schin "\n"
654887Schin
664887Schin "[+SEE ALSO?\bmv\b(1), \brmdir\b(2), \bunlink\b(2), \bremove\b(3)]"
674887Schin ;
684887Schin
694887Schin #include <cmd.h>
704887Schin #include <ls.h>
71*12068SRoger.Faulkner@Oracle.COM #include <fts_fix.h>
724887Schin #include <fs3d.h>
734887Schin
744887Schin #define RM_ENTRY 1
754887Schin
764887Schin #define beenhere(f) (((f)->fts_number>>1)==(f)->fts_statp->st_nlink)
774887Schin #define isempty(f) (!((f)->fts_number&RM_ENTRY))
784887Schin #define nonempty(f) ((f)->fts_parent->fts_number|=RM_ENTRY)
794887Schin #define pathchunk(n) roundof(n,1024)
804887Schin #define retry(f) ((f)->fts_number=((f)->fts_statp->st_nlink<<1))
814887Schin
824887Schin typedef struct State_s /* program state */
834887Schin {
8410898Sroland.mainz@nrubsig.org void* context; /* builtin context */
854887Schin int clobber; /* clear out file data first */
864887Schin int directory; /* remove(dir) not rmdir(dir) */
874887Schin int force; /* force actions */
884887Schin int fs3d; /* 3d enabled */
894887Schin int interactive; /* prompt for approval */
904887Schin int recursive; /* remove subtrees too */
914887Schin int terminal; /* attached to terminal */
924887Schin int uid; /* caller uid */
934887Schin int unconditional; /* enable dir rwx on preorder */
944887Schin int verbose; /* display each file */
954887Schin #if _lib_fsync
964887Schin char buf[SF_BUFSIZE];/* clobber buffer */
974887Schin #endif
984887Schin } State_t;
994887Schin
1004887Schin /*
1014887Schin * remove a single file
1024887Schin */
1034887Schin
1044887Schin static int
rm(State_t * state,register FTSENT * ent)1054887Schin rm(State_t* state, register FTSENT* ent)
1064887Schin {
1074887Schin register char* path;
1084887Schin register int n;
1094887Schin int v;
1104887Schin struct stat st;
1114887Schin
1124887Schin if (ent->fts_info == FTS_NS || ent->fts_info == FTS_ERR || ent->fts_info == FTS_SLNONE)
1134887Schin {
1144887Schin if (!state->force)
1154887Schin error(2, "%s: not found", ent->fts_path);
1164887Schin }
1174887Schin else if (state->fs3d && iview(ent->fts_statp))
1184887Schin fts_set(NiL, ent, FTS_SKIP);
1194887Schin else switch (ent->fts_info)
1204887Schin {
1214887Schin case FTS_DNR:
1224887Schin case FTS_DNX:
1234887Schin if (state->unconditional)
1244887Schin {
1254887Schin if (!chmod(ent->fts_name, (ent->fts_statp->st_mode & S_IPERM)|S_IRWXU))
1264887Schin {
1274887Schin fts_set(NiL, ent, FTS_AGAIN);
1284887Schin break;
1294887Schin }
1304887Schin error_info.errors++;
1314887Schin }
1324887Schin else if (!state->force)
1334887Schin error(2, "%s: cannot %s directory", ent->fts_path, (ent->fts_info & FTS_NR) ? "read" : "search");
1344887Schin else
1354887Schin error_info.errors++;
1364887Schin fts_set(NiL, ent, FTS_SKIP);
1374887Schin nonempty(ent);
1384887Schin break;
1394887Schin case FTS_D:
1404887Schin case FTS_DC:
1414887Schin path = ent->fts_name;
1424887Schin if (path[0] == '.' && (!path[1] || path[1] == '.' && !path[2]) && (ent->fts_level > 0 || path[1]))
1434887Schin {
1444887Schin fts_set(NiL, ent, FTS_SKIP);
1454887Schin if (!state->force)
1464887Schin error(2, "%s: cannot remove", ent->fts_path);
1474887Schin else
1484887Schin error_info.errors++;
1494887Schin break;
1504887Schin }
1514887Schin if (!state->recursive)
1524887Schin {
1534887Schin fts_set(NiL, ent, FTS_SKIP);
1544887Schin error(2, "%s: directory", ent->fts_path);
1554887Schin break;
1564887Schin }
1574887Schin if (!beenhere(ent))
1584887Schin {
1594887Schin if (state->unconditional && (ent->fts_statp->st_mode ^ S_IRWXU))
1604887Schin chmod(path, (ent->fts_statp->st_mode & S_IPERM)|S_IRWXU);
1614887Schin if (ent->fts_level > 0)
1624887Schin {
1634887Schin char* s;
1644887Schin
1654887Schin if (ent->fts_accpath == ent->fts_name || !(s = strrchr(ent->fts_accpath, '/')))
1664887Schin v = !stat(".", &st);
1674887Schin else
1684887Schin {
1694887Schin path = ent->fts_accpath;
1704887Schin *s = 0;
1714887Schin v = !stat(path, &st);
1724887Schin *s = '/';
1734887Schin }
1744887Schin if (v)
1754887Schin 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');
1764887Schin }
1774887Schin else
1784887Schin v = 1;
1794887Schin if (v)
1804887Schin {
1814887Schin if (state->interactive)
1824887Schin {
18310898Sroland.mainz@nrubsig.org if ((v = astquery(-1, "remove directory %s? ", ent->fts_path)) < 0 || sh_checksig(state->context))
1844887Schin return -1;
1854887Schin if (v > 0)
1864887Schin {
1874887Schin fts_set(NiL, ent, FTS_SKIP);
1884887Schin nonempty(ent);
1894887Schin }
1904887Schin }
1914887Schin if (ent->fts_info == FTS_D)
1924887Schin break;
1934887Schin }
1944887Schin else
1954887Schin {
1964887Schin ent->fts_info = FTS_DC;
1974887Schin error(1, "%s: hard link to directory", ent->fts_path);
1984887Schin }
1994887Schin }
2004887Schin else if (ent->fts_info == FTS_D)
2014887Schin break;
2024887Schin /*FALLTHROUGH*/
2034887Schin case FTS_DP:
2044887Schin if (isempty(ent) || state->directory)
2054887Schin {
2064887Schin path = ent->fts_name;
2074887Schin if (path[0] != '.' || path[1])
2084887Schin {
2094887Schin path = ent->fts_accpath;
2104887Schin if (state->verbose)
2114887Schin sfputr(sfstdout, ent->fts_path, '\n');
2124887Schin if ((ent->fts_info == FTS_DC || state->directory) ? remove(path) : rmdir(path))
2134887Schin switch (errno)
2144887Schin {
2158462SApril.Chin@Sun.COM case ENOENT:
2168462SApril.Chin@Sun.COM break;
2174887Schin case EEXIST:
2184887Schin #if defined(ENOTEMPTY) && (ENOTEMPTY) != (EEXIST)
2194887Schin case ENOTEMPTY:
2204887Schin #endif
2214887Schin if (ent->fts_info == FTS_DP && !beenhere(ent))
2224887Schin {
2234887Schin retry(ent);
2244887Schin fts_set(NiL, ent, FTS_AGAIN);
2254887Schin break;
2264887Schin }
2274887Schin /*FALLTHROUGH*/
2284887Schin default:
2294887Schin nonempty(ent);
2304887Schin if (!state->force)
2314887Schin error(ERROR_SYSTEM|2, "%s: directory not removed", ent->fts_path);
2324887Schin else
2334887Schin error_info.errors++;
2344887Schin break;
2354887Schin }
2364887Schin }
2374887Schin else if (!state->force)
2384887Schin error(2, "%s: cannot remove", ent->fts_path);
2394887Schin else
2404887Schin error_info.errors++;
2414887Schin }
2424887Schin else
2434887Schin {
2444887Schin nonempty(ent);
2454887Schin if (!state->force)
2464887Schin error(2, "%s: directory not removed", ent->fts_path);
2474887Schin else
2484887Schin error_info.errors++;
2494887Schin }
2504887Schin break;
2514887Schin default:
2524887Schin path = ent->fts_accpath;
2534887Schin if (state->verbose)
2544887Schin sfputr(sfstdout, ent->fts_path, '\n');
2554887Schin if (state->interactive)
2564887Schin {
25710898Sroland.mainz@nrubsig.org if ((v = astquery(-1, "remove %s? ", ent->fts_path)) < 0 || sh_checksig(state->context))
2584887Schin return -1;
2594887Schin if (v > 0)
2604887Schin {
2614887Schin nonempty(ent);
2624887Schin break;
2634887Schin }
2644887Schin }
2654887Schin else if (!state->force && state->terminal && S_ISREG(ent->fts_statp->st_mode))
2664887Schin {
2674887Schin if ((n = open(path, O_RDWR)) < 0)
2684887Schin {
2694887Schin if (
2704887Schin #ifdef ENOENT
2714887Schin errno != ENOENT &&
2724887Schin #endif
2734887Schin #ifdef EROFS
2744887Schin errno != EROFS &&
2754887Schin #endif
2764887Schin (v = astquery(-1, "override protection %s for %s? ",
2774887Schin #ifdef ETXTBSY
2784887Schin errno == ETXTBSY ? "``running program''" :
2794887Schin #endif
2804887Schin ent->fts_statp->st_uid != state->uid ? "``not owner''" :
28110898Sroland.mainz@nrubsig.org fmtmode(ent->fts_statp->st_mode & S_IPERM, 0) + 1, ent->fts_path)) < 0 ||
28210898Sroland.mainz@nrubsig.org sh_checksig(state->context))
2834887Schin return -1;
2844887Schin if (v > 0)
2854887Schin {
2864887Schin nonempty(ent);
2874887Schin break;
2884887Schin }
2894887Schin }
2904887Schin else
2914887Schin close(n);
2924887Schin }
2934887Schin #if _lib_fsync
2944887Schin if (state->clobber && S_ISREG(ent->fts_statp->st_mode) && ent->fts_statp->st_size > 0)
2954887Schin {
2964887Schin if ((n = open(path, O_WRONLY)) < 0)
2974887Schin error(ERROR_SYSTEM|2, "%s: cannot clear data", ent->fts_path);
2984887Schin else
2994887Schin {
3004887Schin off_t c = ent->fts_statp->st_size;
3014887Schin
3024887Schin for (;;)
3034887Schin {
3044887Schin if (write(n, state->buf, sizeof(state->buf)) != sizeof(state->buf))
3054887Schin {
3064887Schin error(ERROR_SYSTEM|2, "%s: data clear error", ent->fts_path);
3074887Schin break;
3084887Schin }
3094887Schin if (c <= sizeof(state->buf))
3104887Schin break;
3114887Schin c -= sizeof(state->buf);
3124887Schin }
3134887Schin fsync(n);
3144887Schin close(n);
3154887Schin }
3164887Schin }
3174887Schin #endif
3184887Schin if (remove(path))
3194887Schin {
3204887Schin nonempty(ent);
3218462SApril.Chin@Sun.COM switch (errno)
3228462SApril.Chin@Sun.COM {
3238462SApril.Chin@Sun.COM case ENOENT:
3248462SApril.Chin@Sun.COM break;
3258462SApril.Chin@Sun.COM default:
3268462SApril.Chin@Sun.COM if (!state->force || state->interactive)
3278462SApril.Chin@Sun.COM error(ERROR_SYSTEM|2, "%s: not removed", ent->fts_path);
3288462SApril.Chin@Sun.COM else
3298462SApril.Chin@Sun.COM error_info.errors++;
3308462SApril.Chin@Sun.COM break;
3318462SApril.Chin@Sun.COM }
3324887Schin }
3334887Schin break;
3344887Schin }
3354887Schin return 0;
3364887Schin }
3374887Schin
3384887Schin int
b_rm(int argc,register char ** argv,void * context)3394887Schin b_rm(int argc, register char** argv, void* context)
3404887Schin {
3414887Schin State_t state;
3424887Schin FTS* fts;
3434887Schin FTSENT* ent;
3444887Schin int set3d;
3454887Schin
3464887Schin cmdinit(argc, argv, context, ERROR_CATALOG, ERROR_NOTIFY);
3474887Schin memset(&state, 0, sizeof(state));
34810898Sroland.mainz@nrubsig.org state.context = context;
3494887Schin state.fs3d = fs3d(FS3D_TEST);
3504887Schin state.terminal = isatty(0);
3514887Schin for (;;)
3524887Schin {
3534887Schin switch (optget(argv, usage))
3544887Schin {
3554887Schin case 'd':
3564887Schin state.directory = 1;
3574887Schin continue;
3584887Schin case 'f':
3594887Schin state.force = 1;
3604887Schin state.interactive = 0;
3614887Schin continue;
3624887Schin case 'i':
3634887Schin state.interactive = 1;
3644887Schin state.force = 0;
3654887Schin continue;
3664887Schin case 'r':
3674887Schin case 'R':
3684887Schin state.recursive = 1;
3694887Schin continue;
3704887Schin case 'F':
3714887Schin #if _lib_fsync
3724887Schin state.clobber = 1;
3734887Schin #else
3744887Schin error(1, "%s not implemented on this system", opt_info.name);
3754887Schin #endif
3764887Schin continue;
3774887Schin case 'u':
3784887Schin state.unconditional = 1;
3794887Schin continue;
3804887Schin case 'v':
3814887Schin state.verbose = 1;
3824887Schin continue;
3834887Schin case '?':
3844887Schin error(ERROR_USAGE|4, "%s", opt_info.arg);
3854887Schin continue;
3864887Schin case ':':
3874887Schin error(2, "%s", opt_info.arg);
3884887Schin continue;
3894887Schin }
3904887Schin break;
3914887Schin }
3924887Schin argv += opt_info.index;
3934887Schin if (*argv && streq(*argv, "-") && !streq(*(argv - 1), "--"))
3944887Schin argv++;
3954887Schin if (error_info.errors || !*argv)
3964887Schin error(ERROR_USAGE|4, "%s", optusage(NiL));
3974887Schin
3984887Schin /*
3994887Schin * do it
4004887Schin */
4014887Schin
4024887Schin if (state.interactive)
4034887Schin state.verbose = 0;
4044887Schin state.uid = geteuid();
4054887Schin state.unconditional = state.unconditional && state.recursive && state.force;
4064887Schin if (state.recursive && state.fs3d)
4074887Schin {
4084887Schin set3d = state.fs3d;
4094887Schin state.fs3d = 0;
4104887Schin fs3d(0);
4114887Schin }
4124887Schin else
4134887Schin set3d = 0;
4144887Schin if (fts = fts_open(argv, FTS_PHYSICAL, NiL))
4154887Schin {
4168462SApril.Chin@Sun.COM while (!sh_checksig(context) && (ent = fts_read(fts)) && !rm(&state, ent));
4174887Schin fts_close(fts);
4184887Schin }
4194887Schin else if (!state.force)
4204887Schin error(ERROR_SYSTEM|2, "%s: cannot remove", argv[0]);
4214887Schin if (set3d)
4224887Schin fs3d(set3d);
4234887Schin return error_info.errors != 0;
4244887Schin }
425