1/*
2 * Copyright 2005-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#ifdef BUILDING_FS_SHELL
8#	include "compat.h"
9#	define B_OK			0
10#	define B_FILE_ERROR	EBADF
11#else
12#	include <BeOSBuildCompatibility.h>
13#endif
14
15#include "fs_descriptors.h"
16
17#include <map>
18
19#include <fcntl.h>
20#include <stdio.h>
21#include <stdlib.h>
22#include <unistd.h>
23
24#include <fs_attr.h>
25
26#include <syscalls.h>
27
28#include "fs_impl.h"
29
30using std::map;
31
32static const int kVirtualDescriptorStart = 10000;
33
34typedef map<int, BPrivate::Descriptor*> DescriptorMap;
35static DescriptorMap *sDescriptors;
36
37namespace BPrivate {
38
39
40static int
41dup_maybe_system(int fd)
42{
43	if (get_descriptor(fd) != NULL)
44		return _kern_dup(fd);
45
46	int clonedFD = dup(fd);
47	return clonedFD >= 0 ? clonedFD : errno;
48}
49
50
51static status_t
52close_maybe_system(int fd)
53{
54	if (get_descriptor(fd) != NULL)
55		return _kern_close(fd);
56
57	return close(fd) == 0 ? B_OK : errno;
58}
59
60
61// #pragma mark - Descriptor
62
63
64// constructor
65Descriptor::~Descriptor()
66{
67}
68
69// IsSystemFD
70bool
71Descriptor::IsSystemFD() const
72{
73	return false;
74}
75
76// GetPath
77status_t
78Descriptor::GetPath(string& path) const
79{
80	return get_path(fd, NULL, path);
81}
82
83// GetNodeRef
84status_t
85Descriptor::GetNodeRef(NodeRef &ref)
86{
87	struct stat st;
88	status_t error = GetStat(false, &st);
89	if (error != B_OK)
90		return error;
91
92	ref = NodeRef(st);
93
94	return B_OK;
95}
96
97
98// #pragma mark - FileDescriptor
99
100
101// constructor
102FileDescriptor::FileDescriptor(int fd)
103{
104	this->fd = fd;
105}
106
107// destructor
108FileDescriptor::~FileDescriptor()
109{
110	Close();
111}
112
113// Close
114status_t
115FileDescriptor::Close()
116{
117	if (fd >= 0) {
118		int oldFD = fd;
119		fd = -1;
120		if (close(oldFD) < 0)
121			return errno;
122	}
123
124	return B_OK;
125}
126
127// Dup
128status_t
129FileDescriptor::Dup(Descriptor *&clone)
130{
131	int dupFD = dup(fd);
132	if (dupFD < 0)
133		return errno;
134
135	clone = new FileDescriptor(dupFD);
136	return B_OK;
137}
138
139// GetStat
140status_t
141FileDescriptor::GetStat(bool traverseLink, struct stat *st)
142{
143	if (fstat(fd, st) < 0)
144		return errno;
145	return B_OK;
146}
147
148// IsSystemFD
149bool
150FileDescriptor::IsSystemFD() const
151{
152	return true;
153}
154
155
156// #pragma mark - DirectoryDescriptor
157
158
159// constructor
160DirectoryDescriptor::DirectoryDescriptor(DIR *dir, const NodeRef &ref)
161{
162	this->dir = dir;
163	this->ref = ref;
164}
165
166// destructor
167DirectoryDescriptor::~DirectoryDescriptor()
168{
169	Close();
170}
171
172// Close
173status_t
174DirectoryDescriptor::Close()
175{
176	if (dir) {
177		DIR *oldDir = dir;
178		dir = NULL;
179		if (closedir(oldDir) < 0)
180			return errno;
181	}
182
183	return B_OK;
184}
185
186// Dup
187status_t
188DirectoryDescriptor::Dup(Descriptor *&clone)
189{
190	string path;
191	status_t error = get_path(fd, NULL, path);
192	if (error != B_OK)
193		return error;
194
195	DIR *dupDir = opendir(path.c_str());
196	if (!dupDir)
197		return errno;
198
199	clone = new DirectoryDescriptor(dupDir, ref);
200	return B_OK;
201}
202
203// GetStat
204status_t
205DirectoryDescriptor::GetStat(bool traverseLink, struct stat *st)
206{
207	// get a usable path
208	string realPath;
209	status_t error = get_path(fd, NULL, realPath);
210	if (error != B_OK)
211		return error;
212
213	// stat
214	int result;
215	result = stat(realPath.c_str(), st);
216
217	if (result < 0)
218		return errno;
219
220	return B_OK;
221}
222
223// GetNodeRef
224status_t
225DirectoryDescriptor::GetNodeRef(NodeRef &ref)
226{
227	ref = this->ref;
228
229	return B_OK;
230}
231
232
233// #pragma mark - SymlinkDescriptor
234
235
236// constructor
237SymlinkDescriptor::SymlinkDescriptor(const char *path)
238{
239	this->path = path;
240}
241
242// Close
243status_t
244SymlinkDescriptor::Close()
245{
246	return B_OK;
247}
248
249// Dup
250status_t
251SymlinkDescriptor::Dup(Descriptor *&clone)
252{
253	clone = new SymlinkDescriptor(path.c_str());
254	return B_OK;
255}
256
257// GetStat
258status_t
259SymlinkDescriptor::GetStat(bool traverseLink, struct stat *st)
260{
261	// stat
262	int result;
263	if (traverseLink)
264		result = stat(path.c_str(), st);
265	else
266		result = lstat(path.c_str(), st);
267
268	if (result < 0)
269		return errno;
270
271	return B_OK;
272}
273
274// GetPath
275status_t
276SymlinkDescriptor::GetPath(string& path) const
277{
278	path = this->path;
279	return B_OK;
280}
281
282
283// #pragma mark - AttributeDescriptor
284
285
286AttributeDescriptor::AttributeDescriptor(int fileFD, const char* attribute,
287	uint32 type, int openMode)
288	:
289	fFileFD(dup_maybe_system(fileFD)),
290	fType(type),
291	fOpenMode(openMode),
292	fData(NULL),
293	fDataSize(0)
294
295{
296	strlcpy(fAttribute, attribute, sizeof(fAttribute));
297}
298
299
300AttributeDescriptor::~AttributeDescriptor()
301{
302	Close();
303}
304
305
306status_t
307AttributeDescriptor::Init()
308{
309	if (fFileFD < 0)
310		return B_IO_ERROR;
311
312	// stat the attribute
313	attr_info info;
314	if (fs_stat_attr(fFileFD, fAttribute, &info) < 0) {
315		if (errno == B_ENTRY_NOT_FOUND) {
316			if ((fOpenMode & O_CREAT) == 0)
317				return errno;
318
319			// create the attribute
320			if (fs_write_attr(fFileFD, fAttribute, fType, 0, NULL, 0) < 0)
321				return errno;
322			return B_OK;
323		}
324		return errno;
325	}
326
327	if ((fOpenMode & O_TRUNC) == 0) {
328		// truncate the attribute
329		if (fs_write_attr(fFileFD, fAttribute, fType, 0, NULL, 0) < 0)
330			return errno;
331		return B_OK;
332	}
333
334	// we have to read in the attribute data
335	if (info.size == 0)
336		return B_OK;
337
338	fData = (uint8*)malloc(info.size);
339	if (fData == NULL)
340		return B_NO_MEMORY;
341
342	fDataSize = info.size;
343
344	ssize_t bytesRead = fs_read_attr(fFileFD, fAttribute, fType, 0, fData,
345		fDataSize);
346	if (bytesRead < 0)
347		return errno;
348	if ((size_t)bytesRead != fDataSize)
349		return B_IO_ERROR;
350
351	return B_OK;
352}
353
354
355status_t
356AttributeDescriptor::Write(off_t offset, const void* buffer, size_t bufferSize)
357{
358	if (offset < 0)
359		return B_BAD_VALUE;
360
361	if ((fOpenMode & O_ACCMODE) != O_WRONLY
362		&& (fOpenMode & O_ACCMODE) != O_RDWR) {
363		return B_NOT_ALLOWED;
364	}
365
366	// we may need to resize the buffer
367	size_t minSize = (size_t)offset + bufferSize;
368	if (minSize > fDataSize) {
369		uint8* data = (uint8*)realloc(fData, minSize);
370		if (data == NULL)
371			return B_NO_MEMORY;
372
373		if ((size_t)offset > fDataSize)
374			memset(data + offset, 0, offset - fDataSize);
375
376		fData = data;
377		fDataSize = minSize;
378	}
379
380	// copy the data and write all of it
381	if (bufferSize == 0)
382		return B_OK;
383
384	memcpy((uint8*)fData + offset, buffer, bufferSize);
385
386	ssize_t bytesWritten = fs_write_attr(fFileFD, fAttribute, fType, 0,
387		fData, fDataSize);
388	if (bytesWritten < 0)
389		return errno;
390	if ((size_t)bytesWritten != fDataSize)
391		return B_IO_ERROR;
392
393	return B_OK;
394}
395
396
397status_t
398AttributeDescriptor::Close()
399{
400	if (fFileFD < 0)
401		return B_BAD_VALUE;
402
403	close_maybe_system(fFileFD);
404	fFileFD = -1;
405
406	free(fData);
407	fData = NULL;
408	fDataSize = 0;
409
410	return B_OK;
411}
412
413
414status_t
415AttributeDescriptor::Dup(Descriptor*& clone)
416{
417	return B_NOT_SUPPORTED;
418}
419
420
421status_t
422AttributeDescriptor::GetStat(bool traverseLink, struct stat* st)
423{
424	return B_NOT_SUPPORTED;
425}
426
427
428// #pragma mark - AttrDirDescriptor
429
430
431// constructor
432AttrDirDescriptor::AttrDirDescriptor(DIR *dir, const NodeRef &ref)
433	: DirectoryDescriptor(dir, ref)
434{
435}
436
437// destructor
438AttrDirDescriptor::~AttrDirDescriptor()
439{
440	Close();
441}
442
443// Close
444status_t
445AttrDirDescriptor::Close()
446{
447	if (dir) {
448		DIR *oldDir = dir;
449		dir = NULL;
450		if (fs_close_attr_dir(oldDir) < 0)
451			return errno;
452	}
453
454	return B_OK;
455}
456
457// Dup
458status_t
459AttrDirDescriptor::Dup(Descriptor *&clone)
460{
461	// we don't allow dup()int attr dir descriptors
462	return B_FILE_ERROR;
463}
464
465// GetStat
466status_t
467AttrDirDescriptor::GetStat(bool traverseLink, struct stat *st)
468{
469	// we don't allow stat()int attr dir descriptors
470	return B_FILE_ERROR;
471}
472
473// GetNodeRef
474status_t
475AttrDirDescriptor::GetNodeRef(NodeRef &ref)
476{
477	ref = this->ref;
478
479	return B_OK;
480}
481
482
483// get_descriptor
484Descriptor *
485get_descriptor(int fd)
486{
487	if (!sDescriptors)
488		return NULL;
489	DescriptorMap::iterator it = sDescriptors->find(fd);
490	if (it == sDescriptors->end())
491		return NULL;
492	return it->second;
493}
494
495// add_descriptor
496int
497add_descriptor(Descriptor *descriptor)
498{
499	if (!sDescriptors)
500		sDescriptors = new DescriptorMap;
501
502	int fd = -1;
503	if (FileDescriptor *file = dynamic_cast<FileDescriptor*>(descriptor)) {
504		fd = file->fd;
505	} else {
506		// find a free slot
507		for (fd = kVirtualDescriptorStart;
508			sDescriptors->find(fd) != sDescriptors->end();
509			fd++) {
510		}
511	}
512
513	(*sDescriptors)[fd] = descriptor;
514	descriptor->fd = fd;
515
516	return fd;
517}
518
519// delete_descriptor
520status_t
521delete_descriptor(int fd)
522{
523	DescriptorMap::iterator it = sDescriptors->find(fd);
524	if (it == sDescriptors->end())
525		return B_FILE_ERROR;
526
527	status_t error = it->second->Close();
528	delete it->second;
529	sDescriptors->erase(it);
530
531	if (sDescriptors->size() == 0) {
532		delete sDescriptors;
533		sDescriptors = NULL;
534	}
535	return error;
536}
537
538
539bool
540is_unknown_or_system_descriptor(int fd)
541{
542	Descriptor* descriptor = get_descriptor(fd);
543	return descriptor == NULL || descriptor->IsSystemFD();
544}
545
546
547} // namespace BPrivate
548