xref: /haiku/src/kits/storage/Node.cpp (revision 9642f7705b27e5c270c15fa526d14e1848c2c27d)
1 /*
2  * Copyright 2002-2011 Haiku, Inc. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Tyler Dauwalder
7  *		Ingo Weinhold, bonefish@users.sf.net
8  */
9 
10 
11 #include <Node.h>
12 
13 #include <errno.h>
14 #include <fcntl.h>
15 #include <new>
16 #include <string.h>
17 #include <unistd.h>
18 
19 #include <compat/sys/stat.h>
20 
21 #include <Directory.h>
22 #include <Entry.h>
23 #include <fs_attr.h>
24 #include <String.h>
25 #include <TypeConstants.h>
26 
27 #include <syscalls.h>
28 
29 #include "storage_support.h"
30 
31 
32 //	#pragma mark - node_ref
33 
34 
35 node_ref::node_ref()
36 	:
37 	device((dev_t)-1),
38 	node((ino_t)-1)
39 {
40 }
41 
42 
43 node_ref::node_ref(dev_t device, ino_t node)
44 	:
45 	device(device),
46 	node(node)
47 {
48 }
49 
50 
51 node_ref::node_ref(const node_ref& other)
52 	:
53 	device((dev_t)-1),
54 	node((ino_t)-1)
55 {
56 	*this = other;
57 }
58 
59 
60 bool
61 node_ref::operator==(const node_ref& other) const
62 {
63 	return (device == other.device && node == other.node);
64 }
65 
66 
67 bool
68 node_ref::operator!=(const node_ref& other) const
69 {
70 	return !(*this == other);
71 }
72 
73 
74 bool
75 node_ref::operator<(const node_ref& other) const
76 {
77 	if (this->device != other.device)
78 		return this->device < other.device;
79 
80 	return this->node < other.node;
81 }
82 
83 
84 node_ref&
85 node_ref::operator=(const node_ref& other)
86 {
87 	device = other.device;
88 	node = other.node;
89 	return *this;
90 }
91 
92 
93 //	#pragma mark - BNode
94 
95 
96 BNode::BNode()
97 	:
98 	fFd(-1),
99 	fAttrFd(-1),
100 	fCStatus(B_NO_INIT)
101 {
102 }
103 
104 
105 BNode::BNode(const entry_ref* ref)
106 	:
107 	fFd(-1),
108 	fAttrFd(-1),
109 	fCStatus(B_NO_INIT)
110 {
111 	// fCStatus is set by SetTo(), ignore return value
112 	(void)SetTo(ref);
113 }
114 
115 
116 BNode::BNode(const BEntry* entry)
117 	:
118 	fFd(-1),
119 	fAttrFd(-1),
120 	fCStatus(B_NO_INIT)
121 {
122 	// fCStatus is set by SetTo(), ignore return value
123 	(void)SetTo(entry);
124 }
125 
126 
127 BNode::BNode(const char* path)
128 	:
129 	fFd(-1),
130 	fAttrFd(-1),
131 	fCStatus(B_NO_INIT)
132 {
133 	// fCStatus is set by SetTo(), ignore return value
134 	(void)SetTo(path);
135 }
136 
137 
138 BNode::BNode(const BDirectory* dir, const char* path)
139 	:
140 	fFd(-1),
141 	fAttrFd(-1),
142 	fCStatus(B_NO_INIT)
143 {
144 	// fCStatus is set by SetTo(), ignore return value
145 	(void)SetTo(dir, path);
146 }
147 
148 
149 BNode::BNode(const BNode& node)
150 	:
151 	fFd(-1),
152 	fAttrFd(-1),
153 	fCStatus(B_NO_INIT)
154 {
155 	*this = node;
156 }
157 
158 
159 BNode::~BNode()
160 {
161 	Unset();
162 }
163 
164 
165 status_t
166 BNode::InitCheck() const
167 {
168 	return fCStatus;
169 }
170 
171 
172 status_t
173 BNode::SetTo(const entry_ref* ref)
174 {
175 	return _SetTo(ref, false);
176 }
177 
178 
179 status_t
180 BNode::SetTo(const BEntry* entry)
181 {
182 	if (entry == NULL) {
183 		Unset();
184 		return (fCStatus = B_BAD_VALUE);
185 	}
186 
187 	return _SetTo(entry->fDirFd, entry->fName, false);
188 }
189 
190 
191 status_t
192 BNode::SetTo(const char* path)
193 {
194 	return _SetTo(-1, path, false);
195 }
196 
197 
198 status_t
199 BNode::SetTo(const BDirectory* dir, const char* path)
200 {
201 	if (dir == NULL || path == NULL
202 		|| BPrivate::Storage::is_absolute_path(path)) {
203 		Unset();
204 		return (fCStatus = B_BAD_VALUE);
205 	}
206 
207 	return _SetTo(dir->fDirFd, path, false);
208 }
209 
210 
211 void
212 BNode::Unset()
213 {
214 	close_fd();
215 	fCStatus = B_NO_INIT;
216 }
217 
218 
219 status_t
220 BNode::Lock()
221 {
222 	if (fCStatus != B_OK)
223 		return fCStatus;
224 
225 	return _kern_lock_node(fFd);
226 }
227 
228 
229 status_t
230 BNode::Unlock()
231 {
232 	if (fCStatus != B_OK)
233 		return fCStatus;
234 
235 	return _kern_unlock_node(fFd);
236 }
237 
238 
239 status_t
240 BNode::Sync()
241 {
242 	return (fCStatus != B_OK) ? B_FILE_ERROR : _kern_fsync(fFd);
243 }
244 
245 
246 ssize_t
247 BNode::WriteAttr(const char* attr, type_code type, off_t offset,
248 	const void* buffer, size_t length)
249 {
250 	if (fCStatus != B_OK)
251 		return B_FILE_ERROR;
252 
253 	if (attr == NULL || buffer == NULL)
254 		return B_BAD_VALUE;
255 
256 	ssize_t result = fs_write_attr(fFd, attr, type, offset, buffer, length);
257 
258 	return result < 0 ? errno : result;
259 }
260 
261 
262 ssize_t
263 BNode::ReadAttr(const char* attr, type_code type, off_t offset,
264 	void* buffer, size_t length) const
265 {
266 	if (fCStatus != B_OK)
267 		return B_FILE_ERROR;
268 
269 	if (attr == NULL || buffer == NULL)
270 		return B_BAD_VALUE;
271 
272 	ssize_t result = fs_read_attr(fFd, attr, type, offset, buffer, length);
273 
274 	return result == -1 ? errno : result;
275 }
276 
277 
278 status_t
279 BNode::RemoveAttr(const char* name)
280 {
281 	return fCStatus != B_OK ? B_FILE_ERROR : _kern_remove_attr(fFd, name);
282 }
283 
284 
285 status_t
286 BNode::RenameAttr(const char* oldName, const char* newName)
287 {
288 	if (fCStatus != B_OK)
289 		return B_FILE_ERROR;
290 
291 	return _kern_rename_attr(fFd, oldName, fFd, newName);
292 }
293 
294 
295 status_t
296 BNode::GetAttrInfo(const char* name, struct attr_info* info) const
297 {
298 	if (fCStatus != B_OK)
299 		return B_FILE_ERROR;
300 
301 	if (name == NULL || info == NULL)
302 		return B_BAD_VALUE;
303 
304 	return fs_stat_attr(fFd, name, info) < 0 ? errno : B_OK ;
305 }
306 
307 
308 status_t
309 BNode::GetNextAttrName(char* buffer)
310 {
311 	// We're allowed to assume buffer is at least
312 	// B_ATTR_NAME_LENGTH chars long, but NULLs
313 	// are not acceptable.
314 
315 	// BeOS R5 crashed when passed NULL
316 	if (buffer == NULL)
317 		return B_BAD_VALUE;
318 
319 	if (InitAttrDir() != B_OK)
320 		return B_FILE_ERROR;
321 
322 	BPrivate::Storage::LongDirEntry entry;
323 	ssize_t result = _kern_read_dir(fAttrFd, &entry, sizeof(entry), 1);
324 	if (result < 0)
325 		return result;
326 
327 	if (result == 0)
328 		return B_ENTRY_NOT_FOUND;
329 
330 	strlcpy(buffer, entry.d_name, B_ATTR_NAME_LENGTH);
331 
332 	return B_OK;
333 }
334 
335 
336 status_t
337 BNode::RewindAttrs()
338 {
339 	if (InitAttrDir() != B_OK)
340 		return B_FILE_ERROR;
341 
342 	return _kern_rewind_dir(fAttrFd);
343 }
344 
345 
346 status_t
347 BNode::WriteAttrString(const char* name, const BString* data)
348 {
349 	status_t error = (!name || !data)  ? B_BAD_VALUE : B_OK;
350 	if (error == B_OK) {
351 		int32 length = data->Length() + 1;
352 		ssize_t sizeWritten = WriteAttr(name, B_STRING_TYPE, 0, data->String(),
353 			length);
354 		if (sizeWritten != length)
355 			error = sizeWritten;
356 	}
357 
358 	return error;
359 }
360 
361 
362 status_t
363 BNode::ReadAttrString(const char* name, BString* result) const
364 {
365 	if (name == NULL || result == NULL)
366 		return B_BAD_VALUE;
367 
368 	attr_info info;
369 	status_t error;
370 
371 	error = GetAttrInfo(name, &info);
372 	if (error != B_OK)
373 		return error;
374 
375 	// Lock the string's buffer so we can meddle with it
376 	char* data = result->LockBuffer(info.size + 1);
377 	if (data == NULL)
378 		return B_NO_MEMORY;
379 
380 	// Read the attribute
381 	ssize_t bytes = ReadAttr(name, B_STRING_TYPE, 0, data, info.size);
382 	// Check for failure
383 	if (bytes < 0) {
384 		error = bytes;
385 		bytes = 0;
386 			// In this instance, we simply clear the string
387 	} else
388 		error = B_OK;
389 
390 	// Null terminate the new string just to be sure (since it *is*
391 	// possible to read and write non-NULL-terminated strings)
392 	data[bytes] = 0;
393 	result->UnlockBuffer();
394 
395 	return error;
396 }
397 
398 
399 BNode&
400 BNode::operator=(const BNode& node)
401 {
402 	// No need to do any assignment if already equal
403 	if (*this == node)
404 		return *this;
405 
406 	// Close down out current state
407 	Unset();
408 	// We have to manually dup the node, because R5::BNode::Dup()
409 	// is not declared to be const (which IMO is retarded).
410 	fFd = _kern_dup(node.fFd);
411 	fCStatus = (fFd < 0) ? B_NO_INIT : B_OK ;
412 
413 	return *this;
414 }
415 
416 
417 bool
418 BNode::operator==(const BNode& node) const
419 {
420 	if (fCStatus == B_NO_INIT && node.InitCheck() == B_NO_INIT)
421 		return true;
422 
423 	if (fCStatus == B_OK && node.InitCheck() == B_OK) {
424 		// compare the node_refs
425 		node_ref ref1, ref2;
426 		if (GetNodeRef(&ref1) != B_OK)
427 			return false;
428 
429 		if (node.GetNodeRef(&ref2) != B_OK)
430 			return false;
431 
432 		return (ref1 == ref2);
433 	}
434 
435 	return false;
436 }
437 
438 
439 bool
440 BNode::operator!=(const BNode& node) const
441 {
442 	return !(*this == node);
443 }
444 
445 
446 int
447 BNode::Dup()
448 {
449 	int fd = _kern_dup(fFd);
450 
451 	return (fd >= 0 ? fd : -1);
452 		// comply with R5 return value
453 }
454 
455 
456 /*! (currently unused) */
457 void BNode::_RudeNode1() { }
458 void BNode::_RudeNode2() { }
459 void BNode::_RudeNode3() { }
460 void BNode::_RudeNode4() { }
461 void BNode::_RudeNode5() { }
462 void BNode::_RudeNode6() { }
463 
464 
465 /*!	Sets the node's file descriptor.
466 
467 	Used by each implementation (i.e. BNode, BFile, BDirectory, etc.) to set
468 	the node's file descriptor. This allows each subclass to use the various
469 	file-type specific system calls for opening file descriptors.
470 
471 	\note This method calls close_fd() to close previously opened FDs. Thus
472 		derived classes should take care to first call set_fd() and set
473 		class specific resources freed in their close_fd() version
474 		thereafter.
475 
476 	\param fd the file descriptor this BNode should be set to (may be -1).
477 
478 	\returns \c B_OK if everything went fine, or an error code if something
479 		went wrong.
480 */
481 status_t
482 BNode::set_fd(int fd)
483 {
484 	if (fFd != -1)
485 		close_fd();
486 
487 	fFd = fd;
488 
489 	return B_OK;
490 }
491 
492 
493 /*!	Closes the node's file descriptor(s).
494 
495 	To be implemented by subclasses to close the file descriptor using the
496 	proper system call for the given file-type. This implementation calls
497 	_kern_close(fFd) and also _kern_close(fAttrDir) if necessary.
498 */
499 void
500 BNode::close_fd()
501 {
502 	if (fAttrFd >= 0) {
503 		_kern_close(fAttrFd);
504 		fAttrFd = -1;
505 	}
506 	if (fFd >= 0) {
507 		_kern_close(fFd);
508 		fFd = -1;
509 	}
510 }
511 
512 
513 /*!	Sets the BNode's status.
514 
515 	To be used by derived classes instead of accessing the BNode's private
516 	\c fCStatus member directly.
517 
518 	\param newStatus the new value for the status variable.
519 */
520 void
521 BNode::set_status(status_t newStatus)
522 {
523 	fCStatus = newStatus;
524 }
525 
526 
527 /*!	Initializes the BNode's file descriptor to the node referred to
528 	by the given FD and path combo.
529 
530 	\a path must either be \c NULL, an absolute or a relative path.
531 	In the first case, \a fd must not be \c NULL; the node it refers to will
532 	be opened. If absolute, \a fd is ignored. If relative and \a fd is >= 0,
533 	it will be reckoned off the directory identified by \a fd, otherwise off
534 	the current working directory.
535 
536 	The method will first try to open the node with read and write permission.
537 	If that fails due to a read-only FS or because the user has no write
538 	permission for the node, it will re-try opening the node read-only.
539 
540 	The \a fCStatus member will be set to the return value of this method.
541 
542 	\param fd Either a directory FD or a value < 0. In the latter case \a path
543 	       must be specified.
544 	\param path Either \a NULL in which case \a fd must be given, absolute, or
545 	       relative to the directory specified by \a fd (if given) or to the
546 	       current working directory.
547 	\param traverse If the node identified by \a fd and \a path is a symlink
548 	       and \a traverse is \c true, the symlink will be resolved recursively.
549 
550 	\returns \c B_OK if everything went fine, or an error code otherwise.
551 */
552 status_t
553 BNode::_SetTo(int fd, const char* path, bool traverse)
554 {
555 	Unset();
556 
557 	status_t error = (fd >= 0 || path ? B_OK : B_BAD_VALUE);
558 	if (error == B_OK) {
559 		int traverseFlag = (traverse ? 0 : O_NOTRAVERSE);
560 		fFd = _kern_open(fd, path, O_RDWR | O_CLOEXEC | traverseFlag, 0);
561 		if (fFd < B_OK && fFd != B_ENTRY_NOT_FOUND) {
562 			// opening read-write failed, re-try read-only
563 			fFd = _kern_open(fd, path, O_RDONLY | O_CLOEXEC | traverseFlag, 0);
564 		}
565 		if (fFd < 0)
566 			error = fFd;
567 	}
568 
569 	return fCStatus = error;
570 }
571 
572 
573 /*!	Initializes the BNode's file descriptor to the node referred to
574 	by the given entry_ref.
575 
576 	The method will first try to open the node with read and write permission.
577 	If that fails due to a read-only FS or because the user has no write
578 	permission for the node, it will re-try opening the node read-only.
579 
580 	The \a fCStatus member will be set to the return value of this method.
581 
582 	\param ref An entry_ref identifying the node to be opened.
583 	\param traverse If the node identified by \a ref is a symlink and
584 	       \a traverse is \c true, the symlink will be resolved recursively.
585 
586 	\returns \c B_OK if everything went fine, or an error code otherwise.
587 */
588 status_t
589 BNode::_SetTo(const entry_ref* ref, bool traverse)
590 {
591 	Unset();
592 
593 	status_t result = (ref ? B_OK : B_BAD_VALUE);
594 	if (result == B_OK) {
595 		int traverseFlag = (traverse ? 0 : O_NOTRAVERSE);
596 		fFd = _kern_open_entry_ref(ref->device, ref->directory, ref->name,
597 			O_RDWR | O_CLOEXEC | traverseFlag, 0);
598 		if (fFd < B_OK && fFd != B_ENTRY_NOT_FOUND) {
599 			// opening read-write failed, re-try read-only
600 			fFd = _kern_open_entry_ref(ref->device, ref->directory, ref->name,
601 				O_RDONLY | O_CLOEXEC | traverseFlag, 0);
602 		}
603 		if (fFd < 0)
604 			result = fFd;
605 	}
606 
607 	return fCStatus = result;
608 }
609 
610 
611 /*!	Modifies a certain setting for this node based on \a what and the
612 	corresponding value in \a st.
613 
614 	Inherited from and called by BStatable.
615 
616 	\param st a stat structure containing the value to be set.
617 	\param what specifies what setting to be modified.
618 
619 	\returns \c B_OK if everything went fine, or an error code otherwise.
620 */
621 status_t
622 BNode::set_stat(struct stat& stat, uint32 what)
623 {
624 	if (fCStatus != B_OK)
625 		return B_FILE_ERROR;
626 
627 	return _kern_write_stat(fFd, NULL, false, &stat, sizeof(struct stat),
628 		what);
629 }
630 
631 
632 
633 /*!	Verifies that the BNode has been properly initialized, and then
634 	(if necessary) opens the attribute directory on the node's file
635 	descriptor, storing it in fAttrDir.
636 
637 	\returns \c B_OK if everything went fine, or an error code otherwise.
638 */
639 status_t
640 BNode::InitAttrDir()
641 {
642 	if (fCStatus == B_OK && fAttrFd < 0) {
643 		fAttrFd = _kern_open_attr_dir(fFd, NULL, false);
644 		if (fAttrFd < 0)
645 			return fAttrFd;
646 
647 		// set close on exec flag
648 		fcntl(fAttrFd, F_SETFD, FD_CLOEXEC);
649 	}
650 
651 	return fCStatus;
652 }
653 
654 
655 status_t
656 BNode::_GetStat(struct stat* stat) const
657 {
658 	return fCStatus != B_OK
659 		? fCStatus
660 		: _kern_read_stat(fFd, NULL, false, stat, sizeof(struct stat));
661 }
662 
663 
664 status_t
665 BNode::_GetStat(struct stat_beos* stat) const
666 {
667 	struct stat newStat;
668 	status_t error = _GetStat(&newStat);
669 	if (error != B_OK)
670 		return error;
671 
672 	convert_to_stat_beos(&newStat, stat);
673 
674 	return B_OK;
675 }
676 
677 
678 //	#pragma mark - symbol versions
679 
680 
681 #ifdef HAIKU_TARGET_PLATFORM_LIBBE_TEST
682 #	if __GNUC__ == 2	// gcc 2
683 
684 	B_DEFINE_SYMBOL_VERSION("_GetStat__C5BNodeP4stat",
685 		"GetStat__C5BNodeP4stat@@LIBBE_TEST");
686 
687 #	else	// gcc 4
688 
689 	B_DEFINE_SYMBOL_VERSION("_ZNK5BNode8_GetStatEP4stat",
690 		"_ZNK5BNode7GetStatEP4stat@@LIBBE_TEST");
691 
692 #	endif	// gcc 4
693 #else	// !HAIKU_TARGET_PLATFORM_LIBBE_TEST
694 #	if __GNUC__ == 2	// gcc 2
695 
696 	// BeOS compatible GetStat()
697 	B_DEFINE_SYMBOL_VERSION("_GetStat__C5BNodeP9stat_beos",
698 		"GetStat__C5BNodeP4stat@LIBBE_BASE");
699 
700 	// Haiku GetStat()
701 	B_DEFINE_SYMBOL_VERSION("_GetStat__C5BNodeP4stat",
702 		"GetStat__C5BNodeP4stat@@LIBBE_1_ALPHA1");
703 
704 #	else	// gcc 4
705 
706 	// BeOS compatible GetStat()
707 	B_DEFINE_SYMBOL_VERSION("_ZNK5BNode8_GetStatEP9stat_beos",
708 		"_ZNK5BNode7GetStatEP4stat@LIBBE_BASE");
709 
710 	// Haiku GetStat()
711 	B_DEFINE_SYMBOL_VERSION("_ZNK5BNode8_GetStatEP4stat",
712 		"_ZNK5BNode7GetStatEP4stat@@LIBBE_1_ALPHA1");
713 
714 #	endif	// gcc 4
715 #endif	// !HAIKU_TARGET_PLATFORM_LIBBE_TEST
716