2 #include <BeOSBuildCompatibility.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"
31 # error No attribute support for this host platform!
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";
46 // init_attribute_dir_base_dir
48 init_attribute_dir_base_dir()
50 static bool initialized
= false;
51 static status_t initError
;
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
;
68 // doesn't exist yet: create it
69 if (mkdir(sAttributeDirBasePath
, S_IRWXU
| S_IRWXG
| S_IRWXO
) < 0)
79 escape_attr_name(const char *name
)
81 string
escapedName("_");
82 while (*name
!= '\0') {
83 // we replace '/' with "_s" and '_' with "__"
86 else if (*name
== '_')
99 deescape_attr_name(const char *name
)
101 if (name
[0] != '_') {
102 debugger("deescape_attr_name(): name doesn't start with '_'!\n");
107 string deescapedName
;
108 while (*name
!= '\0') {
112 deescapedName
+= '/';
113 } else if (*name
== '_') {
114 deescapedName
+= '_';
116 debugger("deescape_attr_name(): name contains invalid escaped "
121 deescapedName
+= *name
;
126 return deescapedName
;
130 #if HAIKU_HOST_USE_XATTR_REF
134 make_unique_node_id(string
& _id
)
136 // open random device
137 int fd
= open("/dev/urandom", O_RDONLY
);
139 fd
= open("/dev/random", O_RDONLY
);
141 return B_NOT_SUPPORTED
;
146 ssize_t bytesRead
= read(fd
, buffer
, sizeof(buffer
));
147 status_t error
= B_OK
;
155 if (bytesRead
!= (ssize_t
)sizeof(buffer
))
158 // convert to hex string
159 static const char* const kHexChars
= "0123456789abcdef";
161 for (size_t i
= 0; i
< sizeof(buffer
); i
++) {
162 _id
+= kHexChars
[buffer
[i
] >> 4];
163 _id
+= kHexChars
[buffer
[i
] & 0xf];
171 get_id_attribute(const char *path
, int fd
, string
& _id
)
173 // list_attributes() and remove_attribute() are unused here -- prevent the
175 (void)list_attributes
;
176 (void)remove_attribute
;
178 string
attributeName(kAttributeNamespace
);
179 attributeName
+= kIDAttributeName
;
182 ssize_t bytesRead
= get_attribute(fd
, path
, attributeName
.c_str(), buffer
,
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
;
190 if (path
== NULL
|| lstat(path
, &st
) < 0 || !S_ISLNK(st
.st_mode
))
194 snprintf(buffer
, sizeof(buffer
), "symlink-%" B_PRIdINO
, st
.st_ino
);
199 _id
= string(buffer
, bytesRead
);
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)
217 get_attribute_dir_path(NodeRef ref
, const char *path
, int fd
)
220 status_t error
= get_id_attribute(path
, fd
, id
);
222 id
= "_no_attributes_";
224 string
attrDirPath(sAttributeDirBasePath
);
232 get_attribute_dir_path_needed(NodeRef ref
, const char *path
, int fd
,
233 string
& _attrDirPath
)
236 status_t error
= get_id_attribute(path
, fd
, id
);
238 error
= make_unique_node_id(id
);
242 error
= set_id_attribute(path
, fd
, id
.c_str());
247 _attrDirPath
= sAttributeDirBasePath
;
258 get_attribute_dir_path(NodeRef ref
, const char *path
, int fd
)
260 string
attrDirPath(sAttributeDirBasePath
);
262 sprintf(buffer
, "/%" B_PRIdINO
, ref
.node
);
263 attrDirPath
+= buffer
;
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
);
280 // ensure_attribute_dir_exists
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();
290 error
= get_attribute_dir_path_needed(ref
, path
, fd
, attrDirPath
);
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
);
308 // doesn't exist yet: create it
309 if (mkdir(attrDirPath
.c_str(), S_IRWXU
| S_IRWXG
| S_IRWXO
) < 0)
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
);
327 string
dirPath(get_attribute_dir_path(ref
, path
, fd
));
328 return opendir(dirPath
.c_str());
331 // get_attribute_path
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)
339 // make sure the attribute dir for the node exits
340 status_t error
= ensure_attribute_dir_exists(ref
, path
, fd
);
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
;
356 // get_attribute_path_virtual_fd
358 get_attribute_path_virtual_fd(int fd
, const char *attribute
, string
&attrPath
,
361 // stat the file to get a NodeRef
363 status_t error
= _kern_read_stat(fd
, NULL
, false, &st
, sizeof(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.
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
381 get_attribute_path(int fd
, const char *attribute
, string
&attrPath
,
384 if (get_descriptor(fd
)) {
385 // This is a virtual file descriptor -- we have a special function
387 return get_attribute_path_virtual_fd(fd
, attribute
, attrPath
,
390 // This is a real (i.e. system) file descriptor -- fstat() it and
393 // stat the file to get a NodeRef
395 if (fstat(fd
, &st
) < 0)
399 return get_attribute_path(ref
, NULL
, fd
, attribute
, attrPath
, typePath
);
404 // # pragma mark - Public API
409 fs_open_attr_dir(const char *path
)
412 if (lstat(path
, &st
))
415 return open_attr_dir(NodeRef(st
), path
, -1);
420 fs_fopen_attr_dir(int fd
)
424 status_t error
= _kern_read_stat(fd
, NULL
, false, &st
,
425 sizeof(struct stat
));
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.
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
));
443 fs_close_attr_dir(DIR *dir
)
445 return closedir(dir
);
450 fs_read_attr_dir(DIR *dir
)
452 struct dirent
*entry
= NULL
;
454 // read the next entry
455 entry
= readdir(dir
);
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());
468 // fs_rewind_attr_dir
470 fs_rewind_attr_dir(DIR *dir
)
477 fs_fopen_attr(int fd
, const char *attribute
, uint32 type
, int openMode
)
484 // get the attribute path
487 status_t error
= get_attribute_path(fd
, attribute
, attrPath
, typePath
);
493 // check, if the attribute already exists
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
);
502 // set the type, if the attribute didn't exist yet
504 // create a file prefixed "t"
505 int typeFD
= creat(typePath
.c_str(), S_IRWXU
| S_IRWXG
| S_IRWXO
);
507 // write the type into the file
508 if (write(typeFD
, &type
, sizeof(type
)) < 0)
516 // remove type and attribute file, if something went wrong
519 unlink(typePath
.c_str());
523 unlink(attrPath
.c_str());
535 fs_close_attr(int fd
)
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
);
551 ssize_t bytesRead
= read_pos(attrFD
, pos
, buffer
, readBytes
);
552 status_t error
= errno
;
554 // close the attribute
555 fs_close_attr(attrFD
);
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
);
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.
578 if (strcmp(attribute
, "BEOS:TYPE") == 0 && fstat(fd
, &st
) == 0
579 && S_ISLNK(st
.st_mode
)) {
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) {
602 fs_remove_attr(int fd
, const char *attribute
)
609 // get the attribute path
612 status_t error
= get_attribute_path(fd
, attribute
, attrPath
, typePath
);
618 // remove the attribute
619 if (unlink(attrPath
.c_str()) < 0)
622 unlink(typePath
.c_str());
629 fs_stat_attr(int fd
, const char *attribute
, struct attr_info
*attrInfo
)
631 if (!attribute
|| !attrInfo
) {
636 // get the attribute path
639 status_t error
= get_attribute_path(fd
, attribute
, attrPath
, typePath
);
645 // stat the attribute file to get the size of the attribute
647 if (lstat(attrPath
.c_str(), &st
) < 0)
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
);
657 ssize_t bytesRead
= read(typeFD
, &attrInfo
->type
, sizeof(attrInfo
->type
));
660 else if (bytesRead
< (ssize_t
)sizeof(attrInfo
->type
))
661 error
= B_FILE_ERROR
;
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
684 status_t error
= _kern_read_stat(fd
, path
, false, &st
,
685 sizeof(struct stat
));
692 // If a path was given, get a usable path.
695 error
= get_path(fd
, path
, realPath
);
701 DIR *dir
= open_attr_dir(ref
, (path
? realPath
.c_str() : NULL
),
707 AttrDirDescriptor
*descriptor
= new AttrDirDescriptor(dir
, ref
);
708 return add_descriptor(descriptor
);
713 _kern_rename_attr(int fromFile
, const char *fromName
, int toFile
,
716 if (!fromName
|| !toName
)
719 // get the attribute paths
722 status_t error
= get_attribute_path_virtual_fd(fromFile
, fromName
,
723 fromAttrPath
, fromTypePath
);
729 error
= get_attribute_path_virtual_fd(toFile
, toName
, toAttrPath
,
734 // rename the attribute and type files
735 if (rename(fromAttrPath
.c_str(), toAttrPath
.c_str()) < 0)
738 if (rename(fromTypePath
.c_str(), toTypePath
.c_str()) < 0) {
739 // renaming the type file failed: try to rename back the attribute file
742 rename(toAttrPath
.c_str(), fromAttrPath
.c_str());
752 _kern_remove_attr(int fd
, const char *name
)
757 // get the attribute path
760 status_t error
= get_attribute_path_virtual_fd(fd
, name
, attrPath
,
765 // remove the attribute
766 if (unlink(attrPath
.c_str()) < 0)
769 unlink(typePath
.c_str());
775 // __get_attribute_dir_path
776 extern "C" bool __get_attribute_dir_path(const struct stat
* st
,
777 const char* path
, char* buffer
);
779 __get_attribute_dir_path(const struct stat
* st
, const char* path
, char* buffer
)
782 string dirPath
= get_attribute_dir_path(ref
, path
, -1);
783 strcpy(buffer
, dirPath
.c_str());