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
35node_ref::node_ref()
36	:
37	device((dev_t)-1),
38	node((ino_t)-1)
39{
40}
41
42
43node_ref::node_ref(dev_t device, ino_t node)
44	:
45	device(device),
46	node(node)
47{
48}
49
50
51node_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
60bool
61node_ref::operator==(const node_ref& other) const
62{
63	return (device == other.device && node == other.node);
64}
65
66
67bool
68node_ref::operator!=(const node_ref& other) const
69{
70	return !(*this == other);
71}
72
73
74bool
75node_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
84node_ref&
85node_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
96BNode::BNode()
97	:
98	fFd(-1),
99	fAttrFd(-1),
100	fCStatus(B_NO_INIT)
101{
102}
103
104
105BNode::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
116BNode::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
127BNode::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
138BNode::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
149BNode::BNode(const BNode& node)
150	:
151	fFd(-1),
152	fAttrFd(-1),
153	fCStatus(B_NO_INIT)
154{
155	*this = node;
156}
157
158
159BNode::~BNode()
160{
161	Unset();
162}
163
164
165status_t
166BNode::InitCheck() const
167{
168	return fCStatus;
169}
170
171
172status_t
173BNode::SetTo(const entry_ref* ref)
174{
175	return _SetTo(ref, false);
176}
177
178
179status_t
180BNode::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
191status_t
192BNode::SetTo(const char* path)
193{
194	return _SetTo(-1, path, false);
195}
196
197
198status_t
199BNode::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
211void
212BNode::Unset()
213{
214	close_fd();
215	fCStatus = B_NO_INIT;
216}
217
218
219status_t
220BNode::Lock()
221{
222	if (fCStatus != B_OK)
223		return fCStatus;
224
225	return _kern_lock_node(fFd);
226}
227
228
229status_t
230BNode::Unlock()
231{
232	if (fCStatus != B_OK)
233		return fCStatus;
234
235	return _kern_unlock_node(fFd);
236}
237
238
239status_t
240BNode::Sync()
241{
242	return (fCStatus != B_OK) ? B_FILE_ERROR : _kern_fsync(fFd);
243}
244
245
246ssize_t
247BNode::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
262ssize_t
263BNode::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
278status_t
279BNode::RemoveAttr(const char* name)
280{
281	return fCStatus != B_OK ? B_FILE_ERROR : _kern_remove_attr(fFd, name);
282}
283
284
285status_t
286BNode::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
295status_t
296BNode::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
308status_t
309BNode::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
336status_t
337BNode::RewindAttrs()
338{
339	if (InitAttrDir() != B_OK)
340		return B_FILE_ERROR;
341
342	return _kern_rewind_dir(fAttrFd);
343}
344
345
346status_t
347BNode::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
362status_t
363BNode::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
399BNode&
400BNode::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
417bool
418BNode::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
439bool
440BNode::operator!=(const BNode& node) const
441{
442	return !(*this == node);
443}
444
445
446int
447BNode::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) */
457void BNode::_RudeNode1() { }
458void BNode::_RudeNode2() { }
459void BNode::_RudeNode3() { }
460void BNode::_RudeNode4() { }
461void BNode::_RudeNode5() { }
462void 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*/
481status_t
482BNode::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*/
499void
500BNode::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*/
520void
521BNode::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*/
552status_t
553BNode::_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*/
588status_t
589BNode::_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*/
621status_t
622BNode::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*/
639status_t
640BNode::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
655status_t
656BNode::_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
664status_t
665BNode::_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