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