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
33node_ref::node_ref()
34	:
35	device((dev_t)-1),
36	node((ino_t)-1)
37{
38}
39
40
41node_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
50bool
51node_ref::operator==(const node_ref& other) const
52{
53	return (device == other.device && node == other.node);
54}
55
56
57bool
58node_ref::operator!=(const node_ref& other) const
59{
60	return !(*this == other);
61}
62
63
64bool
65node_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
74node_ref&
75node_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
86BNode::BNode()
87	:
88	fFd(-1),
89	fAttrFd(-1),
90	fCStatus(B_NO_INIT)
91{
92}
93
94
95BNode::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
106BNode::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
117BNode::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
128BNode::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
139BNode::BNode(const BNode& node)
140	:
141	fFd(-1),
142	fAttrFd(-1),
143	fCStatus(B_NO_INIT)
144{
145	*this = node;
146}
147
148
149BNode::~BNode()
150{
151	Unset();
152}
153
154
155status_t
156BNode::InitCheck() const
157{
158	return fCStatus;
159}
160
161
162status_t
163BNode::SetTo(const entry_ref* ref)
164{
165	return _SetTo(ref, false);
166}
167
168
169status_t
170BNode::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
181status_t
182BNode::SetTo(const char* path)
183{
184	return _SetTo(-1, path, false);
185}
186
187
188status_t
189BNode::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
201void
202BNode::Unset()
203{
204	close_fd();
205	fCStatus = B_NO_INIT;
206}
207
208
209status_t
210BNode::Lock()
211{
212	if (fCStatus != B_OK)
213		return fCStatus;
214
215	return _kern_lock_node(fFd);
216}
217
218
219status_t
220BNode::Unlock()
221{
222	if (fCStatus != B_OK)
223		return fCStatus;
224
225	return _kern_unlock_node(fFd);
226}
227
228
229status_t
230BNode::Sync()
231{
232	return (fCStatus != B_OK) ? B_FILE_ERROR : _kern_fsync(fFd);
233}
234
235
236ssize_t
237BNode::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
252ssize_t
253BNode::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
268status_t
269BNode::RemoveAttr(const char* name)
270{
271	return fCStatus != B_OK ? B_FILE_ERROR : _kern_remove_attr(fFd, name);
272}
273
274
275status_t
276BNode::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
285status_t
286BNode::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
298status_t
299BNode::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
327status_t
328BNode::RewindAttrs()
329{
330	if (InitAttrDir() != B_OK)
331		return B_FILE_ERROR;
332
333	return _kern_rewind_dir(fAttrFd);
334}
335
336
337status_t
338BNode::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
353status_t
354BNode::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
390BNode&
391BNode::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
408bool
409BNode::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
430bool
431BNode::operator!=(const BNode& node) const
432{
433	return !(*this == node);
434}
435
436
437int
438BNode::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) */
448void BNode::_RudeNode1() { }
449void BNode::_RudeNode2() { }
450void BNode::_RudeNode3() { }
451void BNode::_RudeNode4() { }
452void BNode::_RudeNode5() { }
453void 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*/
472status_t
473BNode::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*/
490void
491BNode::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*/
511void
512BNode::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*/
543status_t
544BNode::_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*/
579status_t
580BNode::_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*/
612status_t
613BNode::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*/
630status_t
631BNode::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
646status_t
647BNode::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