xref: /haiku/src/kits/storage/Node.cpp (revision 99d1318ec02694fc520a0dc38ae38565db7e8c3c)
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 longEntry;
323 	struct dirent* entry = longEntry.dirent();
324 	ssize_t result = _kern_read_dir(fAttrFd, entry, sizeof(longEntry), 1);
325 	if (result < 0)
326 		return result;
327 
328 	if (result == 0)
329 		return B_ENTRY_NOT_FOUND;
330 
331 	strlcpy(buffer, entry->d_name, B_ATTR_NAME_LENGTH);
332 
333 	return B_OK;
334 }
335 
336 
337 status_t
338 BNode::RewindAttrs()
339 {
340 	if (InitAttrDir() != B_OK)
341 		return B_FILE_ERROR;
342 
343 	return _kern_rewind_dir(fAttrFd);
344 }
345 
346 
347 status_t
348 BNode::WriteAttrString(const char* name, const BString* data)
349 {
350 	status_t error = (!name || !data)  ? B_BAD_VALUE : B_OK;
351 	if (error == B_OK) {
352 		int32 length = data->Length() + 1;
353 		ssize_t sizeWritten = WriteAttr(name, B_STRING_TYPE, 0, data->String(),
354 			length);
355 		if (sizeWritten != length)
356 			error = sizeWritten;
357 	}
358 
359 	return error;
360 }
361 
362 
363 status_t
364 BNode::ReadAttrString(const char* name, BString* result) const
365 {
366 	if (name == NULL || result == NULL)
367 		return B_BAD_VALUE;
368 
369 	attr_info info;
370 	status_t error;
371 
372 	error = GetAttrInfo(name, &info);
373 	if (error != B_OK)
374 		return error;
375 
376 	// Lock the string's buffer so we can meddle with it
377 	char* data = result->LockBuffer(info.size + 1);
378 	if (data == NULL)
379 		return B_NO_MEMORY;
380 
381 	// Read the attribute
382 	ssize_t bytes = ReadAttr(name, B_STRING_TYPE, 0, data, info.size);
383 	// Check for failure
384 	if (bytes < 0) {
385 		error = bytes;
386 		bytes = 0;
387 			// In this instance, we simply clear the string
388 	} else
389 		error = B_OK;
390 
391 	// Null terminate the new string just to be sure (since it *is*
392 	// possible to read and write non-NULL-terminated strings)
393 	data[bytes] = 0;
394 	result->UnlockBuffer();
395 
396 	return error;
397 }
398 
399 
400 BNode&
401 BNode::operator=(const BNode& node)
402 {
403 	// No need to do any assignment if already equal
404 	if (*this == node)
405 		return *this;
406 
407 	// Close down out current state
408 	Unset();
409 	// We have to manually dup the node, because R5::BNode::Dup()
410 	// is not declared to be const (which IMO is retarded).
411 	fFd = _kern_dup(node.fFd);
412 	fCStatus = (fFd < 0) ? B_NO_INIT : B_OK ;
413 
414 	return *this;
415 }
416 
417 
418 bool
419 BNode::operator==(const BNode& node) const
420 {
421 	if (fCStatus == B_NO_INIT && node.InitCheck() == B_NO_INIT)
422 		return true;
423 
424 	if (fCStatus == B_OK && node.InitCheck() == B_OK) {
425 		// compare the node_refs
426 		node_ref ref1, ref2;
427 		if (GetNodeRef(&ref1) != B_OK)
428 			return false;
429 
430 		if (node.GetNodeRef(&ref2) != B_OK)
431 			return false;
432 
433 		return (ref1 == ref2);
434 	}
435 
436 	return false;
437 }
438 
439 
440 bool
441 BNode::operator!=(const BNode& node) const
442 {
443 	return !(*this == node);
444 }
445 
446 
447 int
448 BNode::Dup()
449 {
450 	int fd = _kern_dup(fFd);
451 
452 	return (fd >= 0 ? fd : -1);
453 		// comply with R5 return value
454 }
455 
456 
457 /*! (currently unused) */
458 void BNode::_RudeNode1() { }
459 void BNode::_RudeNode2() { }
460 void BNode::_RudeNode3() { }
461 void BNode::_RudeNode4() { }
462 void BNode::_RudeNode5() { }
463 void BNode::_RudeNode6() { }
464 
465 
466 /*!	Sets the node's file descriptor.
467 
468 	Used by each implementation (i.e. BNode, BFile, BDirectory, etc.) to set
469 	the node's file descriptor. This allows each subclass to use the various
470 	file-type specific system calls for opening file descriptors.
471 
472 	\note This method calls close_fd() to close previously opened FDs. Thus
473 		derived classes should take care to first call set_fd() and set
474 		class specific resources freed in their close_fd() version
475 		thereafter.
476 
477 	\param fd the file descriptor this BNode should be set to (may be -1).
478 
479 	\returns \c B_OK if everything went fine, or an error code if something
480 		went wrong.
481 */
482 status_t
483 BNode::set_fd(int fd)
484 {
485 	if (fFd != -1)
486 		close_fd();
487 
488 	fFd = fd;
489 
490 	return B_OK;
491 }
492 
493 
494 /*!	Closes the node's file descriptor(s).
495 
496 	To be implemented by subclasses to close the file descriptor using the
497 	proper system call for the given file-type. This implementation calls
498 	_kern_close(fFd) and also _kern_close(fAttrDir) if necessary.
499 */
500 void
501 BNode::close_fd()
502 {
503 	if (fAttrFd >= 0) {
504 		_kern_close(fAttrFd);
505 		fAttrFd = -1;
506 	}
507 	if (fFd >= 0) {
508 		_kern_close(fFd);
509 		fFd = -1;
510 	}
511 }
512 
513 
514 /*!	Sets the BNode's status.
515 
516 	To be used by derived classes instead of accessing the BNode's private
517 	\c fCStatus member directly.
518 
519 	\param newStatus the new value for the status variable.
520 */
521 void
522 BNode::set_status(status_t newStatus)
523 {
524 	fCStatus = newStatus;
525 }
526 
527 
528 /*!	Initializes the BNode's file descriptor to the node referred to
529 	by the given FD and path combo.
530 
531 	\a path must either be \c NULL, an absolute or a relative path.
532 	In the first case, \a fd must not be \c NULL; the node it refers to will
533 	be opened. If absolute, \a fd is ignored. If relative and \a fd is >= 0,
534 	it will be reckoned off the directory identified by \a fd, otherwise off
535 	the current working directory.
536 
537 	The method will first try to open the node with read and write permission.
538 	If that fails due to a read-only FS or because the user has no write
539 	permission for the node, it will re-try opening the node read-only.
540 
541 	The \a fCStatus member will be set to the return value of this method.
542 
543 	\param fd Either a directory FD or a value < 0. In the latter case \a path
544 	       must be specified.
545 	\param path Either \a NULL in which case \a fd must be given, absolute, or
546 	       relative to the directory specified by \a fd (if given) or to the
547 	       current working directory.
548 	\param traverse If the node identified by \a fd and \a path is a symlink
549 	       and \a traverse is \c true, the symlink will be resolved recursively.
550 
551 	\returns \c B_OK if everything went fine, or an error code otherwise.
552 */
553 status_t
554 BNode::_SetTo(int fd, const char* path, bool traverse)
555 {
556 	Unset();
557 
558 	status_t error = (fd >= 0 || path ? B_OK : B_BAD_VALUE);
559 	if (error == B_OK) {
560 		int traverseFlag = (traverse ? 0 : O_NOTRAVERSE);
561 		fFd = _kern_open(fd, path, O_RDWR | O_CLOEXEC | traverseFlag, 0);
562 		if (fFd < B_OK && fFd != B_ENTRY_NOT_FOUND) {
563 			// opening read-write failed, re-try read-only
564 			fFd = _kern_open(fd, path, O_RDONLY | O_CLOEXEC | traverseFlag, 0);
565 		}
566 		if (fFd < 0)
567 			error = fFd;
568 	}
569 
570 	return fCStatus = error;
571 }
572 
573 
574 /*!	Initializes the BNode's file descriptor to the node referred to
575 	by the given entry_ref.
576 
577 	The method will first try to open the node with read and write permission.
578 	If that fails due to a read-only FS or because the user has no write
579 	permission for the node, it will re-try opening the node read-only.
580 
581 	The \a fCStatus member will be set to the return value of this method.
582 
583 	\param ref An entry_ref identifying the node to be opened.
584 	\param traverse If the node identified by \a ref is a symlink and
585 	       \a traverse is \c true, the symlink will be resolved recursively.
586 
587 	\returns \c B_OK if everything went fine, or an error code otherwise.
588 */
589 status_t
590 BNode::_SetTo(const entry_ref* ref, bool traverse)
591 {
592 	Unset();
593 
594 	status_t result = (ref ? B_OK : B_BAD_VALUE);
595 	if (result == B_OK) {
596 		int traverseFlag = (traverse ? 0 : O_NOTRAVERSE);
597 		fFd = _kern_open_entry_ref(ref->device, ref->directory, ref->name,
598 			O_RDWR | O_CLOEXEC | traverseFlag, 0);
599 		if (fFd < B_OK && fFd != B_ENTRY_NOT_FOUND) {
600 			// opening read-write failed, re-try read-only
601 			fFd = _kern_open_entry_ref(ref->device, ref->directory, ref->name,
602 				O_RDONLY | O_CLOEXEC | traverseFlag, 0);
603 		}
604 		if (fFd < 0)
605 			result = fFd;
606 	}
607 
608 	return fCStatus = result;
609 }
610 
611 
612 /*!	Modifies a certain setting for this node based on \a what and the
613 	corresponding value in \a st.
614 
615 	Inherited from and called by BStatable.
616 
617 	\param st a stat structure containing the value to be set.
618 	\param what specifies what setting to be modified.
619 
620 	\returns \c B_OK if everything went fine, or an error code otherwise.
621 */
622 status_t
623 BNode::set_stat(struct stat& stat, uint32 what)
624 {
625 	if (fCStatus != B_OK)
626 		return B_FILE_ERROR;
627 
628 	return _kern_write_stat(fFd, NULL, false, &stat, sizeof(struct stat),
629 		what);
630 }
631 
632 
633 
634 /*!	Verifies that the BNode has been properly initialized, and then
635 	(if necessary) opens the attribute directory on the node's file
636 	descriptor, storing it in fAttrDir.
637 
638 	\returns \c B_OK if everything went fine, or an error code otherwise.
639 */
640 status_t
641 BNode::InitAttrDir()
642 {
643 	if (fCStatus == B_OK && fAttrFd < 0) {
644 		fAttrFd = _kern_open_attr_dir(fFd, NULL, false);
645 		if (fAttrFd < 0)
646 			return fAttrFd;
647 
648 		// set close on exec flag
649 		fcntl(fAttrFd, F_SETFD, FD_CLOEXEC);
650 	}
651 
652 	return fCStatus;
653 }
654 
655 
656 status_t
657 BNode::_GetStat(struct stat* stat) const
658 {
659 	return fCStatus != B_OK
660 		? fCStatus
661 		: _kern_read_stat(fFd, NULL, false, stat, sizeof(struct stat));
662 }
663 
664 
665 status_t
666 BNode::_GetStat(struct stat_beos* stat) const
667 {
668 	struct stat newStat;
669 	status_t error = _GetStat(&newStat);
670 	if (error != B_OK)
671 		return error;
672 
673 	convert_to_stat_beos(&newStat, stat);
674 
675 	return B_OK;
676 }
677 
678 
679 //	#pragma mark - symbol versions
680 
681 
682 #ifdef HAIKU_TARGET_PLATFORM_LIBBE_TEST
683 #	if __GNUC__ == 2	// gcc 2
684 
685 	B_DEFINE_SYMBOL_VERSION("_GetStat__C5BNodeP4stat",
686 		"GetStat__C5BNodeP4stat@@LIBBE_TEST");
687 
688 #	else	// gcc 4
689 
690 	B_DEFINE_SYMBOL_VERSION("_ZNK5BNode8_GetStatEP4stat",
691 		"_ZNK5BNode7GetStatEP4stat@@LIBBE_TEST");
692 
693 #	endif	// gcc 4
694 #else	// !HAIKU_TARGET_PLATFORM_LIBBE_TEST
695 #	if __GNUC__ == 2	// gcc 2
696 
697 	// BeOS compatible GetStat()
698 	B_DEFINE_SYMBOL_VERSION("_GetStat__C5BNodeP9stat_beos",
699 		"GetStat__C5BNodeP4stat@LIBBE_BASE");
700 
701 	// Haiku GetStat()
702 	B_DEFINE_SYMBOL_VERSION("_GetStat__C5BNodeP4stat",
703 		"GetStat__C5BNodeP4stat@@LIBBE_1_ALPHA1");
704 
705 #	else	// gcc 4
706 
707 	// BeOS compatible GetStat()
708 	B_DEFINE_SYMBOL_VERSION("_ZNK5BNode8_GetStatEP9stat_beos",
709 		"_ZNK5BNode7GetStatEP4stat@LIBBE_BASE");
710 
711 	// Haiku GetStat()
712 	B_DEFINE_SYMBOL_VERSION("_ZNK5BNode8_GetStatEP4stat",
713 		"_ZNK5BNode7GetStatEP4stat@@LIBBE_1_ALPHA1");
714 
715 #	endif	// gcc 4
716 #endif	// !HAIKU_TARGET_PLATFORM_LIBBE_TEST
717