1 /*
2 * Copyright 2010, 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 <algorithm>
9 #include <dirent.h>
10 #include <sys/ioctl.h>
11 #include <util/kernel_cpp.h>
12 #include <string.h>
13
14 #include <AutoDeleter.h>
15 #include <fs_cache.h>
16 #include <fs_info.h>
17 #include <io_requests.h>
18 #include <NodeMonitor.h>
19 #include <util/AutoLock.h>
20
21 #include "Attribute.h"
22 #include "CachedBlock.h"
23 #include "DirectoryIterator.h"
24 #include "ext2.h"
25 #include "HTree.h"
26 #include "Inode.h"
27 #include "Journal.h"
28 #include "Utility.h"
29
30
31 //#define TRACE_EXT2
32 #ifdef TRACE_EXT2
33 # define TRACE(x...) dprintf("\33[34mext2:\33[0m " x)
34 #else
35 # define TRACE(x...) ;
36 #endif
37 #define ERROR(x...) dprintf("\33[34mext2:\33[0m " x)
38
39
40 #define EXT2_IO_SIZE 65536
41
42
43 struct identify_cookie {
44 ext2_super_block super_block;
45 };
46
47
48 // #pragma mark - Scanning
49
50
51 static float
ext2_identify_partition(int fd,partition_data * partition,void ** _cookie)52 ext2_identify_partition(int fd, partition_data *partition, void **_cookie)
53 {
54 STATIC_ASSERT(sizeof(struct ext2_super_block) == 1024);
55 STATIC_ASSERT(sizeof(struct ext2_block_group) == 64);
56
57 ext2_super_block superBlock;
58 status_t status = Volume::Identify(fd, &superBlock);
59 if (status != B_OK)
60 return -1;
61
62 identify_cookie *cookie = new identify_cookie;
63 memcpy(&cookie->super_block, &superBlock, sizeof(ext2_super_block));
64
65 *_cookie = cookie;
66 return 0.8f;
67 }
68
69
70 static status_t
ext2_scan_partition(int fd,partition_data * partition,void * _cookie)71 ext2_scan_partition(int fd, partition_data *partition, void *_cookie)
72 {
73 identify_cookie *cookie = (identify_cookie *)_cookie;
74
75 partition->status = B_PARTITION_VALID;
76 partition->flags |= B_PARTITION_FILE_SYSTEM;
77 partition->content_size = cookie->super_block.NumBlocks(
78 (cookie->super_block.CompatibleFeatures()
79 & EXT2_INCOMPATIBLE_FEATURE_64BIT) != 0)
80 << cookie->super_block.BlockShift();
81 partition->block_size = 1UL << cookie->super_block.BlockShift();
82 partition->content_name = strdup(cookie->super_block.name);
83 if (partition->content_name == NULL)
84 return B_NO_MEMORY;
85
86 return B_OK;
87 }
88
89
90 static void
ext2_free_identify_partition_cookie(partition_data * partition,void * _cookie)91 ext2_free_identify_partition_cookie(partition_data* partition, void* _cookie)
92 {
93 delete (identify_cookie*)_cookie;
94 }
95
96
97 // #pragma mark -
98
99
100 static status_t
ext2_mount(fs_volume * _volume,const char * device,uint32 flags,const char * args,ino_t * _rootID)101 ext2_mount(fs_volume* _volume, const char* device, uint32 flags,
102 const char* args, ino_t* _rootID)
103 {
104 Volume* volume = new(std::nothrow) Volume(_volume);
105 if (volume == NULL)
106 return B_NO_MEMORY;
107
108 // TODO: this is a bit hacky: we can't use publish_vnode() to publish
109 // the root node, or else its file cache cannot be created (we could
110 // create it later, though). Therefore we're using get_vnode() in Mount(),
111 // but that requires us to export our volume data before calling it.
112 _volume->private_volume = volume;
113 _volume->ops = &gExt2VolumeOps;
114
115 status_t status = volume->Mount(device, flags);
116 if (status != B_OK) {
117 ERROR("Failed mounting the volume. Error: %s\n", strerror(status));
118 delete volume;
119 return status;
120 }
121
122 *_rootID = volume->RootNode()->ID();
123 return B_OK;
124 }
125
126
127 static status_t
ext2_unmount(fs_volume * _volume)128 ext2_unmount(fs_volume *_volume)
129 {
130 Volume* volume = (Volume *)_volume->private_volume;
131
132 status_t status = volume->Unmount();
133 delete volume;
134
135 return status;
136 }
137
138
139 static status_t
ext2_read_fs_info(fs_volume * _volume,struct fs_info * info)140 ext2_read_fs_info(fs_volume* _volume, struct fs_info* info)
141 {
142 Volume* volume = (Volume*)_volume->private_volume;
143
144 // File system flags
145 info->flags = B_FS_IS_PERSISTENT | B_FS_HAS_ATTR
146 | (volume->IsReadOnly() ? B_FS_IS_READONLY : 0);
147 info->io_size = EXT2_IO_SIZE;
148 info->block_size = volume->BlockSize();
149 info->total_blocks = volume->NumBlocks();
150 info->free_blocks = volume->NumFreeBlocks();
151
152 // Volume name
153 strlcpy(info->volume_name, volume->Name(), sizeof(info->volume_name));
154
155 // File system name
156 if (volume->HasExtentsFeature())
157 strlcpy(info->fsh_name, "ext4", sizeof(info->fsh_name));
158 else if (volume->HasJournalFeature())
159 strlcpy(info->fsh_name, "ext3", sizeof(info->fsh_name));
160 else
161 strlcpy(info->fsh_name, "ext2", sizeof(info->fsh_name));
162
163 return B_OK;
164 }
165
166
167 static status_t
ext2_write_fs_info(fs_volume * _volume,const struct fs_info * info,uint32 mask)168 ext2_write_fs_info(fs_volume* _volume, const struct fs_info* info, uint32 mask)
169 {
170 Volume* volume = (Volume*)_volume->private_volume;
171
172 if (volume->IsReadOnly())
173 return B_READ_ONLY_DEVICE;
174
175 MutexLocker locker(volume->Lock());
176
177 status_t status = B_BAD_VALUE;
178
179 if (mask & FS_WRITE_FSINFO_NAME) {
180 Transaction transaction(volume->GetJournal());
181 volume->SetName(info->volume_name);
182 status = volume->WriteSuperBlock(transaction);
183 transaction.Done();
184 }
185 return status;
186 }
187
188
189 static status_t
ext2_sync(fs_volume * _volume)190 ext2_sync(fs_volume* _volume)
191 {
192 Volume* volume = (Volume*)_volume->private_volume;
193 return volume->Sync();
194 }
195
196
197 // #pragma mark -
198
199
200 static status_t
ext2_get_vnode(fs_volume * _volume,ino_t id,fs_vnode * _node,int * _type,uint32 * _flags,bool reenter)201 ext2_get_vnode(fs_volume* _volume, ino_t id, fs_vnode* _node, int* _type,
202 uint32* _flags, bool reenter)
203 {
204 Volume* volume = (Volume*)_volume->private_volume;
205
206 if (id < 2 || id > volume->NumInodes()) {
207 ERROR("invalid inode id %" B_PRIdINO " requested!\n", id);
208 return B_BAD_VALUE;
209 }
210
211 Inode* inode = new(std::nothrow) Inode(volume, id);
212 if (inode == NULL)
213 return B_NO_MEMORY;
214
215 status_t status = inode->InitCheck();
216 if (status != B_OK)
217 delete inode;
218
219 if (status == B_OK) {
220 _node->private_node = inode;
221 _node->ops = &gExt2VnodeOps;
222 *_type = inode->Mode();
223 *_flags = 0;
224 } else
225 ERROR("get_vnode: InitCheck() failed. Error: %s\n", strerror(status));
226
227 return status;
228 }
229
230
231 static status_t
ext2_put_vnode(fs_volume * _volume,fs_vnode * _node,bool reenter)232 ext2_put_vnode(fs_volume* _volume, fs_vnode* _node, bool reenter)
233 {
234 delete (Inode*)_node->private_node;
235 return B_OK;
236 }
237
238
239 static status_t
ext2_remove_vnode(fs_volume * _volume,fs_vnode * _node,bool reenter)240 ext2_remove_vnode(fs_volume* _volume, fs_vnode* _node, bool reenter)
241 {
242 TRACE("ext2_remove_vnode()\n");
243 Volume* volume = (Volume*)_volume->private_volume;
244 Inode* inode = (Inode*)_node->private_node;
245 ObjectDeleter<Inode> inodeDeleter(inode);
246
247 if (!inode->IsDeleted())
248 return B_OK;
249
250 TRACE("ext2_remove_vnode(): Starting transaction\n");
251 Transaction transaction(volume->GetJournal());
252
253 if (!inode->IsSymLink() || inode->Size() >= EXT2_SHORT_SYMLINK_LENGTH) {
254 TRACE("ext2_remove_vnode(): Truncating\n");
255 status_t status = inode->Resize(transaction, 0);
256 if (status != B_OK)
257 return status;
258 }
259
260 TRACE("ext2_remove_vnode(): Removing from orphan list\n");
261 status_t status = volume->RemoveOrphan(transaction, inode->ID());
262 if (status != B_OK)
263 return status;
264
265 TRACE("ext2_remove_vnode(): Setting deletion time\n");
266 inode->Node().SetDeletionTime(real_time_clock());
267
268 status = inode->WriteBack(transaction);
269 if (status != B_OK)
270 return status;
271
272 TRACE("ext2_remove_vnode(): Freeing inode\n");
273 status = volume->FreeInode(transaction, inode->ID(), inode->IsDirectory());
274
275 // TODO: When Transaction::Done() fails, do we have to re-add the vnode?
276 if (status == B_OK)
277 status = transaction.Done();
278
279 return status;
280 }
281
282
283 static bool
ext2_can_page(fs_volume * _volume,fs_vnode * _node,void * _cookie)284 ext2_can_page(fs_volume* _volume, fs_vnode* _node, void* _cookie)
285 {
286 return true;
287 }
288
289
290 static status_t
ext2_read_pages(fs_volume * _volume,fs_vnode * _node,void * _cookie,off_t pos,const iovec * vecs,size_t count,size_t * _numBytes)291 ext2_read_pages(fs_volume* _volume, fs_vnode* _node, void* _cookie,
292 off_t pos, const iovec* vecs, size_t count, size_t* _numBytes)
293 {
294 Volume* volume = (Volume*)_volume->private_volume;
295 Inode* inode = (Inode*)_node->private_node;
296
297 if (inode->FileCache() == NULL)
298 return B_BAD_VALUE;
299
300 rw_lock_read_lock(inode->Lock());
301
302 uint32 vecIndex = 0;
303 size_t vecOffset = 0;
304 size_t bytesLeft = *_numBytes;
305 status_t status;
306
307 while (true) {
308 file_io_vec fileVecs[8];
309 size_t fileVecCount = 8;
310
311 status = file_map_translate(inode->Map(), pos, bytesLeft, fileVecs,
312 &fileVecCount, 0);
313 if (status != B_OK && status != B_BUFFER_OVERFLOW)
314 break;
315
316 bool bufferOverflow = status == B_BUFFER_OVERFLOW;
317
318 size_t bytes = bytesLeft;
319 status = read_file_io_vec_pages(volume->Device(), fileVecs,
320 fileVecCount, vecs, count, &vecIndex, &vecOffset, &bytes);
321 if (status != B_OK || !bufferOverflow)
322 break;
323
324 pos += bytes;
325 bytesLeft -= bytes;
326 }
327
328 rw_lock_read_unlock(inode->Lock());
329
330 return status;
331 }
332
333
334 static status_t
ext2_write_pages(fs_volume * _volume,fs_vnode * _node,void * _cookie,off_t pos,const iovec * vecs,size_t count,size_t * _numBytes)335 ext2_write_pages(fs_volume* _volume, fs_vnode* _node, void* _cookie,
336 off_t pos, const iovec* vecs, size_t count, size_t* _numBytes)
337 {
338 Volume* volume = (Volume*)_volume->private_volume;
339 Inode* inode = (Inode*)_node->private_node;
340
341 if (volume->IsReadOnly())
342 return B_READ_ONLY_DEVICE;
343
344 if (inode->FileCache() == NULL)
345 return B_BAD_VALUE;
346
347 rw_lock_read_lock(inode->Lock());
348
349 uint32 vecIndex = 0;
350 size_t vecOffset = 0;
351 size_t bytesLeft = *_numBytes;
352 status_t status;
353
354 while (true) {
355 file_io_vec fileVecs[8];
356 size_t fileVecCount = 8;
357
358 status = file_map_translate(inode->Map(), pos, bytesLeft, fileVecs,
359 &fileVecCount, 0);
360 if (status != B_OK && status != B_BUFFER_OVERFLOW)
361 break;
362
363 bool bufferOverflow = status == B_BUFFER_OVERFLOW;
364
365 size_t bytes = bytesLeft;
366 status = write_file_io_vec_pages(volume->Device(), fileVecs,
367 fileVecCount, vecs, count, &vecIndex, &vecOffset, &bytes);
368 if (status != B_OK || !bufferOverflow)
369 break;
370
371 pos += bytes;
372 bytesLeft -= bytes;
373 }
374
375 rw_lock_read_unlock(inode->Lock());
376
377 return status;
378 }
379
380
381 static status_t
ext2_get_file_map(fs_volume * _volume,fs_vnode * _node,off_t offset,size_t size,struct file_io_vec * vecs,size_t * _count)382 ext2_get_file_map(fs_volume* _volume, fs_vnode* _node, off_t offset,
383 size_t size, struct file_io_vec* vecs, size_t* _count)
384 {
385 TRACE("ext2_get_file_map()\n");
386 Volume* volume = (Volume*)_volume->private_volume;
387 Inode* inode = (Inode*)_node->private_node;
388 size_t index = 0, max = *_count;
389
390 while (true) {
391 fsblock_t block;
392 uint32 count = 1;
393 status_t status = inode->FindBlock(offset, block, &count);
394 if (status != B_OK)
395 return status;
396
397 if (block > volume->NumBlocks()) {
398 panic("ext2_get_file_map() found block %" B_PRIu64 " for offset %"
399 B_PRIdOFF "\n", block, offset);
400 }
401
402 off_t blockOffset = block << volume->BlockShift();
403 uint32 blockLength = volume->BlockSize() * count;
404
405 if (index > 0 && (vecs[index - 1].offset
406 == blockOffset - vecs[index - 1].length
407 || (vecs[index - 1].offset == -1 && block == 0))) {
408 vecs[index - 1].length += blockLength;
409 } else {
410 if (index >= max) {
411 // we're out of file_io_vecs; let's bail out
412 *_count = index;
413 return B_BUFFER_OVERFLOW;
414 }
415
416 // 'block' is 0 for sparse blocks
417 if (block != 0)
418 vecs[index].offset = blockOffset;
419 else
420 vecs[index].offset = -1;
421
422 vecs[index].length = blockLength;
423 index++;
424 }
425
426 offset += blockLength;
427
428 if (offset >= inode->Size() || size <= blockLength) {
429 // We're done!
430 *_count = index;
431 TRACE("ext2_get_file_map for inode %" B_PRIdINO "\n", inode->ID());
432 return B_OK;
433 }
434
435 size -= blockLength;
436 }
437
438 // can never get here
439 return B_ERROR;
440 }
441
442
443 // #pragma mark -
444
445
446 static status_t
ext2_lookup(fs_volume * _volume,fs_vnode * _directory,const char * name,ino_t * _vnodeID)447 ext2_lookup(fs_volume* _volume, fs_vnode* _directory, const char* name,
448 ino_t* _vnodeID)
449 {
450 TRACE("ext2_lookup: name address: %p\n", name);
451 TRACE("ext2_lookup: name: %s\n", name);
452 Volume* volume = (Volume*)_volume->private_volume;
453 Inode* directory = (Inode*)_directory->private_node;
454
455 // check access permissions
456 status_t status = directory->CheckPermissions(X_OK);
457 if (status < B_OK)
458 return status;
459
460 HTree htree(volume, directory);
461 DirectoryIterator* iterator;
462
463 status = htree.Lookup(name, &iterator);
464 if (status != B_OK)
465 return status;
466
467 ObjectDeleter<DirectoryIterator> iteratorDeleter(iterator);
468
469 status = iterator->FindEntry(name, _vnodeID);
470 if (status != B_OK) {
471 if (status == B_ENTRY_NOT_FOUND)
472 entry_cache_add_missing(volume->ID(), directory->ID(), name);
473 return status;
474 }
475 entry_cache_add(volume->ID(), directory->ID(), name, *_vnodeID);
476
477 return get_vnode(volume->FSVolume(), *_vnodeID, NULL);
478 }
479
480
481 static status_t
ext2_ioctl(fs_volume * _volume,fs_vnode * _node,void * _cookie,uint32 cmd,void * buffer,size_t bufferLength)482 ext2_ioctl(fs_volume* _volume, fs_vnode* _node, void* _cookie, uint32 cmd,
483 void* buffer, size_t bufferLength)
484 {
485 TRACE("ioctl: %" B_PRIu32 "\n", cmd);
486
487 Volume* volume = (Volume*)_volume->private_volume;
488 switch (cmd) {
489 case 56742:
490 {
491 TRACE("ioctl: Test the block allocator\n");
492 // Test the block allocator
493 TRACE("ioctl: Creating transaction\n");
494 Transaction transaction(volume->GetJournal());
495 TRACE("ioctl: Creating cached block\n");
496 CachedBlock cached(volume);
497 uint32 blocksPerGroup = volume->BlocksPerGroup();
498 uint32 blockSize = volume->BlockSize();
499 uint32 firstBlock = volume->FirstDataBlock();
500 fsblock_t start = 0;
501 uint32 group = 0;
502 uint32 length;
503
504 TRACE("ioctl: blocks per group: %" B_PRIu32 ", block size: %"
505 B_PRIu32 ", first block: %" B_PRIu32 ", start: %" B_PRIu64
506 ", group: %" B_PRIu32 "\n", blocksPerGroup,
507 blockSize, firstBlock, start, group);
508
509 while (volume->AllocateBlocks(transaction, 1, 2048, group, start,
510 length) == B_OK) {
511 TRACE("ioctl: Allocated blocks in group %" B_PRIu32 ": %"
512 B_PRIu64 "-%" B_PRIu64 "\n", group, start, start + length);
513 off_t blockNum = start + group * blocksPerGroup - firstBlock;
514
515 for (uint32 i = 0; i < length; ++i) {
516 uint8* block = cached.SetToWritable(transaction, blockNum);
517 memset(block, 0, blockSize);
518 blockNum++;
519 }
520
521 TRACE("ioctl: Blocks cleared\n");
522
523 transaction.Done();
524 transaction.Start(volume->GetJournal());
525 }
526
527 TRACE("ioctl: Done\n");
528
529 return B_OK;
530 }
531
532 case FIOSEEKDATA:
533 case FIOSEEKHOLE:
534 {
535 off_t* offset = (off_t*)buffer;
536 Inode* inode = (Inode*)_node->private_node;
537
538 if (*offset >= inode->Size())
539 return ENXIO;
540
541 while (*offset < inode->Size()) {
542 fsblock_t block;
543 uint32 count = 1;
544 status_t status = inode->FindBlock(*offset, block, &count);
545 if (status != B_OK)
546 return status;
547 if ((block != 0 && cmd == FIOSEEKDATA)
548 || (block == 0 && cmd == FIOSEEKHOLE)) {
549 return B_OK;
550 }
551 *offset += count * volume->BlockSize();
552 }
553
554 if (*offset > inode->Size())
555 *offset = inode->Size();
556 return cmd == FIOSEEKDATA ? ENXIO : B_OK;
557 }
558 }
559
560 return B_DEV_INVALID_IOCTL;
561 }
562
563
564 /*! Sets the open-mode flags for the open file cookie - only
565 supports O_APPEND currently, but that should be sufficient
566 for a file system.
567 */
568 static status_t
ext2_set_flags(fs_volume * _volume,fs_vnode * _node,void * _cookie,int flags)569 ext2_set_flags(fs_volume* _volume, fs_vnode* _node, void* _cookie, int flags)
570 {
571 file_cookie* cookie = (file_cookie*)_cookie;
572 cookie->open_mode = (cookie->open_mode & ~O_APPEND) | (flags & O_APPEND);
573
574 return B_OK;
575 }
576
577
578 static status_t
ext2_fsync(fs_volume * _volume,fs_vnode * _node)579 ext2_fsync(fs_volume* _volume, fs_vnode* _node)
580 {
581 Inode* inode = (Inode*)_node->private_node;
582 return inode->Sync();
583 }
584
585
586 static status_t
ext2_read_stat(fs_volume * _volume,fs_vnode * _node,struct stat * stat)587 ext2_read_stat(fs_volume* _volume, fs_vnode* _node, struct stat* stat)
588 {
589 Inode* inode = (Inode*)_node->private_node;
590 const ext2_inode& node = inode->Node();
591
592 stat->st_dev = inode->GetVolume()->ID();
593 stat->st_ino = inode->ID();
594 stat->st_nlink = node.NumLinks();
595 stat->st_blksize = EXT2_IO_SIZE;
596
597 stat->st_uid = node.UserID();
598 stat->st_gid = node.GroupID();
599 stat->st_mode = node.Mode();
600 stat->st_type = 0;
601
602 inode->GetAccessTime(&stat->st_atim);
603 inode->GetModificationTime(&stat->st_mtim);
604 inode->GetChangeTime(&stat->st_ctim);
605 inode->GetCreationTime(&stat->st_crtim);
606
607 stat->st_size = inode->Size();
608 stat->st_blocks = inode->NumBlocks();
609
610 return B_OK;
611 }
612
613
614 status_t
ext2_write_stat(fs_volume * _volume,fs_vnode * _node,const struct stat * stat,uint32 mask)615 ext2_write_stat(fs_volume* _volume, fs_vnode* _node, const struct stat* stat,
616 uint32 mask)
617 {
618 TRACE("ext2_write_stat\n");
619 Volume* volume = (Volume*)_volume->private_volume;
620
621 if (volume->IsReadOnly())
622 return B_READ_ONLY_DEVICE;
623
624 Inode* inode = (Inode*)_node->private_node;
625
626 ext2_inode& node = inode->Node();
627 bool updateTime = false;
628 uid_t uid = geteuid();
629
630 bool isOwnerOrRoot = uid == 0 || uid == (uid_t)node.UserID();
631 bool hasWriteAccess = inode->CheckPermissions(W_OK) == B_OK;
632
633 TRACE("ext2_write_stat: Starting transaction\n");
634 Transaction transaction(volume->GetJournal());
635 inode->WriteLockInTransaction(transaction);
636
637 if ((mask & B_STAT_SIZE) != 0 && inode->Size() != stat->st_size) {
638 if (inode->IsDirectory())
639 return B_IS_A_DIRECTORY;
640 if (!inode->IsFile())
641 return B_BAD_VALUE;
642 if (!hasWriteAccess)
643 return B_NOT_ALLOWED;
644
645 TRACE("ext2_write_stat: Old size: %ld, new size: %ld\n",
646 (long)inode->Size(), (long)stat->st_size);
647
648 off_t oldSize = inode->Size();
649
650 status_t status = inode->Resize(transaction, stat->st_size);
651 if (status != B_OK)
652 return status;
653
654 if ((mask & B_STAT_SIZE_INSECURE) == 0) {
655 rw_lock_write_unlock(inode->Lock());
656 inode->FillGapWithZeros(oldSize, inode->Size());
657 rw_lock_write_lock(inode->Lock());
658 }
659
660 updateTime = true;
661 }
662
663 if ((mask & B_STAT_MODE) != 0) {
664 // only the user or root can do that
665 if (!isOwnerOrRoot)
666 return B_NOT_ALLOWED;
667 node.UpdateMode(stat->st_mode, S_IUMSK);
668 updateTime = true;
669 }
670
671 if ((mask & B_STAT_UID) != 0) {
672 // only root should be allowed
673 if (uid != 0)
674 return B_NOT_ALLOWED;
675 node.SetUserID(stat->st_uid);
676 updateTime = true;
677 }
678
679 if ((mask & B_STAT_GID) != 0) {
680 // only the user or root can do that
681 if (!isOwnerOrRoot)
682 return B_NOT_ALLOWED;
683 node.SetGroupID(stat->st_gid);
684 updateTime = true;
685 }
686
687 if ((mask & B_STAT_MODIFICATION_TIME) != 0 || updateTime
688 || (mask & B_STAT_CHANGE_TIME) != 0) {
689 // the user or root can do that or any user with write access
690 if (!isOwnerOrRoot && !hasWriteAccess)
691 return B_NOT_ALLOWED;
692 struct timespec newTimespec = { 0, 0};
693
694 if ((mask & B_STAT_MODIFICATION_TIME) != 0)
695 newTimespec = stat->st_mtim;
696
697 if ((mask & B_STAT_CHANGE_TIME) != 0
698 && stat->st_ctim.tv_sec > newTimespec.tv_sec)
699 newTimespec = stat->st_ctim;
700
701 if (newTimespec.tv_sec == 0)
702 Inode::_BigtimeToTimespec(real_time_clock_usecs(), &newTimespec);
703
704 inode->SetModificationTime(&newTimespec);
705 }
706 if ((mask & B_STAT_CREATION_TIME) != 0) {
707 // the user or root can do that or any user with write access
708 if (!isOwnerOrRoot && !hasWriteAccess)
709 return B_NOT_ALLOWED;
710 inode->SetCreationTime(&stat->st_crtim);
711 }
712
713 status_t status = inode->WriteBack(transaction);
714 if (status == B_OK)
715 status = transaction.Done();
716 if (status == B_OK)
717 notify_stat_changed(volume->ID(), -1, inode->ID(), mask);
718
719 return status;
720 }
721
722
723 static status_t
ext2_create(fs_volume * _volume,fs_vnode * _directory,const char * name,int openMode,int mode,void ** _cookie,ino_t * _vnodeID)724 ext2_create(fs_volume* _volume, fs_vnode* _directory, const char* name,
725 int openMode, int mode, void** _cookie, ino_t* _vnodeID)
726 {
727 Volume* volume = (Volume*)_volume->private_volume;
728 Inode* directory = (Inode*)_directory->private_node;
729
730 TRACE("ext2_create()\n");
731
732 if (volume->IsReadOnly())
733 return B_READ_ONLY_DEVICE;
734
735 if (!directory->IsDirectory())
736 return B_BAD_TYPE;
737
738 TRACE("ext2_create(): Creating cookie\n");
739
740 // Allocate cookie
741 file_cookie* cookie = new(std::nothrow) file_cookie;
742 if (cookie == NULL)
743 return B_NO_MEMORY;
744 ObjectDeleter<file_cookie> cookieDeleter(cookie);
745
746 cookie->open_mode = openMode;
747 cookie->last_size = 0;
748 cookie->last_notification = system_time();
749
750 TRACE("ext2_create(): Starting transaction\n");
751
752 Transaction transaction(volume->GetJournal());
753
754 TRACE("ext2_create(): Creating inode\n");
755
756 Inode* inode;
757 bool created;
758 status_t status = Inode::Create(transaction, directory, name,
759 S_FILE | (mode & S_IUMSK), openMode, EXT2_TYPE_FILE, &created, _vnodeID,
760 &inode, &gExt2VnodeOps);
761 if (status != B_OK)
762 return status;
763
764 TRACE("ext2_create(): Created inode\n");
765
766 if ((openMode & O_NOCACHE) != 0) {
767 status = inode->DisableFileCache();
768 if (status != B_OK)
769 return status;
770 }
771
772 entry_cache_add(volume->ID(), directory->ID(), name, *_vnodeID);
773
774 status = transaction.Done();
775 if (status != B_OK) {
776 entry_cache_remove(volume->ID(), directory->ID(), name);
777 return status;
778 }
779
780 *_cookie = cookie;
781 cookieDeleter.Detach();
782
783 if (created)
784 notify_entry_created(volume->ID(), directory->ID(), name, *_vnodeID);
785
786 return B_OK;
787 }
788
789
790 static status_t
ext2_create_symlink(fs_volume * _volume,fs_vnode * _directory,const char * name,const char * path,int mode)791 ext2_create_symlink(fs_volume* _volume, fs_vnode* _directory, const char* name,
792 const char* path, int mode)
793 {
794 TRACE("ext2_create_symlink()\n");
795
796 Volume* volume = (Volume*)_volume->private_volume;
797 Inode* directory = (Inode*)_directory->private_node;
798
799 if (volume->IsReadOnly())
800 return B_READ_ONLY_DEVICE;
801
802 if (!directory->IsDirectory())
803 return B_BAD_TYPE;
804
805 status_t status = directory->CheckPermissions(W_OK);
806 if (status != B_OK)
807 return status;
808
809 TRACE("ext2_create_symlink(): Starting transaction\n");
810 Transaction transaction(volume->GetJournal());
811
812 Inode* link;
813 ino_t id;
814 status = Inode::Create(transaction, directory, name, S_SYMLINK | 0777,
815 0, (uint8)EXT2_TYPE_SYMLINK, NULL, &id, &link);
816 if (status != B_OK)
817 return status;
818
819 // TODO: We have to prepare the link before publishing?
820
821 size_t length = strlen(path);
822 TRACE("ext2_create_symlink(): Path (%s) length: %d\n", path, (int)length);
823 if (length < EXT2_SHORT_SYMLINK_LENGTH) {
824 strcpy(link->Node().symlink, path);
825 link->Node().SetSize((uint32)length);
826 } else {
827 if (!link->HasFileCache()) {
828 status = link->CreateFileCache();
829 if (status != B_OK)
830 return status;
831 }
832
833 size_t written = length;
834 status = link->WriteAt(transaction, 0, (const uint8*)path, &written);
835 if (status == B_OK && written != length)
836 status = B_IO_ERROR;
837 }
838
839 if (status == B_OK)
840 status = link->WriteBack(transaction);
841
842 TRACE("ext2_create_symlink(): Publishing vnode\n");
843 publish_vnode(volume->FSVolume(), id, link, &gExt2VnodeOps,
844 link->Mode(), 0);
845 put_vnode(volume->FSVolume(), id);
846
847 if (status == B_OK) {
848 entry_cache_add(volume->ID(), directory->ID(), name, id);
849
850 status = transaction.Done();
851 if (status == B_OK)
852 notify_entry_created(volume->ID(), directory->ID(), name, id);
853 else
854 entry_cache_remove(volume->ID(), directory->ID(), name);
855 }
856 TRACE("ext2_create_symlink(): Done\n");
857
858 return status;
859 }
860
861
862 static status_t
ext2_link(fs_volume * volume,fs_vnode * dir,const char * name,fs_vnode * node)863 ext2_link(fs_volume* volume, fs_vnode* dir, const char* name, fs_vnode* node)
864 {
865 // TODO
866
867 return B_UNSUPPORTED;
868 }
869
870
871 static status_t
ext2_unlink(fs_volume * _volume,fs_vnode * _directory,const char * name)872 ext2_unlink(fs_volume* _volume, fs_vnode* _directory, const char* name)
873 {
874 TRACE("ext2_unlink()\n");
875 if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
876 return B_NOT_ALLOWED;
877
878 Volume* volume = (Volume*)_volume->private_volume;
879 Inode* directory = (Inode*)_directory->private_node;
880
881 status_t status = directory->CheckPermissions(W_OK);
882 if (status != B_OK)
883 return status;
884
885 TRACE("ext2_unlink(): Starting transaction\n");
886 Transaction transaction(volume->GetJournal());
887
888 directory->WriteLockInTransaction(transaction);
889
890 TRACE("ext2_unlink(): Looking up for directory entry\n");
891 HTree htree(volume, directory);
892 DirectoryIterator* directoryIterator;
893
894 status = htree.Lookup(name, &directoryIterator);
895 if (status != B_OK)
896 return status;
897
898 ino_t id;
899 status = directoryIterator->FindEntry(name, &id);
900 if (status != B_OK)
901 return status;
902
903 {
904 Vnode vnode(volume, id);
905 Inode* inode;
906 status = vnode.Get(&inode);
907 if (status != B_OK)
908 return status;
909
910 inode->WriteLockInTransaction(transaction);
911
912 status = inode->Unlink(transaction);
913 if (status != B_OK)
914 return status;
915
916 status = directoryIterator->RemoveEntry(transaction);
917 if (status != B_OK)
918 return status;
919 }
920 entry_cache_remove(volume->ID(), directory->ID(), name);
921
922 status = transaction.Done();
923 if (status != B_OK)
924 entry_cache_add(volume->ID(), directory->ID(), name, id);
925 else
926 notify_entry_removed(volume->ID(), directory->ID(), name, id);
927
928 return status;
929 }
930
931
932 static status_t
ext2_rename(fs_volume * _volume,fs_vnode * _oldDir,const char * oldName,fs_vnode * _newDir,const char * newName)933 ext2_rename(fs_volume* _volume, fs_vnode* _oldDir, const char* oldName,
934 fs_vnode* _newDir, const char* newName)
935 {
936 TRACE("ext2_rename()\n");
937
938 Volume* volume = (Volume*)_volume->private_volume;
939 Inode* oldDirectory = (Inode*)_oldDir->private_node;
940 Inode* newDirectory = (Inode*)_newDir->private_node;
941
942 if (oldDirectory == newDirectory && strcmp(oldName, newName) == 0)
943 return B_OK;
944
945 Transaction transaction(volume->GetJournal());
946
947 oldDirectory->WriteLockInTransaction(transaction);
948 if (oldDirectory != newDirectory)
949 newDirectory->WriteLockInTransaction(transaction);
950
951 status_t status = oldDirectory->CheckPermissions(W_OK);
952 if (status == B_OK)
953 status = newDirectory->CheckPermissions(W_OK);
954 if (status != B_OK)
955 return status;
956
957 HTree oldHTree(volume, oldDirectory);
958 DirectoryIterator* oldIterator;
959
960 status = oldHTree.Lookup(oldName, &oldIterator);
961 if (status != B_OK)
962 return status;
963
964 ObjectDeleter<DirectoryIterator> oldIteratorDeleter(oldIterator);
965
966 ino_t oldID;
967 status = oldIterator->FindEntry(oldName, &oldID);
968 if (status != B_OK)
969 return status;
970
971 TRACE("ext2_rename(): found entry to rename\n");
972
973 if (oldDirectory != newDirectory) {
974 TRACE("ext2_rename(): Different parent directories\n");
975 CachedBlock cached(volume);
976
977 ino_t parentID = newDirectory->ID();
978 ino_t oldDirID = oldDirectory->ID();
979
980 do {
981 Vnode vnode(volume, parentID);
982 Inode* parent;
983
984 status = vnode.Get(&parent);
985 if (status != B_OK)
986 return B_IO_ERROR;
987
988 fsblock_t blockNum;
989 status = parent->FindBlock(0, blockNum);
990 if (status != B_OK)
991 return status;
992
993 const HTreeRoot* data = (const HTreeRoot*)cached.SetTo(blockNum);
994 parentID = data->dotdot.InodeID();
995 } while (parentID != oldID && parentID != oldDirID
996 && parentID != EXT2_ROOT_NODE);
997
998 if (parentID == oldID)
999 return B_BAD_VALUE;
1000 }
1001
1002 HTree newHTree(volume, newDirectory);
1003 DirectoryIterator* newIterator;
1004
1005 status = newHTree.Lookup(newName, &newIterator);
1006 if (status != B_OK)
1007 return status;
1008
1009 TRACE("ext2_rename(): found new directory\n");
1010
1011 ObjectDeleter<DirectoryIterator> newIteratorDeleter(newIterator);
1012
1013 Vnode vnode(volume, oldID);
1014 Inode* inode;
1015
1016 status = vnode.Get(&inode);
1017 if (status != B_OK)
1018 return status;
1019
1020 uint8 fileType;
1021
1022 // TODO: Support all file types?
1023 if (inode->IsDirectory())
1024 fileType = EXT2_TYPE_DIRECTORY;
1025 else if (inode->IsSymLink())
1026 fileType = EXT2_TYPE_SYMLINK;
1027 else
1028 fileType = EXT2_TYPE_FILE;
1029
1030 // Add entry in destination directory
1031 ino_t existentID;
1032 status = newIterator->FindEntry(newName, &existentID);
1033 if (status == B_OK) {
1034 TRACE("ext2_rename(): found existing new entry\n");
1035 if (existentID == oldID) {
1036 // Remove entry in oldID
1037 // return inode->Unlink();
1038 return B_BAD_VALUE;
1039 }
1040
1041 Vnode vnodeExistent(volume, existentID);
1042 Inode* existent;
1043
1044 if (vnodeExistent.Get(&existent) != B_OK)
1045 return B_NAME_IN_USE;
1046
1047 if (existent->IsDirectory() != inode->IsDirectory()) {
1048 return existent->IsDirectory() ? B_IS_A_DIRECTORY
1049 : B_NOT_A_DIRECTORY;
1050 }
1051
1052 // TODO: Perhaps we have to revert this in case of error?
1053 status = newIterator->ChangeEntry(transaction, oldID, fileType);
1054 if (status != B_OK)
1055 return status;
1056
1057 status = existent->Unlink(transaction);
1058 if (status != B_OK)
1059 ERROR("Error while unlinking existing destination\n");
1060
1061 entry_cache_remove(volume->ID(), newDirectory->ID(), newName);
1062
1063 notify_entry_removed(volume->ID(), newDirectory->ID(), newName,
1064 existentID);
1065 } else if (status == B_ENTRY_NOT_FOUND) {
1066 newIterator->Restart();
1067
1068 status = newIterator->AddEntry(transaction, newName, strlen(newName),
1069 oldID, fileType);
1070 if (status != B_OK)
1071 return status;
1072
1073 } else
1074 return status;
1075
1076 if (oldDirectory == newDirectory) {
1077 status = oldHTree.Lookup(oldName, &oldIterator);
1078 if (status != B_OK)
1079 return status;
1080
1081 oldIteratorDeleter.SetTo(oldIterator);
1082 status = oldIterator->FindEntry(oldName, &oldID);
1083 if (status != B_OK)
1084 return status;
1085 }
1086
1087 // Remove entry from source folder
1088 status = oldIterator->RemoveEntry(transaction);
1089 if (status != B_OK)
1090 return status;
1091
1092 inode->WriteLockInTransaction(transaction);
1093
1094 if (oldDirectory != newDirectory && inode->IsDirectory()) {
1095 DirectoryIterator inodeIterator(inode);
1096
1097 status = inodeIterator.FindEntry("..");
1098 if (status == B_ENTRY_NOT_FOUND) {
1099 ERROR("Corrupt file system. Missing \"..\" in directory %"
1100 B_PRIdINO "\n", inode->ID());
1101 return B_BAD_DATA;
1102 } else if (status != B_OK)
1103 return status;
1104
1105 inodeIterator.ChangeEntry(transaction, newDirectory->ID(),
1106 (uint8)EXT2_TYPE_DIRECTORY);
1107 // Decrement hardlink count on the source folder
1108 status = oldDirectory->Unlink(transaction);
1109 if (status != B_OK)
1110 ERROR("Error while decrementing hardlink count on the source folder\n");
1111 // Increment hardlink count on the destination folder
1112 newDirectory->IncrementNumLinks(transaction);
1113 status = newDirectory->WriteBack(transaction);
1114 if (status != B_OK)
1115 ERROR("Error while writing back the destination folder inode\n");
1116 }
1117
1118 status = inode->WriteBack(transaction);
1119 if (status != B_OK)
1120 return status;
1121
1122 entry_cache_remove(volume->ID(), oldDirectory->ID(), oldName);
1123 entry_cache_add(volume->ID(), newDirectory->ID(), newName, oldID);
1124
1125 status = transaction.Done();
1126 if (status != B_OK) {
1127 entry_cache_remove(volume->ID(), oldDirectory->ID(), newName);
1128 entry_cache_add(volume->ID(), newDirectory->ID(), oldName, oldID);
1129
1130 return status;
1131 }
1132
1133 notify_entry_moved(volume->ID(), oldDirectory->ID(), oldName,
1134 newDirectory->ID(), newName, oldID);
1135
1136 return B_OK;
1137 }
1138
1139
1140 static status_t
ext2_open(fs_volume * _volume,fs_vnode * _node,int openMode,void ** _cookie)1141 ext2_open(fs_volume* _volume, fs_vnode* _node, int openMode, void** _cookie)
1142 {
1143 Volume* volume = (Volume*)_volume->private_volume;
1144 Inode* inode = (Inode*)_node->private_node;
1145
1146 // opening a directory read-only is allowed, although you can't read
1147 // any data from it.
1148 if (inode->IsDirectory() && (openMode & O_RWMASK) != 0)
1149 return B_IS_A_DIRECTORY;
1150
1151 status_t status = inode->CheckPermissions(open_mode_to_access(openMode)
1152 | (openMode & O_TRUNC ? W_OK : 0));
1153 if (status != B_OK)
1154 return status;
1155
1156 // Prepare the cookie
1157 file_cookie* cookie = new(std::nothrow) file_cookie;
1158 if (cookie == NULL)
1159 return B_NO_MEMORY;
1160 ObjectDeleter<file_cookie> cookieDeleter(cookie);
1161
1162 cookie->open_mode = openMode & EXT2_OPEN_MODE_USER_MASK;
1163 cookie->last_size = inode->Size();
1164 cookie->last_notification = system_time();
1165
1166 MethodDeleter<Inode, status_t, &Inode::EnableFileCache> fileCacheEnabler;
1167 if ((openMode & O_NOCACHE) != 0) {
1168 status = inode->DisableFileCache();
1169 if (status != B_OK)
1170 return status;
1171 fileCacheEnabler.SetTo(inode);
1172 }
1173
1174 // Should we truncate the file?
1175 if ((openMode & O_TRUNC) != 0) {
1176 if ((openMode & O_RWMASK) == O_RDONLY)
1177 return B_NOT_ALLOWED;
1178
1179 Transaction transaction(volume->GetJournal());
1180 inode->WriteLockInTransaction(transaction);
1181
1182 status_t status = inode->Resize(transaction, 0);
1183 if (status == B_OK)
1184 status = inode->WriteBack(transaction);
1185 if (status == B_OK)
1186 status = transaction.Done();
1187 if (status != B_OK)
1188 return status;
1189
1190 // TODO: No need to notify file size changed?
1191 }
1192
1193 fileCacheEnabler.Detach();
1194 cookieDeleter.Detach();
1195 *_cookie = cookie;
1196
1197 return B_OK;
1198 }
1199
1200
1201 static status_t
ext2_read(fs_volume * _volume,fs_vnode * _node,void * _cookie,off_t pos,void * buffer,size_t * _length)1202 ext2_read(fs_volume* _volume, fs_vnode* _node, void* _cookie, off_t pos,
1203 void* buffer, size_t* _length)
1204 {
1205 Inode* inode = (Inode*)_node->private_node;
1206
1207 if (!inode->IsFile()) {
1208 *_length = 0;
1209 return inode->IsDirectory() ? B_IS_A_DIRECTORY : B_BAD_VALUE;
1210 }
1211
1212 return inode->ReadAt(pos, (uint8*)buffer, _length);
1213 }
1214
1215
1216 static status_t
ext2_write(fs_volume * _volume,fs_vnode * _node,void * _cookie,off_t pos,const void * buffer,size_t * _length)1217 ext2_write(fs_volume* _volume, fs_vnode* _node, void* _cookie, off_t pos,
1218 const void* buffer, size_t* _length)
1219 {
1220 TRACE("ext2_write()\n");
1221 Volume* volume = (Volume*)_volume->private_volume;
1222 Inode* inode = (Inode*)_node->private_node;
1223
1224 if (volume->IsReadOnly())
1225 return B_READ_ONLY_DEVICE;
1226
1227 if (inode->IsDirectory()) {
1228 *_length = 0;
1229 return B_IS_A_DIRECTORY;
1230 }
1231
1232 TRACE("ext2_write(): Preparing cookie\n");
1233
1234 file_cookie* cookie = (file_cookie*)_cookie;
1235
1236 if ((cookie->open_mode & O_APPEND) != 0)
1237 pos = inode->Size();
1238
1239 TRACE("ext2_write(): Creating transaction\n");
1240 Transaction transaction;
1241
1242 status_t status = inode->WriteAt(transaction, pos, (const uint8*)buffer,
1243 _length);
1244 if (status == B_OK)
1245 status = transaction.Done();
1246 if (status == B_OK) {
1247 TRACE("ext2_write(): Finalizing\n");
1248 ReadLocker lock(*inode->Lock());
1249
1250 if (cookie->last_size != inode->Size()
1251 && system_time() > cookie->last_notification
1252 + INODE_NOTIFICATION_INTERVAL) {
1253 notify_stat_changed(volume->ID(), -1, inode->ID(),
1254 B_STAT_MODIFICATION_TIME | B_STAT_SIZE | B_STAT_INTERIM_UPDATE);
1255 cookie->last_size = inode->Size();
1256 cookie->last_notification = system_time();
1257 }
1258 }
1259
1260 TRACE("ext2_write(): Done\n");
1261
1262 return status;
1263 }
1264
1265
1266 static status_t
ext2_close(fs_volume * _volume,fs_vnode * _node,void * _cookie)1267 ext2_close(fs_volume *_volume, fs_vnode *_node, void *_cookie)
1268 {
1269 return B_OK;
1270 }
1271
1272
1273 static status_t
ext2_free_cookie(fs_volume * _volume,fs_vnode * _node,void * _cookie)1274 ext2_free_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie)
1275 {
1276 file_cookie* cookie = (file_cookie*)_cookie;
1277 Volume* volume = (Volume*)_volume->private_volume;
1278 Inode* inode = (Inode*)_node->private_node;
1279
1280 if (inode->Size() != cookie->last_size)
1281 notify_stat_changed(volume->ID(), -1, inode->ID(), B_STAT_SIZE);
1282
1283 if ((cookie->open_mode & O_NOCACHE) != 0)
1284 inode->EnableFileCache();
1285
1286 delete cookie;
1287 return B_OK;
1288 }
1289
1290
1291 static status_t
ext2_access(fs_volume * _volume,fs_vnode * _node,int accessMode)1292 ext2_access(fs_volume* _volume, fs_vnode* _node, int accessMode)
1293 {
1294 Inode* inode = (Inode*)_node->private_node;
1295 return inode->CheckPermissions(accessMode);
1296 }
1297
1298
1299 static status_t
ext2_read_link(fs_volume * _volume,fs_vnode * _node,char * buffer,size_t * _bufferSize)1300 ext2_read_link(fs_volume *_volume, fs_vnode *_node, char *buffer,
1301 size_t *_bufferSize)
1302 {
1303 Inode* inode = (Inode*)_node->private_node;
1304
1305 if (!inode->IsSymLink())
1306 return B_BAD_VALUE;
1307
1308 if (inode->Size() > EXT2_SHORT_SYMLINK_LENGTH) {
1309 status_t result = inode->ReadAt(0, reinterpret_cast<uint8*>(buffer),
1310 _bufferSize);
1311 if (result != B_OK)
1312 return result;
1313 } else {
1314 size_t bytesToCopy = std::min(static_cast<size_t>(inode->Size()),
1315 *_bufferSize);
1316
1317 memcpy(buffer, inode->Node().symlink, bytesToCopy);
1318 }
1319
1320 *_bufferSize = inode->Size();
1321 return B_OK;
1322 }
1323
1324
1325 // #pragma mark - Directory functions
1326
1327
1328 static status_t
ext2_create_dir(fs_volume * _volume,fs_vnode * _directory,const char * name,int mode)1329 ext2_create_dir(fs_volume* _volume, fs_vnode* _directory, const char* name,
1330 int mode)
1331 {
1332 TRACE("ext2_create_dir()\n");
1333 Volume* volume = (Volume*)_volume->private_volume;
1334 Inode* directory = (Inode*)_directory->private_node;
1335
1336 if (volume->IsReadOnly())
1337 return B_READ_ONLY_DEVICE;
1338
1339 if (!directory->IsDirectory())
1340 return B_BAD_TYPE;
1341
1342 status_t status = directory->CheckPermissions(W_OK);
1343 if (status != B_OK)
1344 return status;
1345
1346 TRACE("ext2_create_dir(): Starting transaction\n");
1347 Transaction transaction(volume->GetJournal());
1348
1349 ino_t id;
1350 status = Inode::Create(transaction, directory, name,
1351 S_DIRECTORY | (mode & S_IUMSK), 0, EXT2_TYPE_DIRECTORY, NULL, &id);
1352 if (status != B_OK)
1353 return status;
1354
1355 put_vnode(volume->FSVolume(), id);
1356
1357 entry_cache_add(volume->ID(), directory->ID(), name, id);
1358
1359 status = transaction.Done();
1360 if (status != B_OK) {
1361 entry_cache_remove(volume->ID(), directory->ID(), name);
1362 return status;
1363 }
1364
1365 notify_entry_created(volume->ID(), directory->ID(), name, id);
1366
1367 TRACE("ext2_create_dir(): Done\n");
1368
1369 return B_OK;
1370 }
1371
1372
1373 static status_t
ext2_remove_dir(fs_volume * _volume,fs_vnode * _directory,const char * name)1374 ext2_remove_dir(fs_volume* _volume, fs_vnode* _directory, const char* name)
1375 {
1376 TRACE("ext2_remove_dir()\n");
1377
1378 Volume* volume = (Volume*)_volume->private_volume;
1379 Inode* directory = (Inode*)_directory->private_node;
1380
1381 status_t status = directory->CheckPermissions(W_OK);
1382 if (status != B_OK)
1383 return status;
1384
1385 TRACE("ext2_remove_dir(): Starting transaction\n");
1386 Transaction transaction(volume->GetJournal());
1387
1388 directory->WriteLockInTransaction(transaction);
1389
1390 TRACE("ext2_remove_dir(): Looking up for directory entry\n");
1391 HTree htree(volume, directory);
1392 DirectoryIterator* directoryIterator;
1393
1394 status = htree.Lookup(name, &directoryIterator);
1395 if (status != B_OK)
1396 return status;
1397
1398 ino_t id;
1399 status = directoryIterator->FindEntry(name, &id);
1400 if (status != B_OK)
1401 return status;
1402
1403 Vnode vnode(volume, id);
1404 Inode* inode;
1405 status = vnode.Get(&inode);
1406 if (status != B_OK)
1407 return status;
1408
1409 inode->WriteLockInTransaction(transaction);
1410
1411 status = inode->Unlink(transaction);
1412 if (status != B_OK)
1413 return status;
1414
1415 status = directory->Unlink(transaction);
1416 if (status != B_OK)
1417 return status;
1418
1419 status = directoryIterator->RemoveEntry(transaction);
1420 if (status != B_OK)
1421 return status;
1422
1423 entry_cache_remove(volume->ID(), directory->ID(), name);
1424 entry_cache_remove(volume->ID(), id, "..");
1425
1426 status = transaction.Done();
1427 if (status != B_OK) {
1428 entry_cache_add(volume->ID(), directory->ID(), name, id);
1429 entry_cache_add(volume->ID(), id, "..", id);
1430 } else
1431 notify_entry_removed(volume->ID(), directory->ID(), name, id);
1432
1433 return status;
1434 }
1435
1436
1437 static status_t
ext2_open_dir(fs_volume * _volume,fs_vnode * _node,void ** _cookie)1438 ext2_open_dir(fs_volume* _volume, fs_vnode* _node, void** _cookie)
1439 {
1440 Inode* inode = (Inode*)_node->private_node;
1441 status_t status = inode->CheckPermissions(R_OK);
1442 if (status < B_OK)
1443 return status;
1444
1445 if (!inode->IsDirectory())
1446 return B_NOT_A_DIRECTORY;
1447
1448 DirectoryIterator* iterator = new(std::nothrow) DirectoryIterator(inode);
1449 if (iterator == NULL)
1450 return B_NO_MEMORY;
1451
1452 *_cookie = iterator;
1453 return B_OK;
1454 }
1455
1456
1457 static status_t
ext2_read_dir(fs_volume * _volume,fs_vnode * _node,void * _cookie,struct dirent * dirent,size_t bufferSize,uint32 * _num)1458 ext2_read_dir(fs_volume *_volume, fs_vnode *_node, void *_cookie,
1459 struct dirent *dirent, size_t bufferSize, uint32 *_num)
1460 {
1461 DirectoryIterator *iterator = (DirectoryIterator *)_cookie;
1462 Volume* volume = (Volume*)_volume->private_volume;
1463
1464 uint32 maxCount = *_num;
1465 uint32 count = 0;
1466
1467 while (count < maxCount && bufferSize > sizeof(struct dirent)) {
1468
1469 size_t length = bufferSize - offsetof(struct dirent, d_name);
1470 ino_t id;
1471
1472 status_t status = iterator->GetNext(dirent->d_name, &length, &id);
1473 if (status == B_ENTRY_NOT_FOUND)
1474 break;
1475
1476 if (status == B_BUFFER_OVERFLOW) {
1477 // the remaining name buffer length was too small
1478 if (count == 0)
1479 return B_BUFFER_OVERFLOW;
1480 break;
1481 }
1482
1483 if (status != B_OK)
1484 return status;
1485
1486 status = iterator->Next();
1487 if (status != B_OK && status != B_ENTRY_NOT_FOUND)
1488 return status;
1489
1490 dirent->d_dev = volume->ID();
1491 dirent->d_ino = id;
1492
1493 dirent = next_dirent(dirent, length, bufferSize);
1494 count++;
1495 }
1496
1497 *_num = count;
1498 return B_OK;
1499 }
1500
1501
1502 static status_t
ext2_rewind_dir(fs_volume *,fs_vnode *,void * _cookie)1503 ext2_rewind_dir(fs_volume * /*_volume*/, fs_vnode * /*node*/, void *_cookie)
1504 {
1505 DirectoryIterator *iterator = (DirectoryIterator *)_cookie;
1506 return iterator->Rewind();
1507 }
1508
1509
1510 static status_t
ext2_close_dir(fs_volume *,fs_vnode *,void *)1511 ext2_close_dir(fs_volume * /*_volume*/, fs_vnode * /*node*/, void * /*_cookie*/)
1512 {
1513 return B_OK;
1514 }
1515
1516
1517 static status_t
ext2_free_dir_cookie(fs_volume * _volume,fs_vnode * _node,void * _cookie)1518 ext2_free_dir_cookie(fs_volume *_volume, fs_vnode *_node, void *_cookie)
1519 {
1520 delete (DirectoryIterator*)_cookie;
1521 return B_OK;
1522 }
1523
1524
1525 static status_t
ext2_open_attr_dir(fs_volume * _volume,fs_vnode * _node,void ** _cookie)1526 ext2_open_attr_dir(fs_volume *_volume, fs_vnode *_node, void **_cookie)
1527 {
1528 Inode* inode = (Inode*)_node->private_node;
1529 Volume* volume = (Volume*)_volume->private_volume;
1530 TRACE("%s()\n", __FUNCTION__);
1531
1532 if (!(volume->SuperBlock().CompatibleFeatures() & EXT2_FEATURE_EXT_ATTR))
1533 return ENOSYS;
1534
1535 // on directories too ?
1536 if (!inode->IsFile())
1537 return EINVAL;
1538
1539 int32 *index = new(std::nothrow) int32;
1540 if (index == NULL)
1541 return B_NO_MEMORY;
1542 *index = 0;
1543 *(int32**)_cookie = index;
1544 return B_OK;
1545 }
1546
1547 static status_t
ext2_close_attr_dir(fs_volume * _volume,fs_vnode * _node,void * cookie)1548 ext2_close_attr_dir(fs_volume* _volume, fs_vnode* _node, void* cookie)
1549 {
1550 TRACE("%s()\n", __FUNCTION__);
1551 return B_OK;
1552 }
1553
1554
1555 static status_t
ext2_free_attr_dir_cookie(fs_volume * _volume,fs_vnode * _node,void * _cookie)1556 ext2_free_attr_dir_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie)
1557 {
1558 TRACE("%s()\n", __FUNCTION__);
1559 delete (int32 *)_cookie;
1560 return B_OK;
1561 }
1562
1563
1564 static status_t
ext2_read_attr_dir(fs_volume * _volume,fs_vnode * _node,void * _cookie,struct dirent * dirent,size_t bufferSize,uint32 * _num)1565 ext2_read_attr_dir(fs_volume* _volume, fs_vnode* _node,
1566 void* _cookie, struct dirent* dirent, size_t bufferSize,
1567 uint32* _num)
1568 {
1569 Inode* inode = (Inode*)_node->private_node;
1570 int32 index = *(int32 *)_cookie;
1571 Attribute attribute(inode);
1572 TRACE("%s()\n", __FUNCTION__);
1573
1574 size_t length = bufferSize;
1575 status_t status = attribute.Find(index);
1576 if (status == B_ENTRY_NOT_FOUND) {
1577 *_num = 0;
1578 return B_OK;
1579 } else if (status != B_OK)
1580 return status;
1581
1582 status = attribute.GetName(dirent->d_name, &length);
1583 if (status != B_OK)
1584 return B_OK;
1585
1586 Volume* volume = (Volume*)_volume->private_volume;
1587
1588 dirent->d_dev = volume->ID();
1589 dirent->d_ino = inode->ID();
1590 dirent->d_reclen = offsetof(struct dirent, d_name) + length + 1;
1591
1592 *_num = 1;
1593 *(int32*)_cookie = index + 1;
1594 return B_OK;
1595 }
1596
1597
1598 static status_t
ext2_rewind_attr_dir(fs_volume * _volume,fs_vnode * _node,void * _cookie)1599 ext2_rewind_attr_dir(fs_volume* _volume, fs_vnode* _node, void* _cookie)
1600 {
1601 *(int32*)_cookie = 0;
1602 TRACE("%s()\n", __FUNCTION__);
1603 return B_OK;
1604 }
1605
1606
1607 /* attribute operations */
1608 static status_t
ext2_open_attr(fs_volume * _volume,fs_vnode * _node,const char * name,int openMode,void ** _cookie)1609 ext2_open_attr(fs_volume* _volume, fs_vnode* _node, const char* name,
1610 int openMode, void** _cookie)
1611 {
1612 TRACE("%s()\n", __FUNCTION__);
1613
1614 Volume* volume = (Volume*)_volume->private_volume;
1615 Inode* inode = (Inode*)_node->private_node;
1616 Attribute attribute(inode);
1617
1618 if (!(volume->SuperBlock().CompatibleFeatures() & EXT2_FEATURE_EXT_ATTR))
1619 return ENOSYS;
1620
1621 return attribute.Open(name, openMode, (attr_cookie**)_cookie);
1622 }
1623
1624
1625 static status_t
ext2_close_attr(fs_volume * _volume,fs_vnode * _node,void * cookie)1626 ext2_close_attr(fs_volume* _volume, fs_vnode* _node,
1627 void* cookie)
1628 {
1629 return B_OK;
1630 }
1631
1632
1633 static status_t
ext2_free_attr_cookie(fs_volume * _volume,fs_vnode * _node,void * cookie)1634 ext2_free_attr_cookie(fs_volume* _volume, fs_vnode* _node,
1635 void* cookie)
1636 {
1637 delete (attr_cookie*)cookie;
1638 return B_OK;
1639 }
1640
1641
1642 static status_t
ext2_read_attr(fs_volume * _volume,fs_vnode * _node,void * _cookie,off_t pos,void * buffer,size_t * _length)1643 ext2_read_attr(fs_volume* _volume, fs_vnode* _node, void* _cookie,
1644 off_t pos, void* buffer, size_t* _length)
1645 {
1646 TRACE("%s()\n", __FUNCTION__);
1647
1648 attr_cookie* cookie = (attr_cookie*)_cookie;
1649 Inode* inode = (Inode*)_node->private_node;
1650
1651 Attribute attribute(inode, cookie);
1652
1653 return attribute.Read(cookie, pos, (uint8*)buffer, _length);
1654 }
1655
1656
1657 static status_t
ext2_read_attr_stat(fs_volume * _volume,fs_vnode * _node,void * _cookie,struct stat * stat)1658 ext2_read_attr_stat(fs_volume* _volume, fs_vnode* _node,
1659 void* _cookie, struct stat* stat)
1660 {
1661 attr_cookie* cookie = (attr_cookie*)_cookie;
1662 Inode* inode = (Inode*)_node->private_node;
1663
1664 Attribute attribute(inode, cookie);
1665
1666 return attribute.Stat(*stat);
1667 }
1668
1669
1670 fs_volume_ops gExt2VolumeOps = {
1671 &ext2_unmount,
1672 &ext2_read_fs_info,
1673 &ext2_write_fs_info,
1674 &ext2_sync,
1675 &ext2_get_vnode,
1676 };
1677
1678
1679 fs_vnode_ops gExt2VnodeOps = {
1680 /* vnode operations */
1681 &ext2_lookup,
1682 NULL,
1683 &ext2_put_vnode,
1684 &ext2_remove_vnode,
1685
1686 /* VM file access */
1687 &ext2_can_page,
1688 &ext2_read_pages,
1689 &ext2_write_pages,
1690
1691 NULL, // io()
1692 NULL, // cancel_io()
1693
1694 &ext2_get_file_map,
1695
1696 &ext2_ioctl,
1697 &ext2_set_flags,
1698 NULL, // fs_select
1699 NULL, // fs_deselect
1700 &ext2_fsync,
1701
1702 &ext2_read_link,
1703 &ext2_create_symlink,
1704
1705 &ext2_link,
1706 &ext2_unlink,
1707 &ext2_rename,
1708
1709 &ext2_access,
1710 &ext2_read_stat,
1711 &ext2_write_stat,
1712 NULL, // fs_preallocate
1713
1714 /* file operations */
1715 &ext2_create,
1716 &ext2_open,
1717 &ext2_close,
1718 &ext2_free_cookie,
1719 &ext2_read,
1720 &ext2_write,
1721
1722 /* directory operations */
1723 &ext2_create_dir,
1724 &ext2_remove_dir,
1725 &ext2_open_dir,
1726 &ext2_close_dir,
1727 &ext2_free_dir_cookie,
1728 &ext2_read_dir,
1729 &ext2_rewind_dir,
1730
1731 /* attribute directory operations */
1732 &ext2_open_attr_dir,
1733 &ext2_close_attr_dir,
1734 &ext2_free_attr_dir_cookie,
1735 &ext2_read_attr_dir,
1736 &ext2_rewind_attr_dir,
1737
1738 /* attribute operations */
1739 NULL,
1740 &ext2_open_attr,
1741 &ext2_close_attr,
1742 &ext2_free_attr_cookie,
1743 &ext2_read_attr,
1744 NULL,
1745 &ext2_read_attr_stat,
1746 NULL,
1747 NULL,
1748 NULL,
1749 };
1750
1751
1752 static file_system_module_info sExt2FileSystem = {
1753 {
1754 "file_systems/ext2" B_CURRENT_FS_API_VERSION,
1755 0,
1756 NULL,
1757 },
1758
1759 "ext4", // short_name
1760 "Linux Extended File System 2/3/4", // pretty_name
1761 B_DISK_SYSTEM_SUPPORTS_WRITING
1762 | B_DISK_SYSTEM_SUPPORTS_CONTENT_NAME, // DDM flags
1763
1764 // scanning
1765 ext2_identify_partition,
1766 ext2_scan_partition,
1767 ext2_free_identify_partition_cookie,
1768 NULL, // free_partition_content_cookie()
1769
1770 &ext2_mount,
1771
1772 NULL,
1773 };
1774
1775
1776 module_info *modules[] = {
1777 (module_info *)&sExt2FileSystem,
1778 NULL,
1779 };
1780