vfs: check userland buffers before reading them.
[haiku.git] / src / tools / rm_attrs.cpp
blobbae1b69556866874f9754f942d4a24845de0b3cf
1 /*
2 * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
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>
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);
21 class Path {
22 public:
23 bool Init(const char* path)
25 size_t len = strlen(path);
26 if (len == 0 || len >= PATH_MAX)
27 return false;
29 strcpy(fPath, path);
30 fPathLen = len;
32 return true;
35 const char* GetPath() const
37 return fPath;
40 char* Buffer()
42 return fPath;
45 void BufferChanged()
47 fPathLen = strlen(fPath);
50 bool PushLeaf(const char* leaf)
52 size_t leafLen = strlen(leaf);
54 int separatorLen = (fPath[fPathLen - 1] == '/' ? 0 : 1);
55 if (fPathLen + separatorLen + leafLen >= PATH_MAX)
56 return false;
58 if (separatorLen > 0)
59 fPath[fPathLen++] = '/';
61 strcpy(fPath + fPathLen, leaf);
62 fPathLen += leafLen;
64 return true;
67 bool PopLeaf()
69 char* lastSlash = strrchr(fPath, '/');
70 if (lastSlash == NULL || lastSlash == fPath)
71 return false;
73 *lastSlash = '\0';
74 fPathLen = lastSlash - fPath;
76 return true;
79 char fPath[PATH_MAX];
80 size_t fPathLen;
84 static bool remove_entry(Path& entry, bool recursive, bool force,
85 bool removeAttributes);
88 static void
89 remove_dir_contents(Path& path, bool force, bool removeAttributes)
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;
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;
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;
112 remove_entry(path, true, force, removeAttributes);
114 path.PopLeaf();
116 errno = 0;
119 if (errno != 0) {
120 fprintf(stderr, "Error: Failed to read directory \"%s\": %s\n",
121 path.GetPath(), strerror(errno));
124 // close
125 closedir(dir);
129 static bool
130 remove_entry(Path& path, bool recursive, bool force, bool removeAttributes)
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;
139 fprintf(stderr, "Error: Failed to remove \"%s\": %s\n", path.GetPath(),
140 strerror(errno));
141 return false;
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);
154 if (S_ISDIR(st.st_mode)) {
155 if (!recursive) {
156 fprintf(stderr, "Error: \"%s\" is a directory.\n", path.GetPath());
157 return false;
160 // remove the contents
161 remove_dir_contents(path, force, removeAttributes);
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;
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;
178 return true;
183 main(int argc, const char* const* argv)
185 bool recursive = false;
186 bool force = false;
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;
195 if (arg[1] == '\0') {
196 fprintf(stderr, "Error: Invalid option \"-\"\n");
197 exit(1);
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);
215 // check params
216 if (argi >= argc) {
217 fprintf(stderr, "Usage: %s [ -rf ] <file>...\n", argv[0]);
218 exit(1);
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;
229 remove_entry(path, recursive, force, true);
232 return 0;