BPicture: Fix archive constructor.
[haiku.git] / src / add-ons / kernel / file_systems / nfs4 / InodeDir.cpp
blob4d78e096fe7bf7caf31ab9f234ba37aa7a78470e
1 /*
2 * Copyright 2012 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Paweł Dziepak, pdziepak@quarnos.org
7 */
10 #include "Inode.h"
12 #include <dirent.h>
13 #include <string.h>
15 #include "IdMap.h"
16 #include "Request.h"
17 #include "RootInode.h"
20 status_t
21 Inode::CreateDir(const char* name, int mode, ino_t* id)
23 return CreateObject(name, NULL, mode, NF4DIR, id);
27 status_t
28 Inode::OpenDir(OpenDirCookie* cookie)
30 ASSERT(cookie != NULL);
32 if (fType != NF4DIR)
33 return B_NOT_A_DIRECTORY;
35 status_t result = Access(R_OK);
36 if (result != B_OK)
37 return result;
39 cookie->fSpecial = 0;
40 cookie->fSnapshot = NULL;
41 cookie->fCurrent = NULL;
42 cookie->fEOF = false;
43 cookie->fAttrDir = false;
45 return B_OK;
49 status_t
50 Inode::OpenAttrDir(OpenDirCookie* cookie)
52 ASSERT(cookie != NULL);
54 cookie->fSpecial = 0;
55 cookie->fSnapshot = NULL;
56 cookie->fCurrent = NULL;
57 cookie->fEOF = false;
58 cookie->fAttrDir = true;
60 return LoadAttrDirHandle();
64 status_t
65 Inode::LoadAttrDirHandle()
67 if (fInfo.fAttrDir.fSize != 0)
68 return B_OK;
70 FileHandle handle;
71 status_t result;
73 if (fFileSystem->NamedAttrs()) {
74 result = NFS4Inode::OpenAttrDir(&handle);
75 if (result == B_OK) {
76 fInfo.fAttrDir = handle;
77 return B_OK;
80 if (result != B_UNSUPPORTED)
81 return result;
83 fFileSystem->SetNamedAttrs(false);
86 if (!fFileSystem->GetConfiguration().fEmulateNamedAttrs)
87 return B_UNSUPPORTED;
89 char* attrDir
90 = reinterpret_cast<char*>(malloc(strlen(Name()) + 32));
91 if (attrDir == NULL)
92 return B_NO_MEMORY;
93 strcpy(attrDir, ".");
94 strcat(attrDir, Name());
95 strcat(attrDir, "-haiku-attrs");
97 result = NFS4Inode::LookUp(attrDir, NULL, NULL, &handle, true);
98 if (result == B_ENTRY_NOT_FOUND) {
99 ChangeInfo change;
100 struct stat st;
101 Stat(&st);
102 st.st_mode |= S_IXUSR | S_IXGRP | S_IXOTH;
103 result = NFS4Inode::CreateObject(attrDir, NULL, st.st_mode, NF4DIR,
104 &change, NULL, &handle, true);
107 free(attrDir);
109 if (result != B_OK)
110 return result;
112 fInfo.fAttrDir = handle;
113 return B_OK;
117 status_t
118 Inode::FillDirEntry(struct dirent* de, ino_t id, const char* name, uint32 pos,
119 uint32 size)
121 ASSERT(de != NULL);
122 ASSERT(name != NULL);
124 uint32 nameSize = strlen(name) + 1;
125 const uint32 entSize = sizeof(struct dirent);
127 if (pos + entSize + nameSize > size)
128 return B_BUFFER_OVERFLOW;
130 de->d_dev = fFileSystem->DevId();
131 de->d_ino = id;
132 de->d_reclen = entSize + nameSize;
133 if (de->d_reclen % 8 != 0)
134 de->d_reclen += 8 - de->d_reclen % 8;
136 strcpy(de->d_name, name);
138 return B_OK;
142 status_t
143 Inode::ReadDirUp(struct dirent* de, uint32 pos, uint32 size)
145 ASSERT(de != NULL);
147 uint32 attempt = 0;
148 do {
149 RPC::Server* serv = fFileSystem->Server();
150 Request request(serv, fFileSystem);
151 RequestBuilder& req = request.Builder();
153 req.PutFH(fInfo.fHandle);
154 req.LookUpUp();
155 req.GetFH();
157 if (fFileSystem->IsAttrSupported(FATTR4_FILEID)) {
158 Attribute attr[] = { FATTR4_FILEID };
159 req.GetAttr(attr, sizeof(attr) / sizeof(Attribute));
162 status_t result = request.Send();
163 if (result != B_OK)
164 return result;
166 ReplyInterpreter& reply = request.Reply();
168 if (HandleErrors(attempt, reply.NFS4Error(), serv))
169 continue;
171 reply.PutFH();
172 result = reply.LookUpUp();
173 if (result != B_OK)
174 return result;
176 FileHandle fh;
177 reply.GetFH(&fh);
179 uint64 fileId;
180 if (fFileSystem->IsAttrSupported(FATTR4_FILEID)) {
181 AttrValue* values;
182 uint32 count;
183 result = reply.GetAttr(&values, &count);
184 if (result != B_OK)
185 return result;
187 fileId = values[0].fData.fValue64;
188 delete[] values;
189 } else
190 fileId = fFileSystem->AllocFileId();
192 return FillDirEntry(de, FileIdToInoT(fileId), "..", pos, size);
193 } while (true);
197 static char*
198 FileToAttrName(const char* path)
200 ASSERT(path != NULL);
202 char* name = strdup(path);
203 if (name == NULL)
204 return NULL;
206 char* current = strpbrk(name, "#$");
207 while (current != NULL) {
208 switch (*current) {
209 case '#':
210 *current = '/';
211 break;
212 case '$':
213 *current = ':';
214 break;
216 current = strpbrk(name, "#$");
219 return name;
223 status_t
224 Inode::GetDirSnapshot(DirectoryCacheSnapshot** _snapshot,
225 OpenDirCookie* cookie, uint64* _change, bool attribute)
227 ASSERT(_snapshot != NULL);
229 DirectoryCacheSnapshot* snapshot = new DirectoryCacheSnapshot;
230 if (snapshot == NULL)
231 return B_NO_MEMORY;
233 uint64 change = 0;
234 uint64 dirCookie = 0;
235 uint64 dirCookieVerf = 0;
236 bool eof = false;
238 while (!eof) {
239 uint32 count;
240 DirEntry* dirents;
242 status_t result = ReadDirOnce(&dirents, &count, cookie, &eof, &change,
243 &dirCookie, &dirCookieVerf, attribute);
244 if (result != B_OK) {
245 delete snapshot;
246 return result;
249 uint32 i;
250 for (i = 0; i < count; i++) {
252 // FATTR4_FSID is mandatory
253 void* data = dirents[i].fAttrs[0].fData.fPointer;
254 FileSystemId* fsid = reinterpret_cast<FileSystemId*>(data);
255 if (*fsid != fFileSystem->FsId())
256 continue;
258 if (strstr(dirents[i].fName, "-haiku-attrs") != NULL)
259 continue;
261 ino_t id;
262 if (!attribute) {
263 if (dirents[i].fAttrCount == 2)
264 id = FileIdToInoT(dirents[i].fAttrs[1].fData.fValue64);
265 else
266 id = FileIdToInoT(fFileSystem->AllocFileId());
267 } else
268 id = 0;
270 const char* name = dirents[i].fName;
271 if (attribute)
272 name = FileToAttrName(name);
273 if (name == NULL) {
274 delete snapshot;
275 delete[] dirents;
276 return B_NO_MEMORY;
279 NameCacheEntry* entry = new NameCacheEntry(name, id);
280 if (attribute)
281 free(const_cast<char*>(name));
283 if (entry == NULL || entry->fName == NULL) {
284 if (entry != NULL)
285 delete entry;
286 delete snapshot;
287 delete[] dirents;
288 return B_NO_MEMORY;
290 snapshot->fEntries.Add(entry);
293 delete[] dirents;
296 *_snapshot = snapshot;
297 *_change = change;
299 return B_OK;
303 status_t
304 Inode::ReadDir(void* _buffer, uint32 size, uint32* _count,
305 OpenDirCookie* cookie)
307 ASSERT(_buffer != NULL);
308 ASSERT(_count != NULL);
309 ASSERT(cookie != NULL);
311 if (cookie->fEOF) {
312 *_count = 0;
313 return B_OK;
316 status_t result;
317 DirectoryCache* cache = cookie->fAttrDir ? fAttrCache : fCache;
318 if (cookie->fSnapshot == NULL) {
319 cache->Lock();
320 result = cache->Revalidate();
321 if (result != B_OK) {
322 cache->Unlock();
323 return result;
326 DirectoryCacheSnapshot* snapshot;
327 result = cache->GetSnapshot(&snapshot);
328 if (result != B_OK) {
329 cache->Unlock();
330 return result;
333 cookie->fSnapshot = new DirectoryCacheSnapshot(*snapshot);
334 cache->Unlock();
336 if (cookie->fSnapshot == NULL)
337 return B_NO_MEMORY;
340 char* buffer = reinterpret_cast<char*>(_buffer);
341 uint32 pos = 0;
342 uint32 i = 0;
343 bool overflow = false;
345 if (cookie->fSpecial == 0 && i < *_count && !cookie->fAttrDir) {
346 struct dirent* de = reinterpret_cast<dirent*>(buffer + pos);
348 status_t result;
349 result = FillDirEntry(de, fInfo.fFileId, ".", pos, size);
351 if (result == B_BUFFER_OVERFLOW)
352 overflow = true;
353 else if (result == B_OK) {
354 pos += de->d_reclen;
355 i++;
356 cookie->fSpecial++;
357 } else
358 return result;
361 if (cookie->fSpecial == 1 && i < *_count && !cookie->fAttrDir) {
362 struct dirent* de = reinterpret_cast<dirent*>(buffer + pos);
364 status_t result;
365 result = ReadDirUp(de, pos, size);
366 if (result == B_ENTRY_NOT_FOUND) {
367 result = FillDirEntry(de, FileIdToInoT(fInfo.fFileId), "..", pos,
368 size);
371 if (result == B_BUFFER_OVERFLOW)
372 overflow = true;
373 else if (result == B_OK) {
374 pos += de->d_reclen;
375 i++;
376 cookie->fSpecial++;
377 } else
378 return result;
381 MutexLocker _(cookie->fSnapshot->fLock);
382 for (; !overflow && i < *_count; i++) {
383 struct dirent* de = reinterpret_cast<dirent*>(buffer + pos);
384 NameCacheEntry* temp = cookie->fCurrent;
386 if (cookie->fCurrent == NULL)
387 cookie->fCurrent = cookie->fSnapshot->fEntries.Head();
388 else {
389 cookie->fCurrent
390 = cookie->fSnapshot->fEntries.GetNext(cookie->fCurrent);
393 if (cookie->fCurrent == NULL) {
394 cookie->fEOF = true;
395 break;
398 if (FillDirEntry(de, cookie->fCurrent->fNode, cookie->fCurrent->fName,
399 pos, size) == B_BUFFER_OVERFLOW) {
400 cookie->fCurrent = temp;
401 overflow = true;
402 break;
405 pos += de->d_reclen;
408 if (i == 0 && overflow)
409 return B_BUFFER_OVERFLOW;
411 *_count = i;
413 return B_OK;