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