/* * Copyright 2012 Haiku, Inc. All rights reserved. * Distributed under the terms of the MIT License. * * Authors: * Paweł Dziepak, pdziepak@quarnos.org */ #include "Inode.h" #include #include #include "IdMap.h" #include "Request.h" #include "RootInode.h" status_t Inode::CreateDir(const char* name, int mode, ino_t* id) { return CreateObject(name, NULL, mode, NF4DIR, id); } status_t Inode::OpenDir(OpenDirCookie* cookie) { ASSERT(cookie != NULL); if (fType != NF4DIR) return B_NOT_A_DIRECTORY; status_t result = Access(R_OK); if (result != B_OK) return result; cookie->fSpecial = 0; cookie->fSnapshot = NULL; cookie->fCurrent = NULL; cookie->fEOF = false; cookie->fAttrDir = false; return B_OK; } status_t Inode::OpenAttrDir(OpenDirCookie* cookie) { ASSERT(cookie != NULL); cookie->fSpecial = 0; cookie->fSnapshot = NULL; cookie->fCurrent = NULL; cookie->fEOF = false; cookie->fAttrDir = true; return LoadAttrDirHandle(); } status_t Inode::LoadAttrDirHandle() { if (fInfo.fAttrDir.fSize != 0) return B_OK; FileHandle handle; status_t result; if (fFileSystem->NamedAttrs()) { result = NFS4Inode::OpenAttrDir(&handle); if (result == B_OK) { fInfo.fAttrDir = handle; return B_OK; } if (result != B_UNSUPPORTED) return result; fFileSystem->SetNamedAttrs(false); } if (!fFileSystem->GetConfiguration().fEmulateNamedAttrs) return B_UNSUPPORTED; char* attrDir = reinterpret_cast(malloc(strlen(Name()) + 32)); if (attrDir == NULL) return B_NO_MEMORY; strcpy(attrDir, "."); strcat(attrDir, Name()); strcat(attrDir, "-haiku-attrs"); result = NFS4Inode::LookUp(attrDir, NULL, NULL, &handle, true); if (result == B_ENTRY_NOT_FOUND) { ChangeInfo change; struct stat st; Stat(&st); st.st_mode |= S_IXUSR | S_IXGRP | S_IXOTH; result = NFS4Inode::CreateObject(attrDir, NULL, st.st_mode, NF4DIR, &change, NULL, &handle, true); } free(attrDir); if (result != B_OK) return result; fInfo.fAttrDir = handle; return B_OK; } status_t Inode::FillDirEntry(struct dirent* de, ino_t id, const char* name, uint32 pos, uint32 size) { ASSERT(de != NULL); ASSERT(name != NULL); uint32 nameSize = strlen(name) + 1; const uint32 entSize = offsetof(struct dirent, d_name); if (pos + entSize + nameSize > size) return B_BUFFER_OVERFLOW; de->d_dev = fFileSystem->DevId(); de->d_ino = id; de->d_reclen = entSize + nameSize; if (de->d_reclen % 8 != 0) de->d_reclen += 8 - de->d_reclen % 8; strcpy(de->d_name, name); return B_OK; } status_t Inode::ReadDirUp(struct dirent* de, uint32 pos, uint32 size) { ASSERT(de != NULL); uint32 attempt = 0; do { RPC::Server* serv = fFileSystem->Server(); Request request(serv, fFileSystem); RequestBuilder& req = request.Builder(); req.PutFH(fInfo.fHandle); req.LookUpUp(); req.GetFH(); if (fFileSystem->IsAttrSupported(FATTR4_FILEID)) { Attribute attr[] = { FATTR4_FILEID }; req.GetAttr(attr, sizeof(attr) / sizeof(Attribute)); } status_t result = request.Send(); if (result != B_OK) return result; ReplyInterpreter& reply = request.Reply(); if (HandleErrors(attempt, reply.NFS4Error(), serv)) continue; reply.PutFH(); result = reply.LookUpUp(); if (result != B_OK) return result; FileHandle fh; reply.GetFH(&fh); uint64 fileId; if (fFileSystem->IsAttrSupported(FATTR4_FILEID)) { AttrValue* values; uint32 count; result = reply.GetAttr(&values, &count); if (result != B_OK) return result; fileId = values[0].fData.fValue64; delete[] values; } else fileId = fFileSystem->AllocFileId(); return FillDirEntry(de, FileIdToInoT(fileId), "..", pos, size); } while (true); } static char* FileToAttrName(const char* path) { ASSERT(path != NULL); char* name = strdup(path); if (name == NULL) return NULL; char* current = strpbrk(name, "#$"); while (current != NULL) { switch (*current) { case '#': *current = '/'; break; case '$': *current = ':'; break; } current = strpbrk(name, "#$"); } return name; } status_t Inode::GetDirSnapshot(DirectoryCacheSnapshot** _snapshot, OpenDirCookie* cookie, uint64* _change, bool attribute) { ASSERT(_snapshot != NULL); DirectoryCacheSnapshot* snapshot = new DirectoryCacheSnapshot; if (snapshot == NULL) return B_NO_MEMORY; uint64 change = 0; uint64 dirCookie = 0; uint64 dirCookieVerf = 0; bool eof = false; while (!eof) { uint32 count; DirEntry* dirents; status_t result = ReadDirOnce(&dirents, &count, cookie, &eof, &change, &dirCookie, &dirCookieVerf, attribute); if (result != B_OK) { delete snapshot; return result; } uint32 i; for (i = 0; i < count; i++) { // FATTR4_FSID is mandatory void* data = dirents[i].fAttrs[0].fData.fPointer; FileSystemId* fsid = reinterpret_cast(data); if (*fsid != fFileSystem->FsId()) continue; if (strstr(dirents[i].fName, "-haiku-attrs") != NULL) continue; ino_t id; if (!attribute) { if (dirents[i].fAttrCount == 2) id = FileIdToInoT(dirents[i].fAttrs[1].fData.fValue64); else id = FileIdToInoT(fFileSystem->AllocFileId()); } else id = 0; const char* name = dirents[i].fName; if (attribute) name = FileToAttrName(name); if (name == NULL) { delete snapshot; delete[] dirents; return B_NO_MEMORY; } NameCacheEntry* entry = new NameCacheEntry(name, id); if (attribute) free(const_cast(name)); if (entry == NULL || entry->fName == NULL) { if (entry != NULL) delete entry; delete snapshot; delete[] dirents; return B_NO_MEMORY; } snapshot->fEntries.Add(entry); } delete[] dirents; } *_snapshot = snapshot; *_change = change; return B_OK; } status_t Inode::ReadDir(void* _buffer, uint32 size, uint32* _count, OpenDirCookie* cookie) { ASSERT(_buffer != NULL); ASSERT(_count != NULL); ASSERT(cookie != NULL); if (cookie->fEOF) { *_count = 0; return B_OK; } status_t result; DirectoryCache* cache = cookie->fAttrDir ? fAttrCache : fCache; if (cookie->fSnapshot == NULL) { cache->Lock(); result = cache->Revalidate(); if (result != B_OK) { cache->Unlock(); return result; } DirectoryCacheSnapshot* snapshot; result = cache->GetSnapshot(&snapshot); if (result != B_OK) { cache->Unlock(); return result; } cookie->fSnapshot = new DirectoryCacheSnapshot(*snapshot); cache->Unlock(); if (cookie->fSnapshot == NULL) return B_NO_MEMORY; } char* buffer = reinterpret_cast(_buffer); uint32 pos = 0; uint32 i = 0; bool overflow = false; if (cookie->fSpecial == 0 && i < *_count && !cookie->fAttrDir) { struct dirent* de = reinterpret_cast(buffer + pos); status_t result; result = FillDirEntry(de, fInfo.fFileId, ".", pos, size); if (result == B_BUFFER_OVERFLOW) overflow = true; else if (result == B_OK) { pos += de->d_reclen; i++; cookie->fSpecial++; } else return result; } if (cookie->fSpecial == 1 && i < *_count && !cookie->fAttrDir) { struct dirent* de = reinterpret_cast(buffer + pos); status_t result; result = ReadDirUp(de, pos, size); if (result == B_ENTRY_NOT_FOUND) { result = FillDirEntry(de, FileIdToInoT(fInfo.fFileId), "..", pos, size); } if (result == B_BUFFER_OVERFLOW) overflow = true; else if (result == B_OK) { pos += de->d_reclen; i++; cookie->fSpecial++; } else return result; } MutexLocker _(cookie->fSnapshot->fLock); for (; !overflow && i < *_count; i++) { struct dirent* de = reinterpret_cast(buffer + pos); NameCacheEntry* temp = cookie->fCurrent; if (cookie->fCurrent == NULL) cookie->fCurrent = cookie->fSnapshot->fEntries.Head(); else { cookie->fCurrent = cookie->fSnapshot->fEntries.GetNext(cookie->fCurrent); } if (cookie->fCurrent == NULL) { cookie->fEOF = true; break; } if (FillDirEntry(de, cookie->fCurrent->fNode, cookie->fCurrent->fName, pos, size) == B_BUFFER_OVERFLOW) { cookie->fCurrent = temp; overflow = true; break; } pos += de->d_reclen; } if (i == 0 && overflow) return B_BUFFER_OVERFLOW; *_count = i; return B_OK; }