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