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