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->fFileSystem = fFileSystem; 40 cookie->fSpecial = 0; 41 cookie->fSnapshot = NULL; 42 cookie->fCurrent = NULL; 43 cookie->fEOF = false; 44 cookie->fAttrDir = false; 45 46 return B_OK; 47 } 48 49 50 status_t 51 Inode::OpenAttrDir(OpenDirCookie* cookie) 52 { 53 ASSERT(cookie != NULL); 54 55 cookie->fFileSystem = fFileSystem; 56 cookie->fSpecial = 0; 57 cookie->fSnapshot = NULL; 58 cookie->fCurrent = NULL; 59 cookie->fEOF = false; 60 cookie->fAttrDir = true; 61 62 return LoadAttrDirHandle(); 63 } 64 65 66 status_t 67 Inode::LoadAttrDirHandle() 68 { 69 if (fInfo.fAttrDir.fSize != 0) 70 return B_OK; 71 72 FileHandle handle; 73 status_t result; 74 75 if (fFileSystem->NamedAttrs()) { 76 result = NFS4Inode::OpenAttrDir(&handle); 77 if (result == B_OK) { 78 fInfo.fAttrDir = handle; 79 return B_OK; 80 } 81 82 if (result != B_UNSUPPORTED) 83 return result; 84 85 fFileSystem->SetNamedAttrs(false); 86 } 87 88 if (!fFileSystem->GetConfiguration().fEmulateNamedAttrs) 89 return B_UNSUPPORTED; 90 91 char* attrDir 92 = reinterpret_cast<char*>(malloc(strlen(Name()) + 32)); 93 if (attrDir == NULL) 94 return B_NO_MEMORY; 95 strcpy(attrDir, "."); 96 strcat(attrDir, Name()); 97 strcat(attrDir, "-haiku-attrs"); 98 99 result = NFS4Inode::LookUp(attrDir, NULL, NULL, &handle, true); 100 if (result == B_ENTRY_NOT_FOUND) { 101 ChangeInfo change; 102 struct stat st; 103 Stat(&st); 104 st.st_mode |= S_IXUSR | S_IXGRP | S_IXOTH; 105 result = NFS4Inode::CreateObject(attrDir, NULL, st.st_mode, NF4DIR, 106 &change, NULL, &handle, true); 107 } 108 109 free(attrDir); 110 111 if (result != B_OK) 112 return result; 113 114 fInfo.fAttrDir = handle; 115 return B_OK; 116 } 117 118 119 status_t 120 Inode::FillDirEntry(struct dirent* de, ino_t id, const char* name, uint32 pos, 121 uint32 size) 122 { 123 ASSERT(de != NULL); 124 ASSERT(name != NULL); 125 126 uint32 nameSize = strlen(name) + 1; 127 const uint32 entSize = sizeof(struct dirent); 128 129 if (pos + entSize + nameSize > size) 130 return B_BUFFER_OVERFLOW; 131 132 de->d_dev = fFileSystem->DevId(); 133 de->d_ino = id; 134 de->d_reclen = entSize + nameSize; 135 if (de->d_reclen % 8 != 0) 136 de->d_reclen += 8 - de->d_reclen % 8; 137 138 strcpy(de->d_name, name); 139 140 return B_OK; 141 } 142 143 144 status_t 145 Inode::ReadDirUp(struct dirent* de, uint32 pos, uint32 size) 146 { 147 ASSERT(de != NULL); 148 149 uint32 attempt = 0; 150 do { 151 RPC::Server* serv = fFileSystem->Server(); 152 Request request(serv, fFileSystem); 153 RequestBuilder& req = request.Builder(); 154 155 req.PutFH(fInfo.fHandle); 156 req.LookUpUp(); 157 req.GetFH(); 158 159 if (fFileSystem->IsAttrSupported(FATTR4_FILEID)) { 160 Attribute attr[] = { FATTR4_FILEID }; 161 req.GetAttr(attr, sizeof(attr) / sizeof(Attribute)); 162 } 163 164 status_t result = request.Send(); 165 if (result != B_OK) 166 return result; 167 168 ReplyInterpreter& reply = request.Reply(); 169 170 if (HandleErrors(attempt, reply.NFS4Error(), serv)) 171 continue; 172 173 reply.PutFH(); 174 result = reply.LookUpUp(); 175 if (result != B_OK) 176 return result; 177 178 FileHandle fh; 179 reply.GetFH(&fh); 180 181 uint64 fileId; 182 if (fFileSystem->IsAttrSupported(FATTR4_FILEID)) { 183 AttrValue* values; 184 uint32 count; 185 result = reply.GetAttr(&values, &count); 186 if (result != B_OK) 187 return result; 188 189 fileId = values[0].fData.fValue64; 190 delete[] values; 191 } else 192 fileId = fFileSystem->AllocFileId(); 193 194 return FillDirEntry(de, FileIdToInoT(fileId), "..", pos, size); 195 } while (true); 196 } 197 198 199 static char* 200 FileToAttrName(const char* path) 201 { 202 ASSERT(path != NULL); 203 204 char* name = strdup(path); 205 if (name == NULL) 206 return NULL; 207 208 char* current = strpbrk(name, "#$"); 209 while (current != NULL) { 210 switch (*current) { 211 case '#': 212 *current = '/'; 213 break; 214 case '$': 215 *current = ':'; 216 break; 217 } 218 current = strpbrk(name, "#$"); 219 } 220 221 return name; 222 } 223 224 225 status_t 226 Inode::GetDirSnapshot(DirectoryCacheSnapshot** _snapshot, 227 OpenDirCookie* cookie, uint64* _change, bool attribute) 228 { 229 ASSERT(_snapshot != NULL); 230 231 DirectoryCacheSnapshot* snapshot = new DirectoryCacheSnapshot; 232 if (snapshot == NULL) 233 return B_NO_MEMORY; 234 235 uint64 change = 0; 236 uint64 dirCookie = 0; 237 uint64 dirCookieVerf = 0; 238 bool eof = false; 239 240 while (!eof) { 241 uint32 count; 242 DirEntry* dirents; 243 244 status_t result = ReadDirOnce(&dirents, &count, cookie, &eof, &change, 245 &dirCookie, &dirCookieVerf, attribute); 246 if (result != B_OK) { 247 delete snapshot; 248 return result; 249 } 250 251 uint32 i; 252 for (i = 0; i < count; i++) { 253 254 // FATTR4_FSID is mandatory 255 void* data = dirents[i].fAttrs[0].fData.fPointer; 256 FileSystemId* fsid = reinterpret_cast<FileSystemId*>(data); 257 if (*fsid != fFileSystem->FsId()) 258 continue; 259 260 if (strstr(dirents[i].fName, "-haiku-attrs") != NULL) 261 continue; 262 263 ino_t id; 264 if (!attribute) { 265 if (dirents[i].fAttrCount == 2) 266 id = FileIdToInoT(dirents[i].fAttrs[1].fData.fValue64); 267 else 268 id = FileIdToInoT(fFileSystem->AllocFileId()); 269 } else 270 id = 0; 271 272 const char* name = dirents[i].fName; 273 if (attribute) 274 name = FileToAttrName(name); 275 if (name == NULL) { 276 delete snapshot; 277 delete[] dirents; 278 return B_NO_MEMORY; 279 } 280 281 NameCacheEntry* entry = new NameCacheEntry(name, id); 282 if (attribute) 283 free(const_cast<char*>(name)); 284 285 if (entry == NULL || entry->fName == NULL) { 286 if (entry != NULL) 287 delete entry; 288 delete snapshot; 289 delete[] dirents; 290 return B_NO_MEMORY; 291 } 292 snapshot->fEntries.Add(entry); 293 } 294 295 delete[] dirents; 296 } 297 298 *_snapshot = snapshot; 299 *_change = change; 300 301 return B_OK; 302 } 303 304 305 status_t 306 Inode::ReadDir(void* _buffer, uint32 size, uint32* _count, 307 OpenDirCookie* cookie) 308 { 309 ASSERT(_buffer != NULL); 310 ASSERT(_count != NULL); 311 ASSERT(cookie != NULL); 312 313 if (cookie->fEOF) { 314 *_count = 0; 315 return B_OK; 316 } 317 318 status_t result; 319 DirectoryCache* cache = cookie->fAttrDir ? fAttrCache : fCache; 320 if (cookie->fSnapshot == NULL) { 321 cache->Lock(); 322 result = cache->Revalidate(); 323 if (result != B_OK) { 324 cache->Unlock(); 325 return result; 326 } 327 328 DirectoryCacheSnapshot* snapshot; 329 result = cache->GetSnapshot(&snapshot); 330 if (result != B_OK) { 331 cache->Unlock(); 332 return result; 333 } 334 335 cookie->fSnapshot = new DirectoryCacheSnapshot(*snapshot); 336 cache->Unlock(); 337 338 if (cookie->fSnapshot == NULL) 339 return B_NO_MEMORY; 340 } 341 342 char* buffer = reinterpret_cast<char*>(_buffer); 343 uint32 pos = 0; 344 uint32 i = 0; 345 bool overflow = false; 346 347 if (cookie->fSpecial == 0 && i < *_count && !cookie->fAttrDir) { 348 struct dirent* de = reinterpret_cast<dirent*>(buffer + pos); 349 350 status_t result; 351 result = FillDirEntry(de, fInfo.fFileId, ".", pos, size); 352 353 if (result == B_BUFFER_OVERFLOW) 354 overflow = true; 355 else if (result == B_OK) { 356 pos += de->d_reclen; 357 i++; 358 cookie->fSpecial++; 359 } else 360 return result; 361 } 362 363 if (cookie->fSpecial == 1 && i < *_count && !cookie->fAttrDir) { 364 struct dirent* de = reinterpret_cast<dirent*>(buffer + pos); 365 366 status_t result; 367 result = ReadDirUp(de, pos, size); 368 if (result == B_ENTRY_NOT_FOUND) { 369 result = FillDirEntry(de, FileIdToInoT(fInfo.fFileId), "..", pos, 370 size); 371 } 372 373 if (result == B_BUFFER_OVERFLOW) 374 overflow = true; 375 else if (result == B_OK) { 376 pos += de->d_reclen; 377 i++; 378 cookie->fSpecial++; 379 } else 380 return result; 381 } 382 383 MutexLocker _(cookie->fSnapshot->fLock); 384 for (; !overflow && i < *_count; i++) { 385 struct dirent* de = reinterpret_cast<dirent*>(buffer + pos); 386 NameCacheEntry* temp = cookie->fCurrent; 387 388 if (cookie->fCurrent == NULL) 389 cookie->fCurrent = cookie->fSnapshot->fEntries.Head(); 390 else { 391 cookie->fCurrent 392 = cookie->fSnapshot->fEntries.GetNext(cookie->fCurrent); 393 } 394 395 if (cookie->fCurrent == NULL) { 396 cookie->fEOF = true; 397 break; 398 } 399 400 if (FillDirEntry(de, cookie->fCurrent->fNode, cookie->fCurrent->fName, 401 pos, size) == B_BUFFER_OVERFLOW) { 402 cookie->fCurrent = temp; 403 overflow = true; 404 break; 405 } 406 407 pos += de->d_reclen; 408 } 409 410 if (i == 0 && overflow) 411 return B_BUFFER_OVERFLOW; 412 413 *_count = i; 414 415 return B_OK; 416 } 417 418