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