removefile_tree_walker.c [plain text]
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <fts.h>
#include "removefile.h"
#include "removefile_priv.h"
static int
__removefile_process_file(FTS* stream, FTSENT* current_file, removefile_state_t state) {
int res = 0;
char* path = current_file->fts_path;
int recursive = state->unlink_flags & REMOVEFILE_RECURSIVE;
int keep_parent = state->unlink_flags & REMOVEFILE_KEEP_PARENT;
int secure = state->unlink_flags & (REMOVEFILE_SECURE_7_PASS | REMOVEFILE_SECURE_35_PASS | REMOVEFILE_SECURE_1_PASS | REMOVEFILE_SECURE_3_PASS | REMOVEFILE_SECURE_1_PASS_ZERO);
switch (current_file->fts_info) {
case FTS_D:
if (unlink(path) == 0) {
fts_set(stream, current_file, FTS_SKIP);
}
break;
case FTS_DC:
state->error_num = ELOOP;
res = -1;
break;
case FTS_DNR:
case FTS_ERR:
case FTS_NS:
state->error_num = current_file->fts_errno;
res = -1;
break;
case FTS_DP:
if (recursive &&
(!keep_parent ||
current_file->fts_level != FTS_ROOTLEVEL)) {
if (secure) {
res = __removefile_rename_unlink(path,
state);
} else {
if (geteuid() == 0 &&
(current_file->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) &&
!(current_file->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) &&
chflags(path, current_file->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)) < 0) {
errno = EACCES;
res = -1;
} else {
#if __APPLE__
int is_dataless = (current_file->fts_statp->st_flags & SF_DATALESS) != 0;
if (is_dataless) {
int iopolicy = getiopolicy_np(IOPOL_TYPE_VFS_MATERIALIZE_DATALESS_FILES, IOPOL_SCOPE_THREAD);
int non_materializing = iopolicy == IOPOL_MATERIALIZE_DATALESS_FILES_OFF;
if (non_materializing || state->confirm_callback == NULL) {
res = unlinkat(AT_FDCWD, path, AT_REMOVEDIR_DATALESS);
} else {
res = rmdir(path);
}
} else {
res = rmdir(path);
}
#else
res = rmdir(path);
#endif
}
}
if (res == -1) state->error_num = errno;
}
break;
case FTS_F:
case FTS_SL:
case FTS_SLNONE:
case FTS_DEFAULT:
if (secure) {
res = __removefile_sunlink(path, state);
} else if (geteuid() == 0 &&
(current_file->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) &&
!(current_file->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) &&
chflags(path, current_file->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)) < 0) {
errno = EACCES;
res = -1;
} else {
res = unlink(path);
}
if (res == -1) state->error_num = errno;
break;
case FTS_DOT:
default:
break;
}
return res;
}
int
__removefile_tree_walker(char **trees, removefile_state_t state) {
FTSENT *current_file;
FTS *stream;
int rval = 0;
int open_flags = 0;
removefile_callback_t cb_confirm = NULL;
removefile_callback_t cb_status = NULL;
removefile_callback_t cb_error = NULL;
cb_confirm = state->confirm_callback;
cb_status = state->status_callback;
cb_error = state->error_callback;
open_flags = FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV;
if ((REMOVEFILE_CROSS_MOUNT & state->unlink_flags) != 0)
open_flags &= ~FTS_XDEV;
stream = fts_open(trees, open_flags, NULL);
if (stream == NULL) {
state->error_num = errno;
return -1;
}
while ((current_file = fts_read(stream)) != NULL) {
int res = REMOVEFILE_PROCEED;
if (current_file->fts_info == FTS_DP &&
current_file->fts_number == REMOVEFILE_SKIP) {
current_file->fts_number = 0;
continue;
}
if (current_file->fts_info == FTS_DP &&
stream->fts_options & FTS_XDEV &&
stream->fts_dev != current_file->fts_dev) {
continue;
}
if (__removefile_state_test_cancel(state)) break;
if (cb_confirm && current_file->fts_info != FTS_DP) {
res = cb_confirm(state,
current_file->fts_path, state->confirm_context);
}
if (__removefile_state_test_cancel(state)) break;
if (res == REMOVEFILE_PROCEED) {
state->error_num = 0;
rval = __removefile_process_file(stream, current_file,
state);
if (state->error_num != 0) {
if ((state->error_num != ENOENT &&
state->error_num != ENOTDIR) ||
current_file->fts_level == FTS_ROOTLEVEL) {
if (cb_error) {
res = cb_error(state,
current_file->fts_path,
state->error_context);
if (res == REMOVEFILE_PROCEED ||
res == REMOVEFILE_SKIP) {
rval = 0;
} else if (res == REMOVEFILE_STOP) {
rval = -1;
}
} else {
res = REMOVEFILE_STOP;
}
}
} else if (cb_status &&
current_file->fts_info != FTS_D) {
res = cb_status(state, current_file->fts_path,
state->status_context);
}
}
if (current_file->fts_info == FTS_D && res == REMOVEFILE_SKIP) {
current_file->fts_number = REMOVEFILE_SKIP;
}
if (res == REMOVEFILE_SKIP ||
!(state->unlink_flags & REMOVEFILE_RECURSIVE))
fts_set(stream, current_file, FTS_SKIP);
if (res == REMOVEFILE_STOP ||
__removefile_state_test_cancel(state))
break;
}
if (__removefile_state_test_cancel(state)) {
state->error_num = ECANCELED;
rval = -1;
}
fts_close(stream);
return rval;
}