xref: /haiku/src/build/libroot/fs_attr.cpp (revision 5412911f7f8ca41340b0f5cb928ed9726322ab44)
1 
2 #ifdef BUILDING_FS_SHELL
3 #	include "compat.h"
4 #	define B_OK			0
5 #	define B_BAD_VALUE	EINVAL
6 #	define B_FILE_ERROR	EBADF
7 #else
8 #	include <BeOSBuildCompatibility.h>
9 #	include <syscalls.h>
10 #endif
11 
12 #include <dirent.h>
13 #include <errno.h>
14 #include <fcntl.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <unistd.h>
18 #include <sys/stat.h>
19 
20 #include <string>
21 
22 #include <fs_attr.h>
23 
24 #include "fs_attr_impl.h"
25 
26 using namespace std;
27 using namespace BPrivate;
28 
29 static const char *sAttributeDirBasePath = HAIKU_BUILD_ATTRIBUTES_DIR;
30 
31 // init_attribute_dir_base_dir
32 static status_t
33 init_attribute_dir_base_dir()
34 {
35 	static bool initialized = false;
36 	static status_t initError;
37 
38 	if (initialized)
39 		return initError;
40 
41 	// stat the dir
42 	struct stat st;
43 	initError = B_OK;
44 	if (lstat(sAttributeDirBasePath, &st) == 0) {
45 		if (!S_ISDIR(st.st_mode)) {
46 			// the attribute dir base dir is no directory
47 			fprintf(stderr, "init_attribute_dir_base_dir(): The Attribute "
48 				"directory base directory exists, but is no directory!\n");
49 			initError = B_FILE_ERROR;
50 		}
51 
52 	} else {
53 		// doesn't exist yet: create it
54 		if (mkdir(sAttributeDirBasePath, S_IRWXU | S_IRWXG | S_IRWXO) < 0)
55 			initError = errno;
56 	}
57 
58 	initialized = true;
59 	return initError;
60 }
61 
62 // escape_attr_name
63 static string
64 escape_attr_name(const char *name)
65 {
66 	string escapedName("_");
67 	while (*name != '\0') {
68 		// we replace '/' with "_s" and '_' with "__"
69 		if (*name == '/')
70 			escapedName += "_s";
71 		else if (*name == '_')
72 			escapedName += "__";
73 		else
74 			escapedName += *name;
75 
76 		name++;
77 	}
78 
79 	return escapedName;
80 }
81 
82 // deescape_attr_name
83 static string
84 deescape_attr_name(const char *name)
85 {
86 	if (name[0] != '_') {
87 		debugger("deescape_attr_name(): name doesn't start with '_'!\n");
88 		return "___";
89 	}
90 	name++;
91 
92 	string deescapedName;
93 	while (*name != '\0') {
94 		if (*name == '_') {
95 			name++;
96 			if (*name == 's') {
97 				deescapedName += '/';
98 			} else if (*name == '_') {
99 				deescapedName += '_';
100 			} else {
101 				debugger("deescape_attr_name(): name contains invalid escaped "
102 					"sequence!\n");
103 				name--;
104 			}
105 		} else
106 			deescapedName += *name;
107 
108 		name++;
109 	}
110 
111 	return deescapedName;
112 }
113 
114 // get_attribute_dir_path
115 static string
116 get_attribute_dir_path(NodeRef ref)
117 {
118 	string attrDirPath(sAttributeDirBasePath);
119 	char buffer[32];
120 	sprintf(buffer, "/%lld", (int64)ref.node);
121 	attrDirPath += buffer;
122 	return attrDirPath;
123 }
124 
125 // ensure_attribute_dir_exists
126 static status_t
127 ensure_attribute_dir_exists(NodeRef ref)
128 {
129 	// init the base directory here
130 	status_t error = init_attribute_dir_base_dir();
131 	if (error != B_OK)
132 		return error;
133 
134 	// stat the dir
135 	string attrDirPath(get_attribute_dir_path(ref));
136 	struct stat st;
137 	if (lstat(attrDirPath.c_str(), &st) == 0) {
138 		if (!S_ISDIR(st.st_mode)) {
139 			// the attribute dir is no directory
140 			fprintf(stderr, "ensure_attribute_dir_exists(): Attribute "
141 				"directory for node %lld exists, but is no directory!\n");
142 			return B_FILE_ERROR;
143 		}
144 
145 		return B_OK;
146 	}
147 
148 	// doesn't exist yet: create it
149 	if (mkdir(attrDirPath.c_str(), S_IRWXU | S_IRWXG | S_IRWXO) < 0)
150 		return errno;
151 	return B_OK;
152 }
153 
154 // open_attr_dir
155 DIR *
156 BPrivate::open_attr_dir(NodeRef ref)
157 {
158 	// make sure the directory exists
159 	status_t error = ensure_attribute_dir_exists(ref);
160 	if (error != B_OK) {
161 		errno = error;
162 		return NULL;
163 	}
164 
165 	// open it
166 	string dirPath(get_attribute_dir_path(ref));
167 	return opendir(dirPath.c_str());
168 }
169 
170 // get_attribute_path
171 status_t
172 BPrivate::get_attribute_path(NodeRef ref, const char *attribute,
173 	string &attrPath, string &typePath)
174 {
175 	if (!attribute || strlen(attribute) == 0)
176 		return B_BAD_VALUE;
177 
178 	// make sure the attribute dir for the node exits
179 	status_t error = ensure_attribute_dir_exists(ref);
180 	if (error != B_OK) {
181 		errno = error;
182 		return -1;
183 	}
184 
185 	// construct the attribute path
186 	attrPath = get_attribute_dir_path(ref) + '/';
187 	string attrName(escape_attr_name(attribute));
188 	typePath = attrPath + "t" + attrName;
189 	attrPath += attrName;
190 
191 	return B_OK;
192 }
193 
194 // get_attribute_path
195 static status_t
196 get_attribute_path(int fd, const char *attribute, string &attrPath,
197 	string &typePath)
198 {
199 	// stat the file to get a NodeRef
200 	struct stat st;
201 	if (fstat(fd, &st) < 0)
202 		return errno;
203 	NodeRef ref(st);
204 
205 	return get_attribute_path(ref, attribute, attrPath, typePath);
206 }
207 
208 // fs_open_attr_dir
209 DIR *
210 fs_open_attr_dir(const char *path)
211 {
212 	struct stat st;
213 	if (lstat(path, &st))
214 		return NULL;
215 
216 	return open_attr_dir(NodeRef(st));
217 }
218 
219 // fs_fopen_attr_dir
220 DIR *
221 fs_fopen_attr_dir(int fd)
222 {
223 	struct stat st;
224 
225 #ifdef BUILDING_FS_SHELL
226 
227 	if (fstat(fd, &st) < 0)
228 		return NULL;
229 
230 #else
231 
232 	status_t error = _kern_read_stat(fd, NULL, false, &st,
233 		sizeof(struct stat));
234 	if (error != B_OK) {
235 		errno = error;
236 		return NULL;
237 	}
238 
239 #endif
240 
241 	return open_attr_dir(NodeRef(st));
242 }
243 
244 // fs_close_attr_dir
245 int
246 fs_close_attr_dir(DIR *dir)
247 {
248 	return closedir(dir);
249 }
250 
251 // fs_read_attr_dir
252 struct dirent *
253 fs_read_attr_dir(DIR *dir)
254 {
255 	struct dirent *entry = NULL;
256 	while (true) {
257 		// read the next entry
258 		entry = readdir(dir);
259 		if (!entry)
260 			return NULL;
261 
262 		// ignore administrative entries; the
263 		if (entry->d_name[0] == '_') {
264 			string attrName = deescape_attr_name(entry->d_name);
265 			strcpy(entry->d_name, attrName.c_str());
266 			return entry;
267 		}
268 	}
269 }
270 
271 // fs_rewind_attr_dir
272 void
273 fs_rewind_attr_dir(DIR *dir)
274 {
275 	rewinddir(dir);
276 }
277 
278 // fs_open_attr
279 int
280 fs_open_attr(int fd, const char *attribute, uint32 type, int openMode)
281 {
282 	if (!attribute) {
283 		errno = B_BAD_VALUE;
284 		return -1;
285 	}
286 
287 	// get the attribute path
288 	string attrPath;
289 	string typePath;
290 	status_t error = get_attribute_path(fd, attribute, attrPath, typePath);
291 	if (error != B_OK) {
292 		errno = error;
293 		return -1;
294 	}
295 
296 	// check, if the attribute already exists
297 	struct stat st;
298 	bool exists = (lstat(attrPath.c_str(), &st) == 0);
299 
300 	// open the attribute
301 	int attrFD = open(attrPath.c_str(), openMode, S_IRWXU | S_IRWXG | S_IRWXO);
302 	if (attrFD < 0)
303 		return -1;
304 
305 	// set the type, if the attribute didn't exist yet
306 	if (!exists) {
307 		// create a file prefixed "t"
308 		int typeFD = creat(typePath.c_str(), S_IRWXU | S_IRWXG | S_IRWXO);
309 		if (typeFD >= 0) {
310 			// write the type into the file
311 			if (write(typeFD, &type, sizeof(type)) < 0)
312 				error = errno;
313 
314 			close(typeFD);
315 
316 		} else
317 			error = errno;
318 
319 		// remove type and attribute file, if something went wrong
320 		if (error != B_OK) {
321 			if (typeFD > 0) {
322 				unlink(typePath.c_str());
323 			}
324 
325 			close(attrFD);
326 			unlink(attrPath.c_str());
327 
328 			errno = error;
329 			return -1;
330 		}
331 	}
332 
333 	return attrFD;
334 }
335 
336 // fs_close_attr
337 int
338 fs_close_attr(int fd)
339 {
340 	return close(fd);
341 }
342 
343 // fs_read_attr
344 ssize_t
345 fs_read_attr(int fd, const char *attribute, uint32 type, off_t pos,
346 	void *buffer, size_t readBytes)
347 {
348 	// open the attribute
349 	int attrFD = fs_open_attr(fd, attribute, type, O_RDONLY);
350 	if (attrFD < 0)
351 		return attrFD;
352 
353 	// read
354 	ssize_t bytesRead = read_pos(attrFD, pos, buffer, readBytes);
355 	status_t error = errno;
356 
357 	// close the attribute
358 	fs_close_attr(attrFD);
359 
360 	if (bytesRead < 0) {
361 		errno = error;
362 		return -1;
363 	}
364 
365 	return bytesRead;
366 }
367 
368 // fs_write_attr
369 ssize_t
370 fs_write_attr(int fd, const char *attribute, uint32 type, off_t pos,
371 	const void *buffer, size_t readBytes)
372 {
373 	// open the attribute
374 	int attrFD = fs_open_attr(fd, attribute, type,
375 		O_WRONLY | O_CREAT | O_TRUNC);
376 	if (attrFD < 0)
377 		return attrFD;
378 
379 	// read
380 	ssize_t bytesWritten = write_pos(attrFD, pos, buffer, readBytes);
381 	status_t error = errno;
382 
383 	// close the attribute
384 	fs_close_attr(attrFD);
385 
386 	if (bytesWritten < 0) {
387 		errno = error;
388 		return -1;
389 	}
390 
391 	return bytesWritten;
392 }
393 
394 // fs_remove_attr
395 int
396 fs_remove_attr(int fd, const char *attribute)
397 {
398 	if (!attribute) {
399 		errno = B_BAD_VALUE;
400 		return -1;
401 	}
402 
403 	// get the attribute path
404 	string attrPath;
405 	string typePath;
406 	status_t error = get_attribute_path(fd, attribute, attrPath, typePath);
407 	if (error != B_OK) {
408 		errno = error;
409 		return -1;
410 	}
411 
412 	// remove the attribute
413 	if (unlink(attrPath.c_str()) < 0)
414 		return -1;
415 
416 	unlink(typePath.c_str());
417 
418 	return B_OK;
419 }
420 
421 // fs_stat_attr
422 int
423 fs_stat_attr(int fd, const char *attribute, struct attr_info *attrInfo)
424 {
425 	if (!attribute || !attrInfo) {
426 		errno = B_BAD_VALUE;
427 		return -1;
428 	}
429 
430 	// get the attribute path
431 	string attrPath;
432 	string typePath;
433 	status_t error = get_attribute_path(fd, attribute, attrPath, typePath);
434 	if (error != B_OK) {
435 		errno = error;
436 		return -1;
437 	}
438 
439 	// stat the attribute file to get the size of the attribute
440 	struct stat st;
441 	if (lstat(attrPath.c_str(), &st) < 0)
442 		return -1;
443 
444 	attrInfo->size = st.st_size;
445 
446 	// now open the attribute type file and read the attribute's type
447 	int typeFD = open(typePath.c_str(), O_RDONLY);
448 	if (typeFD < 0)
449 		return -1;
450 
451 	ssize_t bytesRead = read(typeFD, &attrInfo->type, sizeof(attrInfo->type));
452 	if (bytesRead < 0)
453 		error = errno;
454 	else if (bytesRead < (ssize_t)sizeof(attrInfo->type))
455 		error = B_FILE_ERROR;
456 
457 	close(typeFD);
458 
459 	// fail on error
460 	if (error != B_OK) {
461 		errno = error;
462 		return -1;
463 	}
464 
465 	return 0;
466 }
467 
468