1// FDManager.cpp
2
3#include <new>
4
5#include <dirent.h>
6#include <errno.h>
7#include <fcntl.h>
8#include <sys/resource.h>
9
10#include <Directory.h>
11#include <Entry.h>
12#include <File.h>
13#include <fs_attr.h>
14
15#include "AutoLocker.h"
16#include "FDManager.h"
17
18// FD constants
19static const int32 kDefaultFDLimit = 512;
20static const int32 kFDLimitIncrement = 128;
21
22
23// constructor
24FDManager::FDManager()
25	: fLock("FD manager"),
26	  fFDLimit(kDefaultFDLimit)
27{
28}
29
30// destructor
31FDManager::~FDManager()
32{
33}
34
35// Init
36status_t
37FDManager::Init()
38{
39	// ask for a bunch more file descriptors
40	struct rlimit rl;
41	rl.rlim_cur = fFDLimit;
42	rl.rlim_max = RLIM_SAVED_MAX;
43	if (setrlimit(RLIMIT_NOFILE, &rl) < 0)
44		return errno;
45
46	return B_OK;
47}
48
49// CreateDefault
50status_t
51FDManager::CreateDefault()
52{
53	if (sManager)
54		return B_OK;
55
56	FDManager* manager = new(std::nothrow) FDManager;
57	if (!manager)
58		return B_NO_MEMORY;
59
60	status_t error = manager->Init();
61	if (error != B_OK) {
62		delete manager;
63		return error;
64	}
65
66	sManager = manager;
67	return B_OK;
68}
69
70// DeleteDefault
71void
72FDManager::DeleteDefault()
73{
74	if (sManager) {
75		delete sManager;
76		sManager = NULL;
77	}
78}
79
80// GetDefault
81FDManager*
82FDManager::GetDefault()
83{
84	return sManager;
85}
86
87// SetDirectory
88status_t
89FDManager::SetDirectory(BDirectory* directory, const node_ref* ref)
90{
91	status_t error = directory->SetTo(ref);
92
93	if (error == B_NO_MORE_FDS) {
94		GetDefault()->_IncreaseLimit();
95		error = directory->SetTo(ref);
96	}
97
98	return error;
99}
100
101// SetEntry
102status_t
103FDManager::SetEntry(BEntry* entry, const entry_ref* ref)
104{
105	status_t error = entry->SetTo(ref);
106
107	if (error == B_NO_MORE_FDS) {
108		GetDefault()->_IncreaseLimit();
109		error = entry->SetTo(ref);
110	}
111
112	return error;
113}
114
115// SetEntry
116status_t
117FDManager::SetEntry(BEntry* entry, const char* path)
118{
119	status_t error = entry->SetTo(path);
120
121	if (error == B_NO_MORE_FDS) {
122		GetDefault()->_IncreaseLimit();
123		error = entry->SetTo(path);
124	}
125
126	return error;
127}
128
129// SetFile
130status_t
131FDManager::SetFile(BFile* file, const char* path, uint32 openMode)
132{
133	status_t error = file->SetTo(path, openMode);
134
135	if (error == B_NO_MORE_FDS) {
136		GetDefault()->_IncreaseLimit();
137		error = file->SetTo(path, openMode);
138	}
139
140	return error;
141}
142
143// SetNode
144status_t
145FDManager::SetNode(BNode* node, const entry_ref* ref)
146{
147	status_t error = node->SetTo(ref);
148
149	if (error == B_NO_MORE_FDS) {
150		GetDefault()->_IncreaseLimit();
151		error = node->SetTo(ref);
152	}
153
154	return error;
155}
156
157// Open
158status_t
159FDManager::Open(const char* path, uint32 openMode, mode_t mode, int& fd)
160{
161	status_t error = B_OK;
162	fd = open(path, openMode, mode);
163	if (fd < 0)
164		error = errno;
165
166	if (error == B_NO_MORE_FDS) {
167		GetDefault()->_IncreaseLimit();
168
169		error = B_OK;
170		fd = open(path, openMode, mode);
171		if (fd < 0)
172			error = errno;
173	}
174
175	return error;
176}
177
178// OpenDir
179status_t
180FDManager::OpenDir(const char* path, DIR*& dir)
181{
182	status_t error = B_OK;
183	dir = opendir(path);
184	if (!dir)
185		error = errno;
186
187	if (error == B_NO_MORE_FDS) {
188		GetDefault()->_IncreaseLimit();
189
190		error = B_OK;
191		dir = opendir(path);
192		if (!dir)
193			error = errno;
194	}
195
196	return error;
197}
198
199// OpenAttrDir
200status_t
201FDManager::OpenAttrDir(const char* path, DIR*& dir)
202{
203	status_t error = B_OK;
204	dir = fs_open_attr_dir(path);
205	if (!dir)
206		error = errno;
207
208	if (error == B_NO_MORE_FDS) {
209		GetDefault()->_IncreaseLimit();
210
211		error = B_OK;
212		dir = fs_open_attr_dir(path);
213		if (!dir)
214			error = errno;
215	}
216
217	return error;
218}
219
220// _IncreaseLimit
221status_t
222FDManager::_IncreaseLimit()
223{
224	AutoLocker<Locker> _(fLock);
225
226	int32 newLimit = fFDLimit + kFDLimitIncrement;
227
228	struct rlimit rl;
229	rl.rlim_cur = newLimit;
230	rl.rlim_max = RLIM_SAVED_MAX;
231	if (setrlimit(RLIMIT_NOFILE, &rl) < 0)
232		return errno;
233
234	fFDLimit = newLimit;
235
236	return B_OK;
237}
238
239
240// sManager
241FDManager* FDManager::sManager = NULL;
242