1 /* 2 * Copyright 2012 Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Paweł Dziepak, pdziepak@quarnos.org 7 */ 8 9 10 #include "Inode.h" 11 12 #include <dirent.h> 13 #include <string.h> 14 15 #include "IdMap.h" 16 #include "Request.h" 17 #include "RootInode.h" 18 19 20 status_t 21 Inode::CreateDir(const char* name, int mode, ino_t* id) 22 { 23 return CreateObject(name, NULL, mode, NF4DIR, id); 24 } 25 26 27 status_t 28 Inode::OpenDir(OpenDirCookie* cookie) 29 { 30 ASSERT(cookie != NULL); 31 32 if (fType != NF4DIR) 33 return B_NOT_A_DIRECTORY; 34 35 status_t result = Access(R_OK); 36 if (result != B_OK) 37 return result; 38 39 cookie->fSpecial = 0; 40 cookie->fSnapshot = NULL; 41 cookie->fCurrent = NULL; 42 cookie->fEOF = false; 43 cookie->fAttrDir = false; 44 45 return B_OK; 46 } 47 48 49 status_t 50 Inode::OpenAttrDir(OpenDirCookie* cookie) 51 { 52 ASSERT(cookie != NULL); 53 54 cookie->fSpecial = 0; 55 cookie->fSnapshot = NULL; 56 cookie->fCurrent = NULL; 57 cookie->fEOF = false; 58 cookie->fAttrDir = true; 59 60 return LoadAttrDirHandle(); 61 } 62 63 64 status_t 65 Inode::LoadAttrDirHandle() 66 { 67 if (fInfo.fAttrDir.fSize != 0) 68 return B_OK; 69 70 FileHandle handle; 71 status_t result; 72 73 if (fFileSystem->NamedAttrs()) { 74 result = NFS4Inode::OpenAttrDir(&handle); 75 if (result == B_OK) { 76 fInfo.fAttrDir = handle; 77 return B_OK; 78 } 79 80 if (result != B_UNSUPPORTED) 81 return result; 82 83 fFileSystem->SetNamedAttrs(false); 84 } 85 86 if (!fFileSystem->GetConfiguration().fEmulateNamedAttrs) 87 return B_UNSUPPORTED; 88 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"); 96 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); 105 } 106 107 free(attrDir); 108 109 if (result != B_OK) 110 return result; 111 112 fInfo.fAttrDir = handle; 113 return B_OK; 114 } 115 116 117 status_t 118 Inode::FillDirEntry(struct dirent* de, ino_t id, const char* name, uint32 pos, 119 uint32 size) 120 { 121 ASSERT(de != NULL); 122 ASSERT(name != NULL); 123 124 uint32 nameSize = strlen(name) + 1; 125 const uint32 entSize = offsetof(struct dirent, d_name); 126 127 if (pos + entSize + nameSize > size) 128 return B_BUFFER_OVERFLOW; 129 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; 135 136 strcpy(de->d_name, name); 137 138 return B_OK; 139 } 140 141 142 status_t 143 Inode::ReadDirUp(struct dirent* de, uint32 pos, uint32 size) 144 { 145 ASSERT(de != NULL); 146 147 uint32 attempt = 0; 148 do { 149 RPC::Server* serv = fFileSystem->Server(); 150 Request request(serv, fFileSystem); 151 RequestBuilder& req = request.Builder(); 152 153 req.PutFH(fInfo.fHandle); 154 req.LookUpUp(); 155 req.GetFH(); 156 157 if (fFileSystem->IsAttrSupported(FATTR4_FILEID)) { 158 Attribute attr[] = { FATTR4_FILEID }; 159 req.GetAttr(attr, sizeof(attr) / sizeof(Attribute)); 160 } 161 162 status_t result = request.Send(); 163 if (result != B_OK) 164 return result; 165 166 ReplyInterpreter& reply = request.Reply(); 167 168 if (HandleErrors(attempt, reply.NFS4Error(), serv)) 169 continue; 170 171 reply.PutFH(); 172 result = reply.LookUpUp(); 173 if (result != B_OK) 174 return result; 175 176 FileHandle fh; 177 reply.GetFH(&fh); 178 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; 186 187 fileId = values[0].fData.fValue64; 188 delete[] values; 189 } else 190 fileId = fFileSystem->AllocFileId(); 191 192 return FillDirEntry(de, FileIdToInoT(fileId), "..", pos, size); 193 } while (true); 194 } 195 196 197 static char* 198 FileToAttrName(const char* path) 199 { 200 ASSERT(path != NULL); 201 202 char* name = strdup(path); 203 if (name == NULL) 204 return NULL; 205 206 char* current = strpbrk(name, "#$"); 207 while (current != NULL) { 208 switch (*current) { 209 case '#': 210 *current = '/'; 211 break; 212 case '$': 213 *current = ':'; 214 break; 215 } 216 current = strpbrk(name, "#$"); 217 } 218 219 return name; 220 } 221 222 223 status_t 224 Inode::GetDirSnapshot(DirectoryCacheSnapshot** _snapshot, 225 OpenDirCookie* cookie, uint64* _change, bool attribute) 226 { 227 ASSERT(_snapshot != NULL); 228 229 DirectoryCacheSnapshot* snapshot = new DirectoryCacheSnapshot; 230 if (snapshot == NULL) 231 return B_NO_MEMORY; 232 233 uint64 change = 0; 234 uint64 dirCookie = 0; 235 uint64 dirCookieVerf = 0; 236 bool eof = false; 237 238 while (!eof) { 239 uint32 count; 240 DirEntry* dirents; 241 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; 247 } 248 249 uint32 i; 250 for (i = 0; i < count; i++) { 251 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; 257 258 if (strstr(dirents[i].fName, "-haiku-attrs") != NULL) 259 continue; 260 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; 269 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; 277 } 278 279 NameCacheEntry* entry = new NameCacheEntry(name, id); 280 if (attribute) 281 free(const_cast<char*>(name)); 282 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; 289 } 290 snapshot->fEntries.Add(entry); 291 } 292 293 delete[] dirents; 294 } 295 296 *_snapshot = snapshot; 297 *_change = change; 298 299 return B_OK; 300 } 301 302 303 status_t 304 Inode::ReadDir(void* _buffer, uint32 size, uint32* _count, 305 OpenDirCookie* cookie) 306 { 307 ASSERT(_buffer != NULL); 308 ASSERT(_count != NULL); 309 ASSERT(cookie != NULL); 310 311 if (cookie->fEOF) { 312 *_count = 0; 313 return B_OK; 314 } 315 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; 324 } 325 326 DirectoryCacheSnapshot* snapshot; 327 result = cache->GetSnapshot(&snapshot); 328 if (result != B_OK) { 329 cache->Unlock(); 330 return result; 331 } 332 333 cookie->fSnapshot = new DirectoryCacheSnapshot(*snapshot); 334 cache->Unlock(); 335 336 if (cookie->fSnapshot == NULL) 337 return B_NO_MEMORY; 338 } 339 340 char* buffer = reinterpret_cast<char*>(_buffer); 341 uint32 pos = 0; 342 uint32 i = 0; 343 bool overflow = false; 344 345 if (cookie->fSpecial == 0 && i < *_count && !cookie->fAttrDir) { 346 struct dirent* de = reinterpret_cast<dirent*>(buffer + pos); 347 348 status_t result; 349 result = FillDirEntry(de, fInfo.fFileId, ".", pos, size); 350 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; 359 } 360 361 if (cookie->fSpecial == 1 && i < *_count && !cookie->fAttrDir) { 362 struct dirent* de = reinterpret_cast<dirent*>(buffer + pos); 363 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); 369 } 370 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; 379 } 380 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; 385 386 if (cookie->fCurrent == NULL) 387 cookie->fCurrent = cookie->fSnapshot->fEntries.Head(); 388 else { 389 cookie->fCurrent 390 = cookie->fSnapshot->fEntries.GetNext(cookie->fCurrent); 391 } 392 393 if (cookie->fCurrent == NULL) { 394 cookie->fEOF = true; 395 break; 396 } 397 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; 403 } 404 405 pos += de->d_reclen; 406 } 407 408 if (i == 0 && overflow) 409 return B_BUFFER_OVERFLOW; 410 411 *_count = i; 412 413 return B_OK; 414 } 415 416