xref: /haiku/src/tools/rm_attrs.cpp (revision 9f81ca838ce7b92b5689e57d3f86765db4705a7b)
1 /*
2  * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 #include <dirent.h>
7 #include <errno.h>
8 #include <limits.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <sys/stat.h>
13 #include <unistd.h>
14 
15 
16 // exported by the generic attribute support in libroot_build.so
17 extern "C" bool __get_attribute_dir_path(const struct stat* st,
18 	const char* path, char* buffer);
19 
20 
21 class Path {
22 public:
Init(const char * path)23 	bool Init(const char* path)
24 	{
25 		size_t len = strlen(path);
26 		if (len == 0 || len >= PATH_MAX)
27 			return false;
28 
29 		strcpy(fPath, path);
30 		fPathLen = len;
31 
32 		return true;
33 	}
34 
GetPath() const35 	const char* GetPath() const
36 	{
37 		return fPath;
38 	}
39 
Buffer()40 	char* Buffer()
41 	{
42 		return fPath;
43 	}
44 
BufferChanged()45 	void BufferChanged()
46 	{
47 		fPathLen = strlen(fPath);
48 	}
49 
PushLeaf(const char * leaf)50 	bool PushLeaf(const char* leaf)
51 	{
52 		size_t leafLen = strlen(leaf);
53 
54 		int separatorLen = (fPath[fPathLen - 1] == '/' ? 0 : 1);
55 		if (fPathLen + separatorLen + leafLen >= PATH_MAX)
56 			return false;
57 
58 		if (separatorLen > 0)
59 			fPath[fPathLen++] = '/';
60 
61 		strcpy(fPath + fPathLen, leaf);
62 		fPathLen += leafLen;
63 
64 		return true;
65 	}
66 
PopLeaf()67 	bool PopLeaf()
68 	{
69 		char* lastSlash = strrchr(fPath, '/');
70 		if (lastSlash == NULL || lastSlash == fPath)
71 			return false;
72 
73 		*lastSlash = '\0';
74 		fPathLen = lastSlash - fPath;
75 
76 		return true;
77 	}
78 
79 	char	fPath[PATH_MAX];
80 	size_t	fPathLen;
81 };
82 
83 
84 static bool remove_entry(Path& entry, bool recursive, bool force,
85 	bool removeAttributes);
86 
87 
88 static void
remove_dir_contents(Path & path,bool force,bool removeAttributes)89 remove_dir_contents(Path& path, bool force, bool removeAttributes)
90 {
91 	// open the dir
92 	DIR* dir = opendir(path.GetPath());
93 	if (dir == NULL) {
94 		fprintf(stderr, "Error: Failed to open dir \"%s\": %s\n",
95 			path.GetPath(), strerror(errno));
96 		return;
97 	}
98 
99 	// iterate through the entries
100 	errno = 0;
101 	while (dirent* entry = readdir(dir)) {
102 		// skip "." and ".."
103 		if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
104 			continue;
105 
106 		if (!path.PushLeaf(entry->d_name)) {
107 			fprintf(stderr, "Error: Path name of entry too long: dir: \"%s\", "
108 				"entry: \"%s\"\n", path.GetPath(), entry->d_name);
109 			continue;
110 		}
111 
112 		remove_entry(path, true, force, removeAttributes);
113 
114 		path.PopLeaf();
115 
116 		errno = 0;
117 	}
118 
119 	if (errno != 0) {
120 		fprintf(stderr, "Error: Failed to read directory \"%s\": %s\n",
121 			path.GetPath(), strerror(errno));
122 	}
123 
124 	// close
125 	closedir(dir);
126 }
127 
128 
129 static bool
remove_entry(Path & path,bool recursive,bool force,bool removeAttributes)130 remove_entry(Path& path, bool recursive, bool force, bool removeAttributes)
131 {
132 	// stat the file
133 	struct stat st;
134 	if (lstat(path.GetPath(), &st) < 0) {
135 		// errno == 0 shouldn't happen, but found on OpenSUSE Linux 10.3
136 		if (force && (errno == ENOENT || errno == 0))
137 			return true;
138 
139 		fprintf(stderr, "Error: Failed to remove \"%s\": %s\n", path.GetPath(),
140 			strerror(errno));
141 		return false;
142 	}
143 
144 	// remove the file's attributes
145 	if (removeAttributes) {
146 		Path attrDirPath;
147 		if (__get_attribute_dir_path(&st, path.GetPath(),
148 				attrDirPath.Buffer())) {
149 			attrDirPath.BufferChanged();
150 			remove_entry(attrDirPath, true, true, false);
151 		}
152 	}
153 
154 	if (S_ISDIR(st.st_mode)) {
155 		if (!recursive) {
156 			fprintf(stderr, "Error: \"%s\" is a directory.\n", path.GetPath());
157 			return false;
158 		}
159 
160 		// remove the contents
161 		remove_dir_contents(path, force, removeAttributes);
162 
163 		// remove the directory
164 		if (rmdir(path.GetPath()) < 0) {
165 			fprintf(stderr, "Error: Failed to remove directory \"%s\": %s\n",
166 				path.GetPath(), strerror(errno));
167 			return false;
168 		}
169 	} else {
170 		// remove the entry
171 		if (unlink(path.GetPath()) < 0) {
172 			fprintf(stderr, "Error: Failed to remove entry \"%s\": %s\n",
173 				path.GetPath(), strerror(errno));
174 			return false;
175 		}
176 	}
177 
178 	return true;
179 }
180 
181 
182 int
main(int argc,const char * const * argv)183 main(int argc, const char* const* argv)
184 {
185 	bool recursive = false;
186 	bool force = false;
187 
188 	// parse parameters
189 	int argi = 1;
190 	for (argi = 1; argi < argc; argi++) {
191 		const char *arg = argv[argi];
192 		if (arg[0] != '-')
193 			break;
194 
195 		if (arg[1] == '\0') {
196 			fprintf(stderr, "Error: Invalid option \"-\"\n");
197 			exit(1);
198 		}
199 
200 		for (int i = 1; arg[i]; i++) {
201 			switch (arg[i]) {
202 				case 'f':
203 					force = true;
204 					break;
205 				case 'r':
206 					recursive = true;
207 					break;
208 				default:
209 					fprintf(stderr, "Error: Unknown option \"-%c\"\n", arg[i]);
210 					exit(1);
211 			}
212 		}
213 	}
214 
215 	// check params
216 	if (argi >= argc) {
217 		fprintf(stderr, "Usage: %s [ -rf ] <file>...\n", argv[0]);
218 		exit(1);
219 	}
220 
221 	// remove loop
222 	for (; argi < argc; argi++) {
223 		Path path;
224 		if (!path.Init(argv[argi])) {
225 			fprintf(stderr, "Error: Invalid path: \"%s\".\n", argv[argi]);
226 			continue;
227 		}
228 
229 		remove_entry(path, recursive, force, true);
230 	}
231 
232 	return 0;
233 }
234