fat: Greatly simplify and clean up dosfs_get_file_map().
[haiku.git] / src / build / libroot / fs_attr_generic.cpp
bloba86d4d555e69539ecb9db5c9fd7c442f223cac93
2 #include <BeOSBuildCompatibility.h>
3 #include <syscalls.h>
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>
13 #include <string>
15 #include <fs_attr.h>
17 #include "fs_impl.h"
18 #include "fs_descriptors.h"
21 // Include the interface to the host platform attributes support, if it shall be
22 // used to tag files with unique IDs to identify their attribute directory.
23 #if HAIKU_HOST_USE_XATTR_REF
24 # if defined(HAIKU_HOST_PLATFORM_LINUX)
25 # include "fs_attr_xattr.h"
26 # elif defined(HAIKU_HOST_PLATFORM_FREEBSD)
27 # include "fs_attr_extattr.h"
28 # elif defined(HAIKU_HOST_PLATFORM_DARWIN)
29 # include "fs_attr_bsdxattr.h"
30 # else
31 # error No attribute support for this host platform!
32 # endif
33 #endif
36 using namespace std;
37 using namespace BPrivate;
39 static const char *sAttributeDirBasePath = HAIKU_BUILD_ATTRIBUTES_DIR;
41 #if HAIKU_HOST_USE_XATTR_REF
42 static const char* const kIDAttributeName = "id";
43 #endif
46 // init_attribute_dir_base_dir
47 static status_t
48 init_attribute_dir_base_dir()
50 static bool initialized = false;
51 static status_t initError;
53 if (initialized)
54 return initError;
56 // stat the dir
57 struct stat st;
58 initError = B_OK;
59 if (lstat(sAttributeDirBasePath, &st) == 0) {
60 if (!S_ISDIR(st.st_mode)) {
61 // the attribute dir base dir is no directory
62 fprintf(stderr, "init_attribute_dir_base_dir(): The Attribute "
63 "directory base directory exists, but is no directory!\n");
64 initError = B_FILE_ERROR;
67 } else {
68 // doesn't exist yet: create it
69 if (mkdir(sAttributeDirBasePath, S_IRWXU | S_IRWXG | S_IRWXO) < 0)
70 initError = errno;
73 initialized = true;
74 return initError;
77 // escape_attr_name
78 static string
79 escape_attr_name(const char *name)
81 string escapedName("_");
82 while (*name != '\0') {
83 // we replace '/' with "_s" and '_' with "__"
84 if (*name == '/')
85 escapedName += "_s";
86 else if (*name == '_')
87 escapedName += "__";
88 else
89 escapedName += *name;
91 name++;
94 return escapedName;
97 // deescape_attr_name
98 static string
99 deescape_attr_name(const char *name)
101 if (name[0] != '_') {
102 debugger("deescape_attr_name(): name doesn't start with '_'!\n");
103 return "___";
105 name++;
107 string deescapedName;
108 while (*name != '\0') {
109 if (*name == '_') {
110 name++;
111 if (*name == 's') {
112 deescapedName += '/';
113 } else if (*name == '_') {
114 deescapedName += '_';
115 } else {
116 debugger("deescape_attr_name(): name contains invalid escaped "
117 "sequence!\n");
118 name--;
120 } else
121 deescapedName += *name;
123 name++;
126 return deescapedName;
130 #if HAIKU_HOST_USE_XATTR_REF
133 static status_t
134 make_unique_node_id(string& _id)
136 // open random device
137 int fd = open("/dev/urandom", O_RDONLY);
138 if (fd < 0) {
139 fd = open("/dev/random", O_RDONLY);
140 if (fd < 0)
141 return B_NOT_SUPPORTED;
144 // read bytes
145 uint8 buffer[16];
146 ssize_t bytesRead = read(fd, buffer, sizeof(buffer));
147 status_t error = B_OK;
148 if (bytesRead < 0)
149 error = errno;
150 close(fd);
152 if (error != B_OK)
153 return error;
155 if (bytesRead != (ssize_t)sizeof(buffer))
156 error = B_ERROR;
158 // convert to hex string
159 static const char* const kHexChars = "0123456789abcdef";
160 _id.clear();
161 for (size_t i = 0; i < sizeof(buffer); i++) {
162 _id += kHexChars[buffer[i] >> 4];
163 _id += kHexChars[buffer[i] & 0xf];
166 return B_OK;
170 static status_t
171 get_id_attribute(const char *path, int fd, string& _id)
173 // list_attributes() and remove_attribute() are unused here -- prevent the
174 // warning
175 (void)list_attributes;
176 (void)remove_attribute;
178 string attributeName(kAttributeNamespace);
179 attributeName += kIDAttributeName;
181 char buffer[64];
182 ssize_t bytesRead = get_attribute(fd, path, attributeName.c_str(), buffer,
183 sizeof(buffer));
184 if (bytesRead < 0) {
185 // On Linux only priviledged users are allowed to set attributes on
186 // symlinks. So, if this is a symlink, we don't even try and instead
187 // construct a symlink specific node ID.
188 status_t error = errno;
189 struct stat st;
190 if (path == NULL || lstat(path, &st) < 0 || !S_ISLNK(st.st_mode))
191 return error;
193 char buffer[32];
194 snprintf(buffer, sizeof(buffer), "symlink-%" B_PRIdINO, st.st_ino);
195 _id = buffer;
196 return B_OK;
199 _id = string(buffer, bytesRead);
200 return B_OK;
204 static status_t
205 set_id_attribute(const char *path, int fd, const char* id)
207 string attributeName(kAttributeNamespace);
208 attributeName += kIDAttributeName;
210 if (set_attribute(fd, path, attributeName.c_str(), id, strlen(id)) < 0)
211 return errno;
212 return B_OK;
216 static string
217 get_attribute_dir_path(NodeRef ref, const char *path, int fd)
219 string id;
220 status_t error = get_id_attribute(path, fd, id);
221 if (error != B_OK)
222 id = "_no_attributes_";
224 string attrDirPath(sAttributeDirBasePath);
225 attrDirPath += '/';
226 attrDirPath += id;
227 return attrDirPath;
231 static status_t
232 get_attribute_dir_path_needed(NodeRef ref, const char *path, int fd,
233 string& _attrDirPath)
235 string id;
236 status_t error = get_id_attribute(path, fd, id);
237 if (error != B_OK) {
238 error = make_unique_node_id(id);
239 if (error != B_OK)
240 return error;
242 error = set_id_attribute(path, fd, id.c_str());
243 if (error != B_OK)
244 return error;
247 _attrDirPath = sAttributeDirBasePath;
248 _attrDirPath += '/';
249 _attrDirPath += id;
250 return B_OK;
254 #else
257 static string
258 get_attribute_dir_path(NodeRef ref, const char *path, int fd)
260 string attrDirPath(sAttributeDirBasePath);
261 char buffer[32];
262 sprintf(buffer, "/%" B_PRIdINO, ref.node);
263 attrDirPath += buffer;
264 return attrDirPath;
268 static status_t
269 get_attribute_dir_path_needed(NodeRef ref, const char *path, int fd,
270 string& _attrDirPath)
272 _attrDirPath = get_attribute_dir_path(ref, path, fd);
273 return B_OK;
277 #endif
280 // ensure_attribute_dir_exists
281 static status_t
282 ensure_attribute_dir_exists(NodeRef ref, const char *path, int fd)
284 // init the base directory and get the attribute directory path
285 status_t error = init_attribute_dir_base_dir();
286 if (error != B_OK)
287 return error;
289 string attrDirPath;
290 error = get_attribute_dir_path_needed(ref, path, fd, attrDirPath);
291 if (error != B_OK)
292 return error;
294 // stat the dir
295 struct stat st;
296 if (lstat(attrDirPath.c_str(), &st) == 0) {
297 if (!S_ISDIR(st.st_mode)) {
298 // the attribute dir is no directory
299 fprintf(stderr, "ensure_attribute_dir_exists(): Attribute "
300 "directory for node %lld exists, but is no directory!\n",
301 (long long)ref.node);
302 return B_FILE_ERROR;
305 return B_OK;
308 // doesn't exist yet: create it
309 if (mkdir(attrDirPath.c_str(), S_IRWXU | S_IRWXG | S_IRWXO) < 0)
310 return errno;
312 return B_OK;
315 // open_attr_dir
316 static DIR *
317 open_attr_dir(NodeRef ref, const char *path, int fd)
319 // make sure the directory exists
320 status_t error = ensure_attribute_dir_exists(ref, path, fd);
321 if (error != B_OK) {
322 errno = error;
323 return NULL;
326 // open it
327 string dirPath(get_attribute_dir_path(ref, path, fd));
328 return opendir(dirPath.c_str());
331 // get_attribute_path
332 static status_t
333 get_attribute_path(NodeRef ref, const char *path, int fd,
334 const char *attribute, string &attrPath, string &typePath)
336 if (!attribute || strlen(attribute) == 0)
337 return B_BAD_VALUE;
339 // make sure the attribute dir for the node exits
340 status_t error = ensure_attribute_dir_exists(ref, path, fd);
341 if (error != B_OK) {
342 errno = error;
343 return -1;
346 // construct the attribute path
347 attrPath = get_attribute_dir_path(ref, path, fd) + '/';
348 string attrName(escape_attr_name(attribute));
349 typePath = attrPath + "t" + attrName;
350 attrPath += attrName;
352 return B_OK;
356 // get_attribute_path_virtual_fd
357 static status_t
358 get_attribute_path_virtual_fd(int fd, const char *attribute, string &attrPath,
359 string &typePath)
361 // stat the file to get a NodeRef
362 struct stat st;
363 status_t error = _kern_read_stat(fd, NULL, false, &st, sizeof(st));
364 if (error != B_OK)
365 return error;
366 NodeRef ref(st);
368 // Try to get a path. If we can't get a path, this is must be a "real"
369 // (i.e. system) file descriptor, which is just as well.
370 string path;
371 bool pathValid = (get_path(fd, NULL, path) == B_OK);
373 // get the attribute path
374 return get_attribute_path(ref, (pathValid ? path.c_str() : NULL),
375 (pathValid ? -1 : fd), attribute, attrPath, typePath);
379 // get_attribute_path
380 static status_t
381 get_attribute_path(int fd, const char *attribute, string &attrPath,
382 string &typePath)
384 if (get_descriptor(fd)) {
385 // This is a virtual file descriptor -- we have a special function
386 // for handling it.
387 return get_attribute_path_virtual_fd(fd, attribute, attrPath,
388 typePath);
389 } else {
390 // This is a real (i.e. system) file descriptor -- fstat() it and
391 // build the path.
393 // stat the file to get a NodeRef
394 struct stat st;
395 if (fstat(fd, &st) < 0)
396 return errno;
397 NodeRef ref(st);
399 return get_attribute_path(ref, NULL, fd, attribute, attrPath, typePath);
404 // # pragma mark - Public API
407 // fs_open_attr_dir
408 DIR *
409 fs_open_attr_dir(const char *path)
411 struct stat st;
412 if (lstat(path, &st))
413 return NULL;
415 return open_attr_dir(NodeRef(st), path, -1);
418 // fs_fopen_attr_dir
419 DIR *
420 fs_fopen_attr_dir(int fd)
422 struct stat st;
424 status_t error = _kern_read_stat(fd, NULL, false, &st,
425 sizeof(struct stat));
426 if (error != B_OK) {
427 errno = error;
428 return NULL;
431 // Try to get a path. If we can't get a path, this is must be a "real"
432 // (i.e. system) file descriptor, which is just as well.
433 string path;
434 bool pathValid = (get_path(fd, NULL, path) == B_OK);
436 // get the attribute path
437 return open_attr_dir(NodeRef(st), (pathValid ? path.c_str() : NULL),
438 (pathValid ? -1 : fd));
441 // fs_close_attr_dir
443 fs_close_attr_dir(DIR *dir)
445 return closedir(dir);
448 // fs_read_attr_dir
449 struct dirent *
450 fs_read_attr_dir(DIR *dir)
452 struct dirent *entry = NULL;
453 while (true) {
454 // read the next entry
455 entry = readdir(dir);
456 if (!entry)
457 return NULL;
459 // ignore administrative entries; the
460 if (entry->d_name[0] == '_') {
461 string attrName = deescape_attr_name(entry->d_name);
462 strcpy(entry->d_name, attrName.c_str());
463 return entry;
468 // fs_rewind_attr_dir
469 void
470 fs_rewind_attr_dir(DIR *dir)
472 rewinddir(dir);
475 // fs_fopen_attr
477 fs_fopen_attr(int fd, const char *attribute, uint32 type, int openMode)
479 if (!attribute) {
480 errno = B_BAD_VALUE;
481 return -1;
484 // get the attribute path
485 string attrPath;
486 string typePath;
487 status_t error = get_attribute_path(fd, attribute, attrPath, typePath);
488 if (error != B_OK) {
489 errno = error;
490 return -1;
493 // check, if the attribute already exists
494 struct stat st;
495 bool exists = (lstat(attrPath.c_str(), &st) == 0);
497 // open the attribute
498 int attrFD = open(attrPath.c_str(), openMode, S_IRWXU | S_IRWXG | S_IRWXO);
499 if (attrFD < 0)
500 return -1;
502 // set the type, if the attribute didn't exist yet
503 if (!exists) {
504 // create a file prefixed "t"
505 int typeFD = creat(typePath.c_str(), S_IRWXU | S_IRWXG | S_IRWXO);
506 if (typeFD >= 0) {
507 // write the type into the file
508 if (write(typeFD, &type, sizeof(type)) < 0)
509 error = errno;
511 close(typeFD);
513 } else
514 error = errno;
516 // remove type and attribute file, if something went wrong
517 if (error != B_OK) {
518 if (typeFD > 0) {
519 unlink(typePath.c_str());
522 close(attrFD);
523 unlink(attrPath.c_str());
525 errno = error;
526 return -1;
530 return attrFD;
533 // fs_close_attr
535 fs_close_attr(int fd)
537 return close(fd);
540 // fs_read_attr
541 ssize_t
542 fs_read_attr(int fd, const char *attribute, uint32 type, off_t pos,
543 void *buffer, size_t readBytes)
545 // open the attribute
546 int attrFD = fs_fopen_attr(fd, attribute, type, O_RDONLY);
547 if (attrFD < 0)
548 return attrFD;
550 // read
551 ssize_t bytesRead = read_pos(attrFD, pos, buffer, readBytes);
552 status_t error = errno;
554 // close the attribute
555 fs_close_attr(attrFD);
557 if (bytesRead < 0) {
558 errno = error;
559 return -1;
562 return bytesRead;
565 // fs_write_attr
566 ssize_t
567 fs_write_attr(int fd, const char *attribute, uint32 type, off_t pos,
568 const void *buffer, size_t readBytes)
570 // open the attribute
571 int attrFD = fs_fopen_attr(fd, attribute, type,
572 O_WRONLY | O_CREAT | O_TRUNC);
573 if (attrFD < 0) {
574 // Setting user attributes on symlinks is not allowed (xattr). So, if
575 // this is a symlink and we're only supposed to write a "BEOS:TYPE"
576 // attribute we silently pretend to have succeeded.
577 struct stat st;
578 if (strcmp(attribute, "BEOS:TYPE") == 0 && fstat(fd, &st) == 0
579 && S_ISLNK(st.st_mode)) {
580 return readBytes;
582 return attrFD;
585 // read
586 ssize_t bytesWritten = write_pos(attrFD, pos, buffer, readBytes);
587 status_t error = errno;
589 // close the attribute
590 fs_close_attr(attrFD);
592 if (bytesWritten < 0) {
593 errno = error;
594 return -1;
597 return bytesWritten;
600 // fs_remove_attr
602 fs_remove_attr(int fd, const char *attribute)
604 if (!attribute) {
605 errno = B_BAD_VALUE;
606 return -1;
609 // get the attribute path
610 string attrPath;
611 string typePath;
612 status_t error = get_attribute_path(fd, attribute, attrPath, typePath);
613 if (error != B_OK) {
614 errno = error;
615 return -1;
618 // remove the attribute
619 if (unlink(attrPath.c_str()) < 0)
620 return -1;
622 unlink(typePath.c_str());
624 return B_OK;
627 // fs_stat_attr
629 fs_stat_attr(int fd, const char *attribute, struct attr_info *attrInfo)
631 if (!attribute || !attrInfo) {
632 errno = B_BAD_VALUE;
633 return -1;
636 // get the attribute path
637 string attrPath;
638 string typePath;
639 status_t error = get_attribute_path(fd, attribute, attrPath, typePath);
640 if (error != B_OK) {
641 errno = error;
642 return -1;
645 // stat the attribute file to get the size of the attribute
646 struct stat st;
647 if (lstat(attrPath.c_str(), &st) < 0)
648 return -1;
650 attrInfo->size = st.st_size;
652 // now open the attribute type file and read the attribute's type
653 int typeFD = open(typePath.c_str(), O_RDONLY);
654 if (typeFD < 0)
655 return -1;
657 ssize_t bytesRead = read(typeFD, &attrInfo->type, sizeof(attrInfo->type));
658 if (bytesRead < 0)
659 error = errno;
660 else if (bytesRead < (ssize_t)sizeof(attrInfo->type))
661 error = B_FILE_ERROR;
663 close(typeFD);
665 // fail on error
666 if (error != B_OK) {
667 errno = error;
668 return -1;
671 return 0;
675 // #pragma mark - Private Syscalls
678 // _kern_open_attr_dir
680 _kern_open_attr_dir(int fd, const char *path)
682 // get node ref for the node
683 struct stat st;
684 status_t error = _kern_read_stat(fd, path, false, &st,
685 sizeof(struct stat));
686 if (error != B_OK) {
687 errno = error;
688 return -1;
690 NodeRef ref(st);
692 // If a path was given, get a usable path.
693 string realPath;
694 if (path) {
695 error = get_path(fd, path, realPath);
696 if (error != B_OK)
697 return error;
700 // open the attr dir
701 DIR *dir = open_attr_dir(ref, (path ? realPath.c_str() : NULL),
702 (path ? -1 : fd));
703 if (!dir)
704 return errno;
706 // create descriptor
707 AttrDirDescriptor *descriptor = new AttrDirDescriptor(dir, ref);
708 return add_descriptor(descriptor);
711 // _kern_rename_attr
712 status_t
713 _kern_rename_attr(int fromFile, const char *fromName, int toFile,
714 const char *toName)
716 if (!fromName || !toName)
717 return B_BAD_VALUE;
719 // get the attribute paths
720 string fromAttrPath;
721 string fromTypePath;
722 status_t error = get_attribute_path_virtual_fd(fromFile, fromName,
723 fromAttrPath, fromTypePath);
724 if (error != B_OK)
725 return error;
727 string toAttrPath;
728 string toTypePath;
729 error = get_attribute_path_virtual_fd(toFile, toName, toAttrPath,
730 toTypePath);
731 if (error != B_OK)
732 return error;
734 // rename the attribute and type files
735 if (rename(fromAttrPath.c_str(), toAttrPath.c_str()) < 0)
736 return errno;
738 if (rename(fromTypePath.c_str(), toTypePath.c_str()) < 0) {
739 // renaming the type file failed: try to rename back the attribute file
740 error = errno;
742 rename(toAttrPath.c_str(), fromAttrPath.c_str());
744 return error;
747 return B_OK;
750 // _kern_remove_attr
751 status_t
752 _kern_remove_attr(int fd, const char *name)
754 if (!name)
755 return B_BAD_VALUE;
757 // get the attribute path
758 string attrPath;
759 string typePath;
760 status_t error = get_attribute_path_virtual_fd(fd, name, attrPath,
761 typePath);
762 if (error != B_OK)
763 return error;
765 // remove the attribute
766 if (unlink(attrPath.c_str()) < 0)
767 return errno;
769 unlink(typePath.c_str());
771 return B_OK;
775 // __get_attribute_dir_path
776 extern "C" bool __get_attribute_dir_path(const struct stat* st,
777 const char* path, char* buffer);
778 bool
779 __get_attribute_dir_path(const struct stat* st, const char* path, char* buffer)
781 NodeRef ref(*st);
782 string dirPath = get_attribute_dir_path(ref, path, -1);
783 strcpy(buffer, dirPath.c_str());
784 return true;