xref: /haiku/src/build/libroot/fs_attr_generic.cpp (revision b028e77473189065f2baefc6f5e10d451cf591e2)
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_impl.h"
25 #include "fs_descriptors.h"
26 
27 
28 using namespace std;
29 using namespace BPrivate;
30 
31 static const char *sAttributeDirBasePath = HAIKU_BUILD_ATTRIBUTES_DIR;
32 
33 // init_attribute_dir_base_dir
34 static status_t
35 init_attribute_dir_base_dir()
36 {
37 	static bool initialized = false;
38 	static status_t initError;
39 
40 	if (initialized)
41 		return initError;
42 
43 	// stat the dir
44 	struct stat st;
45 	initError = B_OK;
46 	if (lstat(sAttributeDirBasePath, &st) == 0) {
47 		if (!S_ISDIR(st.st_mode)) {
48 			// the attribute dir base dir is no directory
49 			fprintf(stderr, "init_attribute_dir_base_dir(): The Attribute "
50 				"directory base directory exists, but is no directory!\n");
51 			initError = B_FILE_ERROR;
52 		}
53 
54 	} else {
55 		// doesn't exist yet: create it
56 		if (mkdir(sAttributeDirBasePath, S_IRWXU | S_IRWXG | S_IRWXO) < 0)
57 			initError = errno;
58 	}
59 
60 	initialized = true;
61 	return initError;
62 }
63 
64 // escape_attr_name
65 static string
66 escape_attr_name(const char *name)
67 {
68 	string escapedName("_");
69 	while (*name != '\0') {
70 		// we replace '/' with "_s" and '_' with "__"
71 		if (*name == '/')
72 			escapedName += "_s";
73 		else if (*name == '_')
74 			escapedName += "__";
75 		else
76 			escapedName += *name;
77 
78 		name++;
79 	}
80 
81 	return escapedName;
82 }
83 
84 // deescape_attr_name
85 static string
86 deescape_attr_name(const char *name)
87 {
88 	if (name[0] != '_') {
89 		debugger("deescape_attr_name(): name doesn't start with '_'!\n");
90 		return "___";
91 	}
92 	name++;
93 
94 	string deescapedName;
95 	while (*name != '\0') {
96 		if (*name == '_') {
97 			name++;
98 			if (*name == 's') {
99 				deescapedName += '/';
100 			} else if (*name == '_') {
101 				deescapedName += '_';
102 			} else {
103 				debugger("deescape_attr_name(): name contains invalid escaped "
104 					"sequence!\n");
105 				name--;
106 			}
107 		} else
108 			deescapedName += *name;
109 
110 		name++;
111 	}
112 
113 	return deescapedName;
114 }
115 
116 // get_attribute_dir_path
117 static string
118 get_attribute_dir_path(NodeRef ref)
119 {
120 	string attrDirPath(sAttributeDirBasePath);
121 	char buffer[32];
122 	sprintf(buffer, "/%lld", (int64)ref.node);
123 	attrDirPath += buffer;
124 	return attrDirPath;
125 }
126 
127 // ensure_attribute_dir_exists
128 static status_t
129 ensure_attribute_dir_exists(NodeRef ref, const char *path, int fd)
130 {
131 	// init the base directory here
132 	status_t error = init_attribute_dir_base_dir();
133 	if (error != B_OK)
134 		return error;
135 
136 	// stat the dir
137 	string attrDirPath(get_attribute_dir_path(ref));
138 	struct stat st;
139 	if (lstat(attrDirPath.c_str(), &st) == 0) {
140 		if (!S_ISDIR(st.st_mode)) {
141 			// the attribute dir is no directory
142 			fprintf(stderr, "ensure_attribute_dir_exists(): Attribute "
143 				"directory for node %lld exists, but is no directory!\n",
144 				ref.node);
145 			return B_FILE_ERROR;
146 		}
147 
148 		return B_OK;
149 	}
150 
151 	// doesn't exist yet: create it
152 	if (mkdir(attrDirPath.c_str(), S_IRWXU | S_IRWXG | S_IRWXO) < 0)
153 		return errno;
154 
155 	return B_OK;
156 }
157 
158 // open_attr_dir
159 static DIR *
160 open_attr_dir(NodeRef ref, const char *path, int fd)
161 {
162 	// make sure the directory exists
163 	status_t error = ensure_attribute_dir_exists(ref, path, fd);
164 	if (error != B_OK) {
165 		errno = error;
166 		return NULL;
167 	}
168 
169 	// open it
170 	string dirPath(get_attribute_dir_path(ref));
171 	return opendir(dirPath.c_str());
172 }
173 
174 // get_attribute_path
175 static status_t
176 get_attribute_path(NodeRef ref, const char *path, int fd,
177 	const char *attribute, string &attrPath, string &typePath)
178 {
179 	if (!attribute || strlen(attribute) == 0)
180 		return B_BAD_VALUE;
181 
182 	// make sure the attribute dir for the node exits
183 	status_t error = ensure_attribute_dir_exists(ref, path, fd);
184 	if (error != B_OK) {
185 		errno = error;
186 		return -1;
187 	}
188 
189 	// construct the attribute path
190 	attrPath = get_attribute_dir_path(ref) + '/';
191 	string attrName(escape_attr_name(attribute));
192 	typePath = attrPath + "t" + attrName;
193 	attrPath += attrName;
194 
195 	return B_OK;
196 }
197 
198 // get_attribute_path
199 static status_t
200 get_attribute_path(int fd, const char *attribute, string &attrPath,
201 	string &typePath)
202 {
203 	// stat the file to get a NodeRef
204 	struct stat st;
205 	if (fstat(fd, &st) < 0)
206 		return errno;
207 	NodeRef ref(st);
208 
209 	return get_attribute_path(ref, NULL, fd, attribute, attrPath, typePath);
210 }
211 
212 
213 #ifndef BUILDING_FS_SHELL
214 
215 // get_attribute_path_virtual_fd
216 static status_t
217 get_attribute_path_virtual_fd(int fd, const char *attribute, string &attrPath,
218 	string &typePath)
219 {
220 	// stat the file to get a NodeRef
221 	struct stat st;
222 	status_t error = _kern_read_stat(fd, NULL, false, &st, sizeof(st));
223 	if (error != B_OK)
224 		return error;
225 	NodeRef ref(st);
226 
227 	// Try to get a path. If we can't get a path, this is must be a "real"
228 	// (i.e. system) file descriptor, which is just as well.
229 	string path;
230 	bool pathValid = (get_path(fd, NULL, path) == B_OK);
231 
232 	// get the attribute path
233 	return get_attribute_path(ref, (pathValid ? path.c_str() : NULL),
234 		(pathValid ? -1 : fd), attribute, attrPath, typePath);
235 }
236 
237 #endif	// ! BUILDING_FS_SHELL
238 
239 
240 // # pragma mark - Public API
241 
242 
243 // fs_open_attr_dir
244 DIR *
245 fs_open_attr_dir(const char *path)
246 {
247 	struct stat st;
248 	if (lstat(path, &st))
249 		return NULL;
250 
251 	return open_attr_dir(NodeRef(st), path, -1);
252 }
253 
254 // fs_fopen_attr_dir
255 DIR *
256 fs_fopen_attr_dir(int fd)
257 {
258 	struct stat st;
259 
260 #ifdef BUILDING_FS_SHELL
261 
262 	if (fstat(fd, &st) < 0)
263 		return NULL;
264 
265 	return open_attr_dir(NodeRef(st), NULL, fd);
266 
267 #else
268 
269 	status_t error = _kern_read_stat(fd, NULL, false, &st,
270 		sizeof(struct stat));
271 	if (error != B_OK) {
272 		errno = error;
273 		return NULL;
274 	}
275 
276 	// Try to get a path. If we can't get a path, this is must be a "real"
277 	// (i.e. system) file descriptor, which is just as well.
278 	string path;
279 	bool pathValid = (get_path(fd, NULL, path) == B_OK);
280 
281 	// get the attribute path
282 	return open_attr_dir(NodeRef(st), (pathValid ? path.c_str() : NULL),
283 		(pathValid ? -1 : fd));
284 
285 #endif
286 }
287 
288 // fs_close_attr_dir
289 int
290 fs_close_attr_dir(DIR *dir)
291 {
292 	return closedir(dir);
293 }
294 
295 // fs_read_attr_dir
296 struct dirent *
297 fs_read_attr_dir(DIR *dir)
298 {
299 	struct dirent *entry = NULL;
300 	while (true) {
301 		// read the next entry
302 		entry = readdir(dir);
303 		if (!entry)
304 			return NULL;
305 
306 		// ignore administrative entries; the
307 		if (entry->d_name[0] == '_') {
308 			string attrName = deescape_attr_name(entry->d_name);
309 			strcpy(entry->d_name, attrName.c_str());
310 			return entry;
311 		}
312 	}
313 }
314 
315 // fs_rewind_attr_dir
316 void
317 fs_rewind_attr_dir(DIR *dir)
318 {
319 	rewinddir(dir);
320 }
321 
322 // fs_open_attr
323 int
324 fs_open_attr(int fd, const char *attribute, uint32 type, int openMode)
325 {
326 	if (!attribute) {
327 		errno = B_BAD_VALUE;
328 		return -1;
329 	}
330 
331 	// get the attribute path
332 	string attrPath;
333 	string typePath;
334 	status_t error = get_attribute_path(fd, attribute, attrPath, typePath);
335 	if (error != B_OK) {
336 		errno = error;
337 		return -1;
338 	}
339 
340 	// check, if the attribute already exists
341 	struct stat st;
342 	bool exists = (lstat(attrPath.c_str(), &st) == 0);
343 
344 	// open the attribute
345 	int attrFD = open(attrPath.c_str(), openMode, S_IRWXU | S_IRWXG | S_IRWXO);
346 	if (attrFD < 0)
347 		return -1;
348 
349 	// set the type, if the attribute didn't exist yet
350 	if (!exists) {
351 		// create a file prefixed "t"
352 		int typeFD = creat(typePath.c_str(), S_IRWXU | S_IRWXG | S_IRWXO);
353 		if (typeFD >= 0) {
354 			// write the type into the file
355 			if (write(typeFD, &type, sizeof(type)) < 0)
356 				error = errno;
357 
358 			close(typeFD);
359 
360 		} else
361 			error = errno;
362 
363 		// remove type and attribute file, if something went wrong
364 		if (error != B_OK) {
365 			if (typeFD > 0) {
366 				unlink(typePath.c_str());
367 			}
368 
369 			close(attrFD);
370 			unlink(attrPath.c_str());
371 
372 			errno = error;
373 			return -1;
374 		}
375 	}
376 
377 	return attrFD;
378 }
379 
380 // fs_close_attr
381 int
382 fs_close_attr(int fd)
383 {
384 	return close(fd);
385 }
386 
387 // fs_read_attr
388 ssize_t
389 fs_read_attr(int fd, const char *attribute, uint32 type, off_t pos,
390 	void *buffer, size_t readBytes)
391 {
392 	// open the attribute
393 	int attrFD = fs_open_attr(fd, attribute, type, O_RDONLY);
394 	if (attrFD < 0)
395 		return attrFD;
396 
397 	// read
398 	ssize_t bytesRead = read_pos(attrFD, pos, buffer, readBytes);
399 	status_t error = errno;
400 
401 	// close the attribute
402 	fs_close_attr(attrFD);
403 
404 	if (bytesRead < 0) {
405 		errno = error;
406 		return -1;
407 	}
408 
409 	return bytesRead;
410 }
411 
412 // fs_write_attr
413 ssize_t
414 fs_write_attr(int fd, const char *attribute, uint32 type, off_t pos,
415 	const void *buffer, size_t readBytes)
416 {
417 	// open the attribute
418 	int attrFD = fs_open_attr(fd, attribute, type,
419 		O_WRONLY | O_CREAT | O_TRUNC);
420 	if (attrFD < 0)
421 		return attrFD;
422 
423 	// read
424 	ssize_t bytesWritten = write_pos(attrFD, pos, buffer, readBytes);
425 	status_t error = errno;
426 
427 	// close the attribute
428 	fs_close_attr(attrFD);
429 
430 	if (bytesWritten < 0) {
431 		errno = error;
432 		return -1;
433 	}
434 
435 	return bytesWritten;
436 }
437 
438 // fs_remove_attr
439 int
440 fs_remove_attr(int fd, const char *attribute)
441 {
442 	if (!attribute) {
443 		errno = B_BAD_VALUE;
444 		return -1;
445 	}
446 
447 	// get the attribute path
448 	string attrPath;
449 	string typePath;
450 	status_t error = get_attribute_path(fd, attribute, attrPath, typePath);
451 	if (error != B_OK) {
452 		errno = error;
453 		return -1;
454 	}
455 
456 	// remove the attribute
457 	if (unlink(attrPath.c_str()) < 0)
458 		return -1;
459 
460 	unlink(typePath.c_str());
461 
462 	return B_OK;
463 }
464 
465 // fs_stat_attr
466 int
467 fs_stat_attr(int fd, const char *attribute, struct attr_info *attrInfo)
468 {
469 	if (!attribute || !attrInfo) {
470 		errno = B_BAD_VALUE;
471 		return -1;
472 	}
473 
474 	// get the attribute path
475 	string attrPath;
476 	string typePath;
477 	status_t error = get_attribute_path(fd, attribute, attrPath, typePath);
478 	if (error != B_OK) {
479 		errno = error;
480 		return -1;
481 	}
482 
483 	// stat the attribute file to get the size of the attribute
484 	struct stat st;
485 	if (lstat(attrPath.c_str(), &st) < 0)
486 		return -1;
487 
488 	attrInfo->size = st.st_size;
489 
490 	// now open the attribute type file and read the attribute's type
491 	int typeFD = open(typePath.c_str(), O_RDONLY);
492 	if (typeFD < 0)
493 		return -1;
494 
495 	ssize_t bytesRead = read(typeFD, &attrInfo->type, sizeof(attrInfo->type));
496 	if (bytesRead < 0)
497 		error = errno;
498 	else if (bytesRead < (ssize_t)sizeof(attrInfo->type))
499 		error = B_FILE_ERROR;
500 
501 	close(typeFD);
502 
503 	// fail on error
504 	if (error != B_OK) {
505 		errno = error;
506 		return -1;
507 	}
508 
509 	return 0;
510 }
511 
512 
513 // #pragma mark - Private Syscalls
514 
515 
516 #ifndef BUILDING_FS_SHELL
517 
518 // _kern_open_attr_dir
519 int
520 _kern_open_attr_dir(int fd, const char *path)
521 {
522 	// get node ref for the node
523 	struct stat st;
524 	status_t error = _kern_read_stat(fd, path, false, &st,
525 		sizeof(struct stat));
526 	if (error != B_OK) {
527 		errno = error;
528 		return -1;
529 	}
530 	NodeRef ref(st);
531 
532 	// If a path was given, get a usable path.
533 	string realPath;
534 	if (path) {
535 		error = get_path(fd, path, realPath);
536 		if (error != B_OK)
537 			return error;
538 	}
539 
540 	// open the attr dir
541 	DIR *dir = open_attr_dir(ref, (path ? realPath.c_str() : NULL),
542 		(path ? -1 : fd));
543 	if (!dir)
544 		return errno;
545 
546 	// create descriptor
547 	AttrDirDescriptor *descriptor = new AttrDirDescriptor(dir, ref);
548 	return add_descriptor(descriptor);
549 }
550 
551 // _kern_rename_attr
552 status_t
553 _kern_rename_attr(int fromFile, const char *fromName, int toFile,
554 	const char *toName)
555 {
556 	if (!fromName || !toName)
557 		return B_BAD_VALUE;
558 
559 	// get the attribute paths
560 	string fromAttrPath;
561 	string fromTypePath;
562 	status_t error = get_attribute_path_virtual_fd(fromFile, fromName,
563 		fromAttrPath, fromTypePath);
564 	if (error != B_OK)
565 		return error;
566 
567 	string toAttrPath;
568 	string toTypePath;
569 	error = get_attribute_path_virtual_fd(toFile, toName, toAttrPath,
570 		toTypePath);
571 	if (error != B_OK)
572 		return error;
573 
574 	// rename the attribute and type files
575 	if (rename(fromAttrPath.c_str(), toAttrPath.c_str()) < 0)
576 		return errno;
577 
578 	if (rename(fromTypePath.c_str(), toTypePath.c_str()) < 0) {
579 		// renaming the type file failed: try to rename back the attribute file
580 		error = errno;
581 
582 		rename(toAttrPath.c_str(), fromAttrPath.c_str());
583 
584 		return error;
585 	}
586 
587 	return B_OK;
588 }
589 
590 // _kern_remove_attr
591 status_t
592 _kern_remove_attr(int fd, const char *name)
593 {
594 	if (!name)
595 		return B_BAD_VALUE;
596 
597 	// get the attribute path
598 	string attrPath;
599 	string typePath;
600 	status_t error = get_attribute_path_virtual_fd(fd, name, attrPath,
601 		typePath);
602 	if (error != B_OK)
603 		return error;
604 
605 	// remove the attribute
606 	if (unlink(attrPath.c_str()) < 0)
607 		return errno;
608 
609 	unlink(typePath.c_str());
610 
611 	return B_OK;
612 }
613 
614 #endif	// ! BUILDING_FS_SHELL
615