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