File: | src/add-ons/kernel/file_systems/ext2/Inode.cpp |
Location: | line 636, column 16 |
Description: | Called C++ object pointer is null |
1 | /* | |||
2 | * Copyright 2011, Jérôme Duval, korli@users.berlios.de. | |||
3 | * Copyright 2008, Axel Dörfler, axeld@pinc-software.de. | |||
4 | * This file may be used under the terms of the MIT License. | |||
5 | */ | |||
6 | ||||
7 | ||||
8 | #include "Inode.h" | |||
9 | ||||
10 | #include <string.h> | |||
11 | #include <util/AutoLock.h> | |||
12 | #include <NodeMonitor.h> | |||
13 | ||||
14 | #include "CachedBlock.h" | |||
15 | #include "DataStream.h" | |||
16 | #include "DirectoryIterator.h" | |||
17 | #include "ExtentStream.h" | |||
18 | #include "HTree.h" | |||
19 | #include "Utility.h" | |||
20 | ||||
21 | ||||
22 | #undef ASSERT | |||
23 | //#define TRACE_EXT2 | |||
24 | #ifdef TRACE_EXT2 | |||
25 | # define TRACE(x...); dprintf("\33[34mext2:\33[0m " x) | |||
26 | # define ASSERT(x); { if (!(x)) kernel_debugger("ext2: assert failed: " #x "\n"); } | |||
27 | #else | |||
28 | # define TRACE(x...); ; | |||
29 | # define ASSERT(x); ; | |||
30 | #endif | |||
31 | #define ERROR(x...)dprintf("\33[34mext2:\33[0m " x...) dprintf("\33[34mext2:\33[0m " x) | |||
32 | ||||
33 | ||||
34 | Inode::Inode(Volume* volume, ino_t id) | |||
35 | : | |||
36 | fVolume(volume), | |||
37 | fID(id), | |||
38 | fCache(NULL__null), | |||
39 | fMap(NULL__null), | |||
40 | fCached(false), | |||
41 | fHasExtraAttributes(false) | |||
42 | { | |||
43 | rw_lock_init(&fLock, "ext2 inode"); | |||
44 | recursive_lock_init(&fSmallDataLock, "ext2 inode small data"); | |||
45 | ||||
46 | TRACE("Inode::Inode(): ext2_inode: %lu, disk inode: %" B_PRIu32; | |||
47 | "\n", sizeof(ext2_inode), fVolume->InodeSize());; | |||
48 | fNodeSize = sizeof(ext2_inode) > fVolume->InodeSize() | |||
49 | ? fVolume->InodeSize() : sizeof(ext2_inode); | |||
50 | ||||
51 | fInitStatus = UpdateNodeFromDisk(); | |||
52 | if (fInitStatus == B_OK((int)0)) { | |||
53 | fHasExtraAttributes = (fNodeSize == sizeof(ext2_inode) | |||
54 | && fNode.ExtraInodeSize() + EXT2_INODE_NORMAL_SIZE128 | |||
55 | == sizeof(ext2_inode)); | |||
56 | ||||
57 | if (IsDirectory() || (IsSymLink() && Size() < 60)) { | |||
58 | TRACE("Inode::Inode(): Not creating the file cache\n");; | |||
59 | fCached = false; | |||
60 | ||||
61 | fInitStatus = B_OK((int)0); | |||
62 | } else | |||
63 | fInitStatus = EnableFileCache(); | |||
64 | } else | |||
65 | TRACE("Inode: Failed initialization\n");; | |||
66 | } | |||
67 | ||||
68 | ||||
69 | Inode::Inode(Volume* volume) | |||
70 | : | |||
71 | fVolume(volume), | |||
72 | fID(0), | |||
73 | fCache(NULL__null), | |||
74 | fMap(NULL__null), | |||
75 | fCached(false), | |||
76 | fInitStatus(B_NO_INIT((-2147483647 - 1) + 13)) | |||
77 | { | |||
78 | rw_lock_init(&fLock, "ext2 inode"); | |||
79 | recursive_lock_init(&fSmallDataLock, "ext2 inode small data"); | |||
80 | ||||
81 | TRACE("Inode::Inode(): ext2_inode: %lu, disk inode: %" B_PRIu32 "\n",; | |||
82 | sizeof(ext2_inode), fVolume->InodeSize());; | |||
83 | fNodeSize = sizeof(ext2_inode) > fVolume->InodeSize() | |||
84 | ? fVolume->InodeSize() : sizeof(ext2_inode); | |||
85 | } | |||
86 | ||||
87 | ||||
88 | Inode::~Inode() | |||
89 | { | |||
90 | TRACE("Inode destructor\n");; | |||
91 | ||||
92 | if (fCached) { | |||
93 | TRACE("Deleting the file cache and file map\n");; | |||
94 | file_cache_delete(FileCache()); | |||
95 | file_map_delete(Map()); | |||
96 | } | |||
97 | ||||
98 | TRACE("Inode destructor: Done\n");; | |||
99 | } | |||
100 | ||||
101 | ||||
102 | status_t | |||
103 | Inode::InitCheck() | |||
104 | { | |||
105 | return fInitStatus; | |||
106 | } | |||
107 | ||||
108 | ||||
109 | void | |||
110 | Inode::WriteLockInTransaction(Transaction& transaction) | |||
111 | { | |||
112 | acquire_vnode(fVolume->FSVolume(), ID()); | |||
113 | ||||
114 | TRACE("Inode::WriteLockInTransaction(): Locking\n");; | |||
115 | rw_lock_write_lock(&fLock); | |||
116 | ||||
117 | transaction.AddListener(this); | |||
118 | } | |||
119 | ||||
120 | ||||
121 | status_t | |||
122 | Inode::WriteBack(Transaction& transaction) | |||
123 | { | |||
124 | off_t blockNum; | |||
125 | ||||
126 | status_t status = fVolume->GetInodeBlock(fID, blockNum); | |||
127 | if (status != B_OK((int)0)) | |||
128 | return status; | |||
129 | ||||
130 | if (Node().Size() > 0x7fffffffLL) { | |||
131 | status = fVolume->ActivateLargeFiles(transaction); | |||
132 | if (status != B_OK((int)0)) | |||
133 | return status; | |||
134 | } | |||
135 | ||||
136 | CachedBlock cached(fVolume); | |||
137 | uint8* inodeBlockData = cached.SetToWritable(transaction, blockNum); | |||
138 | if (inodeBlockData == NULL__null) | |||
139 | return B_IO_ERROR((-2147483647 - 1) + 1); | |||
140 | ||||
141 | TRACE("Inode::WriteBack(): Inode ID: %" B_PRIdINO ", inode block: %"; | |||
142 | B_PRIdOFF ", data: %p, index: %" B_PRIu32 ", inode size: %" B_PRIu32; | |||
143 | ", node size: %" B_PRIu32 ", this: %p, node: %p\n",; | |||
144 | fID, blockNum, inodeBlockData, fVolume->InodeBlockIndex(fID),; | |||
145 | fVolume->InodeSize(), fNodeSize, this, &fNode);; | |||
146 | memcpy(inodeBlockData + | |||
147 | fVolume->InodeBlockIndex(fID) * fVolume->InodeSize(), | |||
148 | (uint8*)&fNode, fNodeSize); | |||
149 | ||||
150 | TRACE("Inode::WriteBack() finished %" B_PRId32 "\n", Node().stream.direct[0]);; | |||
151 | ||||
152 | return B_OK((int)0); | |||
153 | } | |||
154 | ||||
155 | ||||
156 | status_t | |||
157 | Inode::UpdateNodeFromDisk() | |||
158 | { | |||
159 | off_t blockNum; | |||
160 | ||||
161 | status_t status = fVolume->GetInodeBlock(fID, blockNum); | |||
162 | if (status != B_OK((int)0)) | |||
163 | return status; | |||
164 | ||||
165 | TRACE("inode %" B_PRIdINO " at block %" B_PRIdOFF "\n", fID, blockNum);; | |||
166 | ||||
167 | CachedBlock cached(fVolume); | |||
168 | const uint8* inodeBlock = cached.SetTo(blockNum); | |||
169 | ||||
170 | if (inodeBlock == NULL__null) | |||
171 | return B_IO_ERROR((-2147483647 - 1) + 1); | |||
172 | ||||
173 | TRACE("Inode size: %" B_PRIu32 ", inode index: %" B_PRIu32 "\n",; | |||
174 | fVolume->InodeSize(), fVolume->InodeBlockIndex(fID));; | |||
175 | ext2_inode* inode = (ext2_inode*)(inodeBlock | |||
176 | + fVolume->InodeBlockIndex(fID) * fVolume->InodeSize()); | |||
177 | ||||
178 | TRACE("Attempting to copy inode data from %p to %p, ext2_inode "; | |||
179 | "size: %" B_PRIu32 "\n", inode, &fNode, fNodeSize);; | |||
180 | ||||
181 | memcpy(&fNode, inode, fNodeSize); | |||
182 | ||||
183 | uint32 numLinks = fNode.NumLinks(); | |||
184 | fUnlinked = numLinks == 0 || (IsDirectory() && numLinks == 1); | |||
185 | ||||
186 | return B_OK((int)0); | |||
187 | } | |||
188 | ||||
189 | ||||
190 | status_t | |||
191 | Inode::CheckPermissions(int accessMode) const | |||
192 | { | |||
193 | // you never have write access to a read-only volume | |||
194 | if ((accessMode & W_OK2) != 0 && fVolume->IsReadOnly()) | |||
195 | return B_READ_ONLY_DEVICE(((-2147483647 - 1) + 0x6000) + 8); | |||
196 | ||||
197 | // get node permissions | |||
198 | mode_t mode = Mode(); | |||
199 | int userPermissions = (mode & S_IRWXU00700) >> 6; | |||
200 | int groupPermissions = (mode & S_IRWXG00070) >> 3; | |||
201 | int otherPermissions = mode & S_IRWXO00007; | |||
202 | ||||
203 | // get the node permissions for this uid/gid | |||
204 | int permissions = 0; | |||
205 | uid_t uid = geteuid(); | |||
206 | gid_t gid = getegid(); | |||
207 | ||||
208 | if (uid == 0) { | |||
209 | // user is root | |||
210 | // root has always read/write permission, but at least one of the | |||
211 | // X bits must be set for execute permission | |||
212 | permissions = userPermissions | groupPermissions | otherPermissions | |||
213 | | R_OK4 | W_OK2; | |||
214 | } else if (uid == (uid_t)fNode.UserID()) { | |||
215 | // user is node owner | |||
216 | permissions = userPermissions; | |||
217 | } else if (gid == (gid_t)fNode.GroupID()) { | |||
218 | // user is in owning group | |||
219 | permissions = groupPermissions; | |||
220 | } else { | |||
221 | // user is one of the others | |||
222 | permissions = otherPermissions; | |||
223 | } | |||
224 | ||||
225 | return (accessMode & ~permissions) == 0 ? B_OK((int)0) : B_NOT_ALLOWED((-2147483647 - 1) + 15); | |||
226 | } | |||
227 | ||||
228 | ||||
229 | status_t | |||
230 | Inode::FindBlock(off_t offset, fsblock_t& block, uint32 *_count) | |||
231 | { | |||
232 | if (Flags() & EXT2_INODE_EXTENTS0x00080000) { | |||
233 | ExtentStream stream(fVolume, &fNode.extent_stream, Size()); | |||
234 | return stream.FindBlock(offset, block, _count); | |||
235 | } | |||
236 | DataStream stream(fVolume, &fNode.stream, Size()); | |||
237 | return stream.FindBlock(offset, block, _count); | |||
238 | } | |||
239 | ||||
240 | ||||
241 | status_t | |||
242 | Inode::ReadAt(off_t pos, uint8* buffer, size_t* _length) | |||
243 | { | |||
244 | size_t length = *_length; | |||
245 | ||||
246 | // set/check boundaries for pos/length | |||
247 | if (pos < 0) { | |||
248 | ERROR("inode %" B_PRIdINO ": ReadAt failed(pos %" B_PRIdOFFdprintf("\33[34mext2:\33[0m " "inode %" "ll" "d" ": ReadAt failed(pos %" "ll" "d" ", length %" "l" "u" ")\n", ID(), pos, length) | |||
249 | ", length %" B_PRIuSIZE ")\n", ID(), pos, length)dprintf("\33[34mext2:\33[0m " "inode %" "ll" "d" ": ReadAt failed(pos %" "ll" "d" ", length %" "l" "u" ")\n", ID(), pos, length); | |||
250 | return B_BAD_VALUE((-2147483647 - 1) + 5); | |||
251 | } | |||
252 | ||||
253 | if (pos >= Size() || length == 0) { | |||
254 | TRACE("inode %" B_PRIdINO ": ReadAt 0 (pos %" B_PRIdOFF ", length %"; | |||
255 | B_PRIuSIZE ")\n", ID(), pos, length);; | |||
256 | *_length = 0; | |||
257 | return B_NO_ERROR((int)0); | |||
258 | } | |||
259 | ||||
260 | return file_cache_read(FileCache(), NULL__null, pos, buffer, _length); | |||
261 | } | |||
262 | ||||
263 | ||||
264 | status_t | |||
265 | Inode::WriteAt(Transaction& transaction, off_t pos, const uint8* buffer, | |||
266 | size_t* _length) | |||
267 | { | |||
268 | TRACE("Inode::WriteAt(%" B_PRIdOFF ", %p, *(%p) = %" B_PRIuSIZE ")\n", pos,; | |||
269 | buffer, _length, *_length);; | |||
270 | ReadLocker readLocker(fLock); | |||
271 | ||||
272 | if (IsFileCacheDisabled()) | |||
273 | return B_BAD_VALUE((-2147483647 - 1) + 5); | |||
274 | ||||
275 | if (pos < 0) | |||
276 | return B_BAD_VALUE((-2147483647 - 1) + 5); | |||
277 | ||||
278 | readLocker.Unlock(); | |||
279 | ||||
280 | TRACE("Inode::WriteAt(): Starting transaction\n");; | |||
281 | transaction.Start(fVolume->GetJournal()); | |||
282 | ||||
283 | WriteLocker writeLocker(fLock); | |||
284 | ||||
285 | TRACE("Inode::WriteAt(): Updating modification time\n");; | |||
286 | struct timespec timespec; | |||
287 | _BigtimeToTimespec(real_time_clock_usecs(), ×pec); | |||
288 | SetModificationTime(×pec); | |||
289 | ||||
290 | // NOTE: Debugging info to find why sometimes resize doesn't happen | |||
291 | size_t length = *_length; | |||
292 | #ifdef TRACE_EXT2 | |||
293 | off_t oldEnd = pos + length; | |||
294 | TRACE("Inode::WriteAt(): Old calc for end? %x:%x\n",; | |||
295 | (int)(oldEnd >> 32), (int)(oldEnd & 0xFFFFFFFF));; | |||
296 | #endif | |||
297 | ||||
298 | off_t end = pos + (off_t)length; | |||
299 | off_t oldSize = Size(); | |||
300 | ||||
301 | TRACE("Inode::WriteAt(): Old size: %" B_PRIdOFF ":%" B_PRIdOFF; | |||
302 | ", new size: %" B_PRIdOFF ":%" B_PRIdOFF "\n",; | |||
303 | oldSize >> 32, oldSize & 0xFFFFFFFF,; | |||
304 | end >> 32, end & 0xFFFFFFFF);; | |||
305 | ||||
306 | if (end > oldSize) { | |||
307 | status_t status = Resize(transaction, end); | |||
308 | if (status != B_OK((int)0)) { | |||
309 | *_length = 0; | |||
310 | WriteLockInTransaction(transaction); | |||
311 | return status; | |||
312 | } | |||
313 | ||||
314 | status = WriteBack(transaction); | |||
315 | if (status != B_OK((int)0)) { | |||
316 | *_length = 0; | |||
317 | WriteLockInTransaction(transaction); | |||
318 | return status; | |||
319 | } | |||
320 | } | |||
321 | ||||
322 | writeLocker.Unlock(); | |||
323 | ||||
324 | if (oldSize < pos) | |||
325 | FillGapWithZeros(oldSize, pos); | |||
326 | ||||
327 | if (length == 0) { | |||
328 | // Probably just changed the file size with the pos parameter | |||
329 | return B_OK((int)0); | |||
330 | } | |||
331 | ||||
332 | TRACE("Inode::WriteAt(): Performing write: %p, %" B_PRIdOFF ", %p, %"; | |||
333 | B_PRIuSIZE "\n", FileCache(), pos, buffer, *_length);; | |||
334 | status_t status = file_cache_write(FileCache(), NULL__null, pos, buffer, | |||
335 | _length); | |||
336 | ||||
337 | WriteLockInTransaction(transaction); | |||
338 | ||||
339 | TRACE("Inode::WriteAt(): Done\n");; | |||
340 | ||||
341 | return status; | |||
342 | } | |||
343 | ||||
344 | ||||
345 | status_t | |||
346 | Inode::FillGapWithZeros(off_t start, off_t end) | |||
347 | { | |||
348 | TRACE("Inode::FileGapWithZeros(%" B_PRIdOFF " - %" B_PRIdOFF ")\n", start,; | |||
349 | end);; | |||
350 | ||||
351 | while (start < end) { | |||
352 | size_t size; | |||
353 | ||||
354 | if (end > start + 1024 * 1024 * 1024) | |||
355 | size = 1024 * 1024 * 1024; | |||
356 | else | |||
357 | size = end - start; | |||
358 | ||||
359 | TRACE("Inode::FillGapWithZeros(): Calling file_cache_write(%p, NULL, "; | |||
360 | "%" B_PRIdOFF ", NULL, &(%" B_PRIuSIZE ") = %p)\n", fCache, start,; | |||
361 | size, &size);; | |||
362 | status_t status = file_cache_write(fCache, NULL__null, start, NULL__null, | |||
363 | &size); | |||
364 | if (status != B_OK((int)0)) | |||
365 | return status; | |||
366 | ||||
367 | start += size; | |||
368 | } | |||
369 | ||||
370 | return B_OK((int)0); | |||
371 | } | |||
372 | ||||
373 | ||||
374 | status_t | |||
375 | Inode::Resize(Transaction& transaction, off_t size) | |||
376 | { | |||
377 | TRACE("Inode::Resize() ID:%" B_PRIdINO " size: %" B_PRIdOFF "\n", ID(),; | |||
378 | size);; | |||
379 | if (size < 0) | |||
380 | return B_BAD_VALUE((-2147483647 - 1) + 5); | |||
381 | ||||
382 | off_t oldSize = Size(); | |||
383 | ||||
384 | if (size == oldSize) | |||
385 | return B_OK((int)0); | |||
386 | ||||
387 | TRACE("Inode::Resize(): old size: %" B_PRIdOFF ", new size: %" B_PRIdOFF; | |||
388 | "\n", oldSize, size);; | |||
389 | ||||
390 | status_t status; | |||
391 | if (size > oldSize) { | |||
392 | status = _EnlargeDataStream(transaction, size); | |||
393 | if (status != B_OK((int)0)) { | |||
394 | // Restore original size | |||
395 | _ShrinkDataStream(transaction, oldSize); | |||
396 | } | |||
397 | } else | |||
398 | status = _ShrinkDataStream(transaction, size); | |||
399 | ||||
400 | TRACE("Inode::Resize(): Updating file map and cache\n");; | |||
401 | ||||
402 | if (status != B_OK((int)0)) | |||
403 | return status; | |||
404 | ||||
405 | file_cache_set_size(FileCache(), size); | |||
406 | file_map_set_size(Map(), size); | |||
407 | ||||
408 | TRACE("Inode::Resize(): Writing back inode changes. Size: %" B_PRIdOFF; | |||
409 | "\n", Size());; | |||
410 | ||||
411 | return WriteBack(transaction); | |||
412 | } | |||
413 | ||||
414 | ||||
415 | status_t | |||
416 | Inode::InitDirectory(Transaction& transaction, Inode* parent) | |||
417 | { | |||
418 | TRACE("Inode::InitDirectory()\n");; | |||
419 | uint32 blockSize = fVolume->BlockSize(); | |||
420 | ||||
421 | status_t status = Resize(transaction, blockSize); | |||
422 | if (status != B_OK((int)0)) | |||
423 | return status; | |||
424 | ||||
425 | fsblock_t blockNum; | |||
426 | if (Flags() & EXT2_INODE_EXTENTS0x00080000) { | |||
427 | ExtentStream stream(fVolume, &fNode.extent_stream, Size()); | |||
428 | status = stream.FindBlock(0, blockNum); | |||
429 | } else { | |||
430 | DataStream stream(fVolume, &fNode.stream, Size()); | |||
431 | status = stream.FindBlock(0, blockNum); | |||
432 | } | |||
433 | if (status != B_OK((int)0)) | |||
434 | return status; | |||
435 | ||||
436 | CachedBlock cached(fVolume); | |||
437 | uint8* block = cached.SetToWritable(transaction, blockNum, true); | |||
438 | ||||
439 | HTreeRoot* root = (HTreeRoot*)block; | |||
440 | root->dot.inode_id = fID; | |||
441 | root->dot.entry_length = 12; | |||
442 | root->dot.name_length = 1; | |||
443 | root->dot.file_type = EXT2_TYPE_DIRECTORY2; | |||
444 | root->dot_entry_name[0] = '.'; | |||
445 | ||||
446 | root->dotdot.inode_id = parent == NULL__null ? fID : parent->ID(); | |||
447 | root->dotdot.entry_length = blockSize - 12; | |||
448 | root->dotdot.name_length = 2; | |||
449 | root->dotdot.file_type = EXT2_TYPE_DIRECTORY2; | |||
450 | root->dotdot_entry_name[0] = '.'; | |||
451 | root->dotdot_entry_name[1] = '.'; | |||
452 | ||||
453 | parent->IncrementNumLinks(transaction); | |||
454 | ||||
455 | return parent->WriteBack(transaction); | |||
456 | } | |||
457 | ||||
458 | ||||
459 | status_t | |||
460 | Inode::Unlink(Transaction& transaction) | |||
461 | { | |||
462 | uint32 numLinks = fNode.NumLinks(); | |||
463 | TRACE("Inode::Unlink(): Current links: %" B_PRIu32 "\n", numLinks);; | |||
464 | ||||
465 | if (numLinks == 0) | |||
466 | return B_BAD_VALUE((-2147483647 - 1) + 5); | |||
467 | ||||
468 | if ((IsDirectory() && numLinks == 2) || (numLinks == 1)) { | |||
469 | fUnlinked = true; | |||
470 | ||||
471 | TRACE("Inode::Unlink(): Putting inode in orphan list\n");; | |||
472 | ino_t firstOrphanID; | |||
473 | status_t status = fVolume->SaveOrphan(transaction, fID, firstOrphanID); | |||
474 | if (status != B_OK((int)0)) | |||
475 | return status; | |||
476 | ||||
477 | if (firstOrphanID != 0) { | |||
478 | Vnode firstOrphan(fVolume, firstOrphanID); | |||
479 | Inode* nextOrphan; | |||
480 | ||||
481 | status = firstOrphan.Get(&nextOrphan); | |||
482 | if (status != B_OK((int)0)) | |||
483 | return status; | |||
484 | ||||
485 | fNode.SetNextOrphan(nextOrphan->ID()); | |||
486 | } else { | |||
487 | // Next orphan link is stored in deletion time | |||
488 | fNode.deletion_time = 0; | |||
489 | } | |||
490 | ||||
491 | fNode.num_links = 0; | |||
492 | ||||
493 | status = remove_vnode(fVolume->FSVolume(), fID); | |||
494 | if (status != B_OK((int)0)) | |||
495 | return status; | |||
496 | } else | |||
497 | fNode.SetNumLinks(--numLinks); | |||
498 | ||||
499 | return WriteBack(transaction); | |||
500 | } | |||
501 | ||||
502 | ||||
503 | /*static*/ status_t | |||
504 | Inode::Create(Transaction& transaction, Inode* parent, const char* name, | |||
505 | int32 mode, int openMode, uint8 type, bool* _created, ino_t* _id, | |||
506 | Inode** _inode, fs_vnode_ops* vnodeOps, uint32 publishFlags) | |||
507 | { | |||
508 | TRACE("Inode::Create()\n");; | |||
509 | Volume* volume = transaction.GetVolume(); | |||
510 | ||||
511 | DirectoryIterator* entries = NULL__null; | |||
512 | ObjectDeleter<DirectoryIterator> entriesDeleter; | |||
513 | ||||
514 | if (parent != NULL__null) { | |||
| ||||
515 | parent->WriteLockInTransaction(transaction); | |||
516 | ||||
517 | TRACE("Inode::Create(): Looking up entry destination\n");; | |||
518 | HTree htree(volume, parent); | |||
519 | ||||
520 | status_t status = htree.Lookup(name, &entries); | |||
521 | if (status == B_ENTRY_NOT_FOUND(((-2147483647 - 1) + 0x6000) + 3)) { | |||
522 | panic("We need to add the first node.\n"); | |||
523 | return B_ERROR(-1); | |||
524 | } | |||
525 | if (status != B_OK((int)0)) | |||
526 | return status; | |||
527 | entriesDeleter.SetTo(entries); | |||
528 | ||||
529 | TRACE("Inode::Create(): Looking up to see if file already exists\n");; | |||
530 | ino_t entryID; | |||
531 | ||||
532 | status = entries->FindEntry(name, &entryID); | |||
533 | if (status == B_OK((int)0)) { | |||
534 | // File already exists | |||
535 | TRACE("Inode::Create(): File already exists\n");; | |||
536 | if (S_ISDIR(mode)(((mode) & 00000170000) == 00000040000) || S_ISLNK(mode)(((mode) & 00000170000) == 00000120000) || (openMode & O_EXCL0x0100) != 0) | |||
537 | return B_FILE_EXISTS(((-2147483647 - 1) + 0x6000) + 2); | |||
538 | ||||
539 | Vnode vnode(volume, entryID); | |||
540 | Inode* inode; | |||
541 | ||||
542 | status = vnode.Get(&inode); | |||
543 | if (status != B_OK((int)0)) { | |||
544 | TRACE("Inode::Create() Failed to get the inode from the "; | |||
545 | "vnode\n");; | |||
546 | return B_ENTRY_NOT_FOUND(((-2147483647 - 1) + 0x6000) + 3); | |||
547 | } | |||
548 | ||||
549 | if (inode->IsDirectory() && (openMode & O_RWMASK0x0003) != O_RDONLY0x0000) | |||
550 | return B_IS_A_DIRECTORY(((-2147483647 - 1) + 0x6000) + 9); | |||
551 | if ((openMode & O_DIRECTORY0x00200000) != 0 && !inode->IsDirectory()) | |||
552 | return B_NOT_A_DIRECTORY(((-2147483647 - 1) + 0x6000) + 5); | |||
553 | ||||
554 | if (inode->CheckPermissions(open_mode_to_access(openMode) | |||
555 | | ((openMode & O_TRUNC0x0400) != 0 ? W_OK2 : 0)) != B_OK((int)0)) | |||
556 | return B_NOT_ALLOWED((-2147483647 - 1) + 15); | |||
557 | ||||
558 | if ((openMode & O_TRUNC0x0400) != 0) { | |||
559 | // Truncate requested | |||
560 | TRACE("Inode::Create(): Truncating file\n");; | |||
561 | inode->WriteLockInTransaction(transaction); | |||
562 | ||||
563 | status = inode->Resize(transaction, 0); | |||
564 | if (status != B_OK((int)0)) | |||
565 | return status; | |||
566 | } | |||
567 | ||||
568 | if (_created != NULL__null) | |||
569 | *_created = false; | |||
570 | if (_id != NULL__null) | |||
571 | *_id = inode->ID(); | |||
572 | if (_inode != NULL__null) | |||
573 | *_inode = inode; | |||
574 | ||||
575 | if (_id != NULL__null || _inode != NULL__null) | |||
576 | vnode.Keep(); | |||
577 | ||||
578 | TRACE("Inode::Create(): Done opening file\n");; | |||
579 | return B_OK((int)0); | |||
580 | /*} else if ((mode & S_ATTR_DIR) == 0) { | |||
581 | TRACE("Inode::Create(): (mode & S_ATTR_DIR) == 0\n"); | |||
582 | return B_BAD_VALUE;*/ | |||
583 | } else if ((openMode & O_DIRECTORY0x00200000) != 0) { | |||
584 | TRACE("Inode::Create(): (openMode & O_DIRECTORY) != 0\n");; | |||
585 | return B_ENTRY_NOT_FOUND(((-2147483647 - 1) + 0x6000) + 3); | |||
586 | } | |||
587 | ||||
588 | // Return to initial position | |||
589 | TRACE("Inode::Create(): Restarting iterator\n");; | |||
590 | entries->Restart(); | |||
591 | } | |||
592 | ||||
593 | status_t status; | |||
594 | if (parent != NULL__null) { | |||
595 | status = parent->CheckPermissions(W_OK2); | |||
596 | if (status != B_OK((int)0)) | |||
597 | return status; | |||
598 | } | |||
599 | ||||
600 | TRACE("Inode::Create(): Allocating inode\n");; | |||
601 | ino_t id; | |||
602 | status = volume->AllocateInode(transaction, parent, mode, id); | |||
603 | if (status != B_OK((int)0)) { | |||
604 | ERROR("Inode::Create(): AllocateInode() failed\n")dprintf("\33[34mext2:\33[0m " "Inode::Create(): AllocateInode() failed\n" ); | |||
605 | return status; | |||
606 | } | |||
607 | ||||
608 | if (entries != NULL__null) { | |||
609 | size_t nameLength = strlen(name); | |||
610 | status = entries->AddEntry(transaction, name, nameLength, id, type); | |||
611 | if (status != B_OK((int)0)) { | |||
612 | ERROR("Inode::Create(): AddEntry() failed\n")dprintf("\33[34mext2:\33[0m " "Inode::Create(): AddEntry() failed\n" ); | |||
613 | return status; | |||
614 | } | |||
615 | } | |||
616 | ||||
617 | TRACE("Inode::Create(): Creating inode\n");; | |||
618 | Inode* inode = new(std::nothrow) Inode(volume); | |||
619 | if (inode == NULL__null) | |||
620 | return B_NO_MEMORY((-2147483647 - 1) + 0); | |||
621 | ||||
622 | TRACE("Inode::Create(): Getting node structure\n");; | |||
623 | ext2_inode& node = inode->Node(); | |||
624 | TRACE("Inode::Create(): Initializing inode data\n");; | |||
625 | memset(&node, 0, sizeof(ext2_inode)); | |||
626 | node.SetMode(mode); | |||
627 | node.SetUserID(geteuid()); | |||
628 | node.SetGroupID(parent != NULL__null ? parent->Node().GroupID() : getegid()); | |||
629 | node.SetNumLinks(inode->IsDirectory() ? 2 : 1); | |||
630 | TRACE("Inode::Create(): Updating time\n");; | |||
631 | struct timespec timespec; | |||
632 | _BigtimeToTimespec(real_time_clock_usecs(), ×pec); | |||
633 | inode->SetAccessTime(×pec); | |||
634 | inode->SetCreationTime(×pec); | |||
635 | inode->SetModificationTime(×pec); | |||
636 | node.SetFlags(parent->Flags() & EXT2_INODE_INHERITED(0x00000001 | 0x00000002 | 0x00000004 | 0x00000008 | 0x00000010 | 0x00000020 | 0x00000040 | 0x00000080 | 0x00000400 | 0x00004000 | 0x00008000 | 0x00010000)); | |||
| ||||
637 | if (volume->HasExtentsFeature() | |||
638 | && (inode->IsDirectory() || inode->IsFile())) { | |||
639 | node.SetFlag(EXT2_INODE_EXTENTS0x00080000); | |||
640 | ExtentStream stream(volume, &node.extent_stream, 0); | |||
641 | stream.Init(); | |||
642 | ASSERT(stream.Check());; | |||
643 | } | |||
644 | ||||
645 | if (sizeof(ext2_inode) < volume->InodeSize()) | |||
646 | node.SetExtraInodeSize(sizeof(ext2_inode) - EXT2_INODE_NORMAL_SIZE128); | |||
647 | ||||
648 | TRACE("Inode::Create(): Updating ID\n");; | |||
649 | inode->fID = id; | |||
650 | ||||
651 | if (inode->IsDirectory()) { | |||
652 | TRACE("Inode::Create(): Initializing directory\n");; | |||
653 | status = inode->InitDirectory(transaction, parent); | |||
654 | if (status != B_OK((int)0)) { | |||
655 | ERROR("Inode::Create(): InitDirectory() failed\n")dprintf("\33[34mext2:\33[0m " "Inode::Create(): InitDirectory() failed\n" ); | |||
656 | delete inode; | |||
657 | return status; | |||
658 | } | |||
659 | } | |||
660 | ||||
661 | // TODO: Maybe it can be better | |||
662 | /*if (volume->HasExtendedAttributes()) { | |||
663 | TRACE("Inode::Create(): Initializing extended attributes\n"); | |||
664 | uint32 blockGroup = 0; | |||
665 | uint32 pos = 0; | |||
666 | uint32 allocated; | |||
667 | ||||
668 | status = volume->AllocateBlocks(transaction, 1, 1, blockGroup, pos, | |||
669 | allocated); | |||
670 | if (status != B_OK) | |||
671 | return status; | |||
672 | ||||
673 | // Clear the new block | |||
674 | uint32 blockNum = volume->FirstDataBlock() + pos + | |||
675 | volume->BlocksPerGroup() * blockGroup; | |||
676 | CachedBlock cached(volume); | |||
677 | cached.SetToWritable(transaction, blockNum, true); | |||
678 | ||||
679 | node.SetExtendedAttributesBlock(blockNum); | |||
680 | }*/ | |||
681 | ||||
682 | TRACE("Inode::Create(): Saving inode\n");; | |||
683 | status = inode->WriteBack(transaction); | |||
684 | if (status != B_OK((int)0)) { | |||
685 | delete inode; | |||
686 | return status; | |||
687 | } | |||
688 | ||||
689 | TRACE("Inode::Create(): Creating vnode\n");; | |||
690 | ||||
691 | Vnode vnode; | |||
692 | status = vnode.Publish(transaction, inode, vnodeOps, publishFlags); | |||
693 | if (status != B_OK((int)0)) | |||
694 | return status; | |||
695 | ||||
696 | if (!inode->IsSymLink()) { | |||
697 | // Vnode::Publish doesn't publish symlinks | |||
698 | if (!inode->IsDirectory()) { | |||
699 | status = inode->EnableFileCache(); | |||
700 | if (status != B_OK((int)0)) | |||
701 | return status; | |||
702 | } | |||
703 | ||||
704 | inode->WriteLockInTransaction(transaction); | |||
705 | } | |||
706 | ||||
707 | if (_created) | |||
708 | *_created = true; | |||
709 | if (_id != NULL__null) | |||
710 | *_id = id; | |||
711 | if (_inode != NULL__null) | |||
712 | *_inode = inode; | |||
713 | ||||
714 | if (_id != NULL__null || _inode != NULL__null) | |||
715 | vnode.Keep(); | |||
716 | ||||
717 | TRACE("Inode::Create(): Deleting entries iterator\n");; | |||
718 | DirectoryIterator* iterator = entriesDeleter.Detach(); | |||
719 | TRACE("Inode::Create(): Entries iterator: %p\n", entries);; | |||
720 | delete iterator; | |||
721 | TRACE("Inode::Create(): Done\n");; | |||
722 | ||||
723 | return B_OK((int)0); | |||
724 | } | |||
725 | ||||
726 | ||||
727 | status_t | |||
728 | Inode::EnableFileCache() | |||
729 | { | |||
730 | TRACE("Inode::EnableFileCache()\n");; | |||
731 | ||||
732 | if (fCached) | |||
733 | return B_OK((int)0); | |||
734 | if (fCache != NULL__null) { | |||
735 | fCached = true; | |||
736 | return B_OK((int)0); | |||
737 | } | |||
738 | ||||
739 | TRACE("Inode::EnableFileCache(): Creating file cache: %" B_PRIu32 ", %"; | |||
740 | B_PRIdINO ", %" B_PRIdOFF "\n", fVolume->ID(), ID(), Size());; | |||
741 | fCache = file_cache_create(fVolume->ID(), ID(), Size()); | |||
742 | fMap = file_map_create(fVolume->ID(), ID(), Size()); | |||
743 | ||||
744 | if (fCache == NULL__null) { | |||
745 | ERROR("Inode::EnableFileCache(): Failed to create file cache\n")dprintf("\33[34mext2:\33[0m " "Inode::EnableFileCache(): Failed to create file cache\n" ); | |||
746 | fCached = false; | |||
747 | return B_ERROR(-1); | |||
748 | } | |||
749 | ||||
750 | fCached = true; | |||
751 | TRACE("Inode::EnableFileCache(): Done\n");; | |||
752 | ||||
753 | return B_OK((int)0); | |||
754 | } | |||
755 | ||||
756 | ||||
757 | status_t | |||
758 | Inode::DisableFileCache() | |||
759 | { | |||
760 | TRACE("Inode::DisableFileCache()\n");; | |||
761 | ||||
762 | if (!fCached) | |||
763 | return B_OK((int)0); | |||
764 | ||||
765 | file_cache_delete(FileCache()); | |||
766 | file_map_delete(Map()); | |||
767 | ||||
768 | fCached = false; | |||
769 | ||||
770 | return B_OK((int)0); | |||
771 | } | |||
772 | ||||
773 | ||||
774 | status_t | |||
775 | Inode::Sync() | |||
776 | { | |||
777 | if (!IsFileCacheDisabled()) | |||
778 | return file_cache_sync(fCache); | |||
779 | ||||
780 | return B_OK((int)0); | |||
781 | } | |||
782 | ||||
783 | ||||
784 | void | |||
785 | Inode::TransactionDone(bool success) | |||
786 | { | |||
787 | if (!success) { | |||
788 | // Revert any changes to the inode | |||
789 | if (fInitStatus == B_OK((int)0) && UpdateNodeFromDisk() != B_OK((int)0)) | |||
790 | panic("Failed to reload inode from disk!\n"); | |||
791 | else if (fInitStatus == B_NO_INIT((-2147483647 - 1) + 13)) { | |||
792 | // TODO: Unpublish vnode? | |||
793 | panic("Failed to finish creating inode\n"); | |||
794 | } | |||
795 | } else { | |||
796 | if (fInitStatus == B_NO_INIT((-2147483647 - 1) + 13)) { | |||
797 | TRACE("Inode::TransactionDone(): Inode creation succeeded\n");; | |||
798 | fInitStatus = B_OK((int)0); | |||
799 | } | |||
800 | } | |||
801 | } | |||
802 | ||||
803 | ||||
804 | void | |||
805 | Inode::RemovedFromTransaction() | |||
806 | { | |||
807 | TRACE("Inode::RemovedFromTransaction(): Unlocking\n");; | |||
808 | rw_lock_write_unlock(&fLock); | |||
809 | ||||
810 | put_vnode(fVolume->FSVolume(), ID()); | |||
811 | } | |||
812 | ||||
813 | ||||
814 | status_t | |||
815 | Inode::_EnlargeDataStream(Transaction& transaction, off_t size) | |||
816 | { | |||
817 | if (size < 0) | |||
818 | return B_BAD_VALUE((-2147483647 - 1) + 5); | |||
819 | ||||
820 | TRACE("Inode::_EnlargeDataStream()\n");; | |||
821 | ||||
822 | uint32 blockSize = fVolume->BlockSize(); | |||
823 | off_t oldSize = Size(); | |||
824 | off_t maxSize = oldSize; | |||
825 | if (maxSize % blockSize != 0) | |||
826 | maxSize += blockSize - maxSize % blockSize; | |||
827 | ||||
828 | if (size <= maxSize) { | |||
829 | // No need to allocate more blocks | |||
830 | TRACE("Inode::_EnlargeDataStream(): No need to allocate more blocks\n");; | |||
831 | TRACE("Inode::_EnlargeDataStream(): Setting size to %" B_PRIdOFF "\n",; | |||
832 | size);; | |||
833 | fNode.SetSize(size); | |||
834 | return B_OK((int)0); | |||
835 | } | |||
836 | ||||
837 | off_t end = size == 0 ? 0 : (size - 1) / fVolume->BlockSize() + 1; | |||
838 | if (Flags() & EXT2_INODE_EXTENTS0x00080000) { | |||
839 | ExtentStream stream(fVolume, &fNode.extent_stream, Size()); | |||
840 | stream.Enlarge(transaction, end); | |||
841 | ASSERT(stream.Check());; | |||
842 | } else { | |||
843 | DataStream stream(fVolume, &fNode.stream, oldSize); | |||
844 | stream.Enlarge(transaction, end); | |||
845 | } | |||
846 | TRACE("Inode::_EnlargeDataStream(): Setting size to %" B_PRIdOFF "\n",; | |||
847 | size);; | |||
848 | fNode.SetSize(size); | |||
849 | TRACE("Inode::_EnlargeDataStream(): Setting allocated block count to %"; | |||
850 | B_PRIdOFF "\n", end);; | |||
851 | return _SetNumBlocks(_NumBlocks() + end * (fVolume->BlockSize() / 512)); | |||
852 | } | |||
853 | ||||
854 | ||||
855 | status_t | |||
856 | Inode::_ShrinkDataStream(Transaction& transaction, off_t size) | |||
857 | { | |||
858 | TRACE("Inode::_ShrinkDataStream()\n");; | |||
859 | ||||
860 | if (size < 0) | |||
861 | return B_BAD_VALUE((-2147483647 - 1) + 5); | |||
862 | ||||
863 | uint32 blockSize = fVolume->BlockSize(); | |||
864 | off_t oldSize = Size(); | |||
865 | off_t lastByte = oldSize == 0 ? 0 : oldSize - 1; | |||
866 | off_t minSize = (lastByte / blockSize + 1) * blockSize; | |||
867 | // Minimum size that doesn't require freeing blocks | |||
868 | ||||
869 | if (size > minSize) { | |||
870 | // No need to allocate more blocks | |||
871 | TRACE("Inode::_ShrinkDataStream(): No need to allocate more blocks\n");; | |||
872 | TRACE("Inode::_ShrinkDataStream(): Setting size to %" B_PRIdOFF "\n",; | |||
873 | size);; | |||
874 | fNode.SetSize(size); | |||
875 | return B_OK((int)0); | |||
876 | } | |||
877 | ||||
878 | off_t end = size == 0 ? 0 : (size - 1) / fVolume->BlockSize() + 1; | |||
879 | if (Flags() & EXT2_INODE_EXTENTS0x00080000) { | |||
880 | ExtentStream stream(fVolume, &fNode.extent_stream, Size()); | |||
881 | stream.Shrink(transaction, end); | |||
882 | ASSERT(stream.Check());; | |||
883 | } else { | |||
884 | DataStream stream(fVolume, &fNode.stream, oldSize); | |||
885 | stream.Shrink(transaction, end); | |||
886 | } | |||
887 | ||||
888 | fNode.SetSize(size); | |||
889 | return _SetNumBlocks(_NumBlocks() - end * (fVolume->BlockSize() / 512)); | |||
890 | } | |||
891 | ||||
892 | ||||
893 | uint64 | |||
894 | Inode::_NumBlocks() | |||
895 | { | |||
896 | if (fVolume->HugeFiles()) { | |||
897 | if (fNode.Flags() & EXT2_INODE_HUGE_FILE0x00040000) | |||
898 | return fNode.NumBlocks64() * (fVolume->BlockSize() / 512); | |||
899 | else | |||
900 | return fNode.NumBlocks64(); | |||
901 | } else | |||
902 | return fNode.NumBlocks(); | |||
903 | } | |||
904 | ||||
905 | ||||
906 | status_t | |||
907 | Inode::_SetNumBlocks(uint64 numBlocks) | |||
908 | { | |||
909 | if (numBlocks <= 0xffffffff) { | |||
910 | fNode.SetNumBlocks(numBlocks); | |||
911 | fNode.ClearFlag(EXT2_INODE_HUGE_FILE0x00040000); | |||
912 | return B_OK((int)0); | |||
913 | } | |||
914 | if (!fVolume->HugeFiles()) | |||
915 | return E2BIG(((-2147483647 - 1) + 0x7000) + 1); | |||
916 | ||||
917 | if (numBlocks > 0xffffffffffffULL) { | |||
918 | fNode.SetFlag(EXT2_INODE_HUGE_FILE0x00040000); | |||
919 | numBlocks /= (fVolume->BlockSize() / 512); | |||
920 | } else | |||
921 | fNode.ClearFlag(EXT2_INODE_HUGE_FILE0x00040000); | |||
922 | ||||
923 | fNode.SetNumBlocks64(numBlocks); | |||
924 | return B_OK((int)0); | |||
925 | } | |||
926 | ||||
927 | ||||
928 | void | |||
929 | Inode::IncrementNumLinks(Transaction& transaction) | |||
930 | { | |||
931 | fNode.SetNumLinks(fNode.NumLinks() + 1); | |||
932 | if (IsIndexed() && (fNode.NumLinks() >= EXT2_INODE_MAX_LINKS65000 | |||
933 | || fNode.NumLinks() == 2)) { | |||
934 | fNode.SetNumLinks(1); | |||
935 | fVolume->ActivateDirNLink(transaction); | |||
936 | } | |||
937 | } | |||
938 |