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 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
337status_t
338BNode::RewindAttrs()
339{
340	if (InitAttrDir() != B_OK)
341		return B_FILE_ERROR;
342
343	return _kern_rewind_dir(fAttrFd);
344}
345
346
347status_t
348BNode::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
363status_t
364BNode::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
400BNode&
401BNode::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
418bool
419BNode::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
440bool
441BNode::operator!=(const BNode& node) const
442{
443	return !(*this == node);
444}
445
446
447int
448BNode::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) */
458void BNode::_RudeNode1() { }
459void BNode::_RudeNode2() { }
460void BNode::_RudeNode3() { }
461void BNode::_RudeNode4() { }
462void BNode::_RudeNode5() { }
463void 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*/
482status_t
483BNode::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*/
500void
501BNode::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*/
521void
522BNode::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*/
553status_t
554BNode::_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*/
589status_t
590BNode::_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*/
622status_t
623BNode::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*/
640status_t
641BNode::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
656status_t
657BNode::_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
665status_t
666BNode::_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