1/*
2 * Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include <RemoveEngine.h>
8
9#include <errno.h>
10#include <string.h>
11#include <unistd.h>
12
13#include <Directory.h>
14#include <Entry.h>
15#include <Path.h>
16
17
18namespace BPrivate {
19
20
21// #pragma mark - BRemoveEngine
22
23
24BRemoveEngine::BRemoveEngine()
25	:
26	fController(NULL)
27{
28}
29
30
31BRemoveEngine::~BRemoveEngine()
32{
33}
34
35
36BRemoveEngine::BController*
37BRemoveEngine::Controller() const
38{
39	return fController;
40}
41
42
43void
44BRemoveEngine::SetController(BController* controller)
45{
46	fController = controller;
47}
48
49
50status_t
51BRemoveEngine::RemoveEntry(const Entry& entry)
52{
53	BPath pathBuffer;
54	const char* path;
55	status_t error = entry.GetPath(pathBuffer, path);
56	if (error != B_OK)
57		return error;
58
59	return _RemoveEntry(path);
60}
61
62
63status_t
64BRemoveEngine::_RemoveEntry(const char* path)
65{
66	// apply entry filter
67	if (fController != NULL && !fController->EntryStarted(path))
68		return B_OK;
69
70	// stat entry
71	struct stat st;
72	if (lstat(path, &st) < 0) {
73		return _HandleEntryError(path, errno, "Couldn't access \"%s\": %s\n",
74			path, strerror(errno));
75	}
76
77	// recurse, if entry is a directory
78	if (S_ISDIR(st.st_mode)) {
79		// open directory
80		BDirectory directory;
81		status_t error = directory.SetTo(path);
82		if (error != B_OK) {
83			return _HandleEntryError(path, error,
84				"Failed to open directory \"%s\": %s\n", path, strerror(error));
85		}
86
87		char buffer[offsetof(struct dirent, d_name) + B_FILE_NAME_LENGTH];
88		dirent *entry = (dirent*)buffer;
89		while (directory.GetNextDirents(entry, sizeof(buffer), 1) == 1) {
90			if (strcmp(entry->d_name, ".") == 0
91				|| strcmp(entry->d_name, "..") == 0) {
92				continue;
93			}
94
95			// construct child entry path
96			BPath childPath;
97			error = childPath.SetTo(path, entry->d_name);
98			if (error != B_OK) {
99				return _HandleEntryError(path, error,
100					"Failed to construct entry path from dir \"%s\" and name "
101					"\"%s\": %s\n", path, entry->d_name, strerror(error));
102			}
103
104			// remove the entry
105			error = _RemoveEntry(childPath.Path());
106			if (error != B_OK) {
107				if (fController != NULL
108					&& fController->EntryFinished(path, error)) {
109					return B_OK;
110				}
111				return error;
112			}
113		}
114	}
115
116	// remove entry
117	if (S_ISDIR(st.st_mode)) {
118		if (rmdir(path) < 0) {
119			return _HandleEntryError(path, errno,
120				"Failed to remove \"%s\": %s\n", path, strerror(errno));
121		}
122	} else {
123		if (unlink(path) < 0) {
124			return _HandleEntryError(path, errno,
125				"Failed to unlink \"%s\": %s\n", path, strerror(errno));
126		}
127	}
128
129	if (fController != NULL)
130		fController->EntryFinished(path, B_OK);
131
132	return B_OK;
133}
134
135
136void
137BRemoveEngine::_NotifyErrorVarArgs(status_t error, const char* format,
138	va_list args)
139{
140	if (fController != NULL) {
141		BString message;
142		message.SetToFormatVarArgs(format, args);
143		fController->ErrorOccurred(message, error);
144	}
145}
146
147
148status_t
149BRemoveEngine::_HandleEntryError(const char* path, status_t error,
150	const char* format, ...)
151{
152	if (fController == NULL)
153		return error;
154
155	va_list args;
156	va_start(args, format);
157	_NotifyErrorVarArgs(error, format, args);
158	va_end(args);
159
160	if (fController->EntryFinished(path, error))
161		return B_OK;
162	return error;
163}
164
165
166// #pragma mark - BController
167
168
169BRemoveEngine::BController::BController()
170{
171}
172
173
174BRemoveEngine::BController::~BController()
175{
176}
177
178
179bool
180BRemoveEngine::BController::EntryStarted(const char* path)
181{
182	return true;
183}
184
185
186bool
187BRemoveEngine::BController::EntryFinished(const char* path, status_t error)
188{
189	return error == B_OK;
190}
191
192
193void
194BRemoveEngine::BController::ErrorOccurred(const char* message, status_t error)
195{
196}
197
198
199} // namespace BPrivate
200