1 /*
2 * Copyright 2001-2020, Axel Dörfler, axeld@pinc-software.de.
3 * This file may be used under the terms of the MIT License.
4 */
5
6
7 //! file system interface to Haiku's vnode layer
8
9
10 #include "Attribute.h"
11 #include "CheckVisitor.h"
12 #include "Debug.h"
13 #include "Volume.h"
14 #include "Inode.h"
15 #include "Index.h"
16 #include "BPlusTree.h"
17 #include "Query.h"
18 #include "ResizeVisitor.h"
19 #include "bfs_control.h"
20 #include "bfs_disk_system.h"
21
22 // TODO: temporary solution as long as there is no public I/O requests API
23 #ifndef FS_SHELL
24 # include <io_requests.h>
25 # include <util/fs_trim_support.h>
26 #endif
27
28
29 #define BFS_IO_SIZE 65536
30
31 #if defined(BFS_LITTLE_ENDIAN_ONLY)
32 #define BFS_ENDIAN_SUFFIX ""
33 #define BFS_ENDIAN_PRETTY_SUFFIX ""
34 #else
35 #define BFS_ENDIAN_SUFFIX "_big"
36 #define BFS_ENDIAN_PRETTY_SUFFIX " (Big Endian)"
37 #endif
38
39
40 struct identify_cookie {
41 disk_super_block super_block;
42 };
43
44 extern void fill_stat_buffer(Inode* inode, struct stat& stat);
45
46
47 static void
fill_stat_time(const bfs_inode & node,struct stat & stat)48 fill_stat_time(const bfs_inode& node, struct stat& stat)
49 {
50 bigtime_t now = real_time_clock_usecs();
51 stat.st_atim.tv_sec = now / 1000000LL;
52 stat.st_atim.tv_nsec = (now % 1000000LL) * 1000;
53
54 stat.st_mtim.tv_sec = bfs_inode::ToSecs(node.LastModifiedTime());
55 stat.st_mtim.tv_nsec = bfs_inode::ToNsecs(node.LastModifiedTime());
56 stat.st_crtim.tv_sec = bfs_inode::ToSecs(node.CreateTime());
57 stat.st_crtim.tv_nsec = bfs_inode::ToNsecs(node.CreateTime());
58
59 // For BeOS compatibility, if on-disk ctime is invalid, fall back to mtime:
60 bigtime_t changeTime = node.StatusChangeTime();
61 if (changeTime < node.LastModifiedTime())
62 stat.st_ctim = stat.st_mtim;
63 else {
64 stat.st_ctim.tv_sec = bfs_inode::ToSecs(changeTime);
65 stat.st_ctim.tv_nsec = bfs_inode::ToNsecs(changeTime);
66 }
67 }
68
69
70 void
fill_stat_buffer(Inode * inode,struct stat & stat)71 fill_stat_buffer(Inode* inode, struct stat& stat)
72 {
73 const bfs_inode& node = inode->Node();
74
75 stat.st_dev = inode->GetVolume()->ID();
76 stat.st_ino = inode->ID();
77 stat.st_nlink = 1;
78 stat.st_blksize = BFS_IO_SIZE;
79
80 stat.st_uid = node.UserID();
81 stat.st_gid = node.GroupID();
82 stat.st_mode = node.Mode();
83 stat.st_type = node.Type();
84
85 fill_stat_time(node, stat);
86
87 if (inode->IsSymLink() && (inode->Flags() & INODE_LONG_SYMLINK) == 0) {
88 // symlinks report the size of the link here
89 stat.st_size = strlen(node.short_symlink);
90 } else
91 stat.st_size = inode->Size();
92
93 stat.st_blocks = inode->AllocatedSize() / 512;
94 }
95
96
97 //! bfs_io() callback hook
98 static status_t
iterative_io_get_vecs_hook(void * cookie,io_request * request,off_t offset,size_t size,struct file_io_vec * vecs,size_t * _count)99 iterative_io_get_vecs_hook(void* cookie, io_request* request, off_t offset,
100 size_t size, struct file_io_vec* vecs, size_t* _count)
101 {
102 Inode* inode = (Inode*)cookie;
103
104 return file_map_translate(inode->Map(), offset, size, vecs, _count,
105 inode->GetVolume()->BlockSize());
106 }
107
108
109 //! bfs_io() callback hook
110 static status_t
iterative_io_finished_hook(void * cookie,io_request * request,status_t status,bool partialTransfer,size_t bytesTransferred)111 iterative_io_finished_hook(void* cookie, io_request* request, status_t status,
112 bool partialTransfer, size_t bytesTransferred)
113 {
114 Inode* inode = (Inode*)cookie;
115 rw_lock_read_unlock(&inode->Lock());
116 put_vnode(inode->GetVolume()->FSVolume(), inode->ID());
117 return B_OK;
118 }
119
120
121 // #pragma mark - Scanning
122
123
124 static float
bfs_identify_partition(int fd,partition_data * partition,void ** _cookie)125 bfs_identify_partition(int fd, partition_data* partition, void** _cookie)
126 {
127 disk_super_block superBlock;
128 status_t status = Volume::Identify(fd, &superBlock);
129 if (status != B_OK)
130 return -1;
131
132 identify_cookie* cookie = new(std::nothrow) identify_cookie;
133 if (cookie == NULL)
134 return -1;
135
136 memcpy(&cookie->super_block, &superBlock, sizeof(disk_super_block));
137
138 *_cookie = cookie;
139 return 0.85f;
140 }
141
142
143 static status_t
bfs_scan_partition(int fd,partition_data * partition,void * _cookie)144 bfs_scan_partition(int fd, partition_data* partition, void* _cookie)
145 {
146 identify_cookie* cookie = (identify_cookie*)_cookie;
147
148 partition->status = B_PARTITION_VALID;
149 partition->flags |= B_PARTITION_FILE_SYSTEM;
150 partition->content_size = cookie->super_block.NumBlocks()
151 * cookie->super_block.BlockSize();
152 partition->block_size = cookie->super_block.BlockSize();
153 partition->content_name = strdup(cookie->super_block.name);
154 if (partition->content_name == NULL)
155 return B_NO_MEMORY;
156
157 return B_OK;
158 }
159
160
161 static void
bfs_free_identify_partition_cookie(partition_data * partition,void * _cookie)162 bfs_free_identify_partition_cookie(partition_data* partition, void* _cookie)
163 {
164 identify_cookie* cookie = (identify_cookie*)_cookie;
165 delete cookie;
166 }
167
168
169 // #pragma mark -
170
171
172 static status_t
bfs_mount(fs_volume * _volume,const char * device,uint32 flags,const char * args,ino_t * _rootID)173 bfs_mount(fs_volume* _volume, const char* device, uint32 flags,
174 const char* args, ino_t* _rootID)
175 {
176 FUNCTION();
177
178 Volume* volume = new(std::nothrow) Volume(_volume);
179 if (volume == NULL)
180 return B_NO_MEMORY;
181
182 status_t status = volume->Mount(device, flags);
183 if (status != B_OK) {
184 delete volume;
185 RETURN_ERROR(status);
186 }
187
188 _volume->private_volume = volume;
189 _volume->ops = &gBFSVolumeOps;
190 *_rootID = volume->ToVnode(volume->Root());
191
192 INFORM(("mounted \"%s\" (root node at %" B_PRIdINO ", device = %s)\n",
193 volume->Name(), *_rootID, device));
194 return B_OK;
195 }
196
197
198 static status_t
bfs_unmount(fs_volume * _volume)199 bfs_unmount(fs_volume* _volume)
200 {
201 FUNCTION();
202 Volume* volume = (Volume*)_volume->private_volume;
203
204 status_t status = volume->Unmount();
205 delete volume;
206
207 RETURN_ERROR(status);
208 }
209
210
211 static status_t
bfs_read_fs_stat(fs_volume * _volume,struct fs_info * info)212 bfs_read_fs_stat(fs_volume* _volume, struct fs_info* info)
213 {
214 FUNCTION();
215
216 Volume* volume = (Volume*)_volume->private_volume;
217 MutexLocker locker(volume->Lock());
218
219 // File system flags.
220 info->flags = B_FS_IS_PERSISTENT | B_FS_HAS_ATTR | B_FS_HAS_MIME
221 | (volume->IndicesNode() != NULL ? B_FS_HAS_QUERY : 0)
222 | (volume->IsReadOnly() ? B_FS_IS_READONLY : 0)
223 | B_FS_SUPPORTS_MONITOR_CHILDREN;
224
225 info->io_size = BFS_IO_SIZE;
226 // whatever is appropriate here?
227
228 info->block_size = volume->BlockSize();
229 info->total_blocks = volume->NumBlocks();
230 info->free_blocks = volume->FreeBlocks();
231
232 // Volume name
233 strlcpy(info->volume_name, volume->Name(), sizeof(info->volume_name));
234
235 // File system name
236 strlcpy(info->fsh_name, "bfs", sizeof(info->fsh_name));
237
238 return B_OK;
239 }
240
241
242 static status_t
bfs_write_fs_stat(fs_volume * _volume,const struct fs_info * info,uint32 mask)243 bfs_write_fs_stat(fs_volume* _volume, const struct fs_info* info, uint32 mask)
244 {
245 FUNCTION_START(("mask = %" B_PRId32 "\n", mask));
246
247 Volume* volume = (Volume*)_volume->private_volume;
248 if (volume->IsReadOnly())
249 return B_READ_ONLY_DEVICE;
250
251 MutexLocker locker(volume->Lock());
252
253 status_t status = B_BAD_VALUE;
254
255 if (mask & FS_WRITE_FSINFO_NAME) {
256 disk_super_block& superBlock = volume->SuperBlock();
257
258 strncpy(superBlock.name, info->volume_name,
259 sizeof(superBlock.name) - 1);
260 superBlock.name[sizeof(superBlock.name) - 1] = '\0';
261
262 status = volume->WriteSuperBlock();
263 }
264 return status;
265 }
266
267
268 static status_t
bfs_sync(fs_volume * _volume)269 bfs_sync(fs_volume* _volume)
270 {
271 FUNCTION();
272
273 Volume* volume = (Volume*)_volume->private_volume;
274 return volume->Sync();
275 }
276
277
278 // #pragma mark -
279
280
281 /*! Reads in the node from disk and creates an inode object from it.
282 */
283 static status_t
bfs_get_vnode(fs_volume * _volume,ino_t id,fs_vnode * _node,int * _type,uint32 * _flags,bool reenter)284 bfs_get_vnode(fs_volume* _volume, ino_t id, fs_vnode* _node, int* _type,
285 uint32* _flags, bool reenter)
286 {
287 //FUNCTION_START(("ino_t = %lld\n", id));
288 Volume* volume = (Volume*)_volume->private_volume;
289
290 // first inode may be after the log area, we don't go through
291 // the hassle and try to load an earlier block from disk
292 if (id < volume->ToBlock(volume->Log()) + volume->Log().Length()
293 || id > volume->NumBlocks()) {
294 INFORM(("inode at %" B_PRIdINO " requested!\n", id));
295 return B_ERROR;
296 }
297
298 CachedBlock cached(volume);
299 status_t status = cached.SetTo(id);
300 if (status != B_OK) {
301 FATAL(("could not read inode: %" B_PRIdINO ": %s\n", id,
302 strerror(status)));
303 return status;
304 }
305 bfs_inode* node = (bfs_inode*)cached.Block();
306
307 status = node->InitCheck(volume);
308 if (status != B_OK) {
309 if ((node->Flags() & INODE_DELETED) != 0) {
310 INFORM(("inode at %" B_PRIdINO " is already deleted!\n", id));
311 } else {
312 FATAL(("inode at %" B_PRIdINO " could not be read: %s!\n", id,
313 strerror(status)));
314 }
315 return status;
316 }
317
318 Inode* inode = new(std::nothrow) Inode(volume, id);
319 if (inode == NULL)
320 return B_NO_MEMORY;
321
322 status = inode->InitCheck(false);
323 if (status != B_OK)
324 delete inode;
325
326 if (status == B_OK) {
327 _node->private_node = inode;
328 _node->ops = &gBFSVnodeOps;
329 *_type = inode->Mode();
330 *_flags = 0;
331 }
332
333 return status;
334 }
335
336
337 static status_t
bfs_put_vnode(fs_volume * _volume,fs_vnode * _node,bool reenter)338 bfs_put_vnode(fs_volume* _volume, fs_vnode* _node, bool reenter)
339 {
340 Volume* volume = (Volume*)_volume->private_volume;
341 Inode* inode = (Inode*)_node->private_node;
342
343 // since a directory's size can be changed without having it opened,
344 // we need to take care about their preallocated blocks here
345 if (!volume->IsReadOnly() && !volume->IsCheckingThread()
346 && inode->NeedsTrimming()) {
347 Transaction transaction(volume, inode->BlockNumber());
348
349 if (inode->TrimPreallocation(transaction) == B_OK)
350 transaction.Done();
351 else if (transaction.HasParent()) {
352 // TODO: for now, we don't let sub-transactions fail
353 transaction.Done();
354 }
355 }
356
357 delete inode;
358 return B_OK;
359 }
360
361
362 static status_t
bfs_remove_vnode(fs_volume * _volume,fs_vnode * _node,bool reenter)363 bfs_remove_vnode(fs_volume* _volume, fs_vnode* _node, bool reenter)
364 {
365 FUNCTION();
366
367 Volume* volume = (Volume*)_volume->private_volume;
368 Inode* inode = (Inode*)_node->private_node;
369
370 // If the inode isn't in use anymore, we were called before
371 // bfs_unlink() returns - in this case, we can just use the
372 // transaction which has already deleted the inode.
373 Transaction transaction(volume, volume->ToBlock(inode->Parent()));
374
375 // The file system check functionality uses this flag to prevent the space
376 // used up by the inode from being freed - this flag is set only in
377 // situations where this does not cause any harm as the block bitmap will
378 // get fixed anyway in this case).
379 if ((inode->Flags() & INODE_DONT_FREE_SPACE) != 0) {
380 delete inode;
381 return B_OK;
382 }
383
384 ASSERT((inode->Flags() & INODE_DELETED) != 0);
385
386 status_t status = inode->Free(transaction);
387 if (status == B_OK) {
388 status = transaction.Done();
389 } else if (transaction.HasParent()) {
390 // TODO: for now, we don't let sub-transactions fail
391 status = transaction.Done();
392 }
393
394 volume->RemovedInodes().Remove(inode);
395
396 // TODO: the VFS currently does not allow this to fail
397 delete inode;
398
399 return status;
400 }
401
402
403 static bool
bfs_can_page(fs_volume * _volume,fs_vnode * _v,void * _cookie)404 bfs_can_page(fs_volume* _volume, fs_vnode* _v, void* _cookie)
405 {
406 // TODO: we're obviously not even asked...
407 return false;
408 }
409
410
411 static status_t
bfs_read_pages(fs_volume * _volume,fs_vnode * _node,void * _cookie,off_t pos,const iovec * vecs,size_t count,size_t * _numBytes)412 bfs_read_pages(fs_volume* _volume, fs_vnode* _node, void* _cookie,
413 off_t pos, const iovec* vecs, size_t count, size_t* _numBytes)
414 {
415 Volume* volume = (Volume*)_volume->private_volume;
416 Inode* inode = (Inode*)_node->private_node;
417
418 if (inode->FileCache() == NULL)
419 RETURN_ERROR(B_BAD_VALUE);
420
421 InodeReadLocker _(inode);
422
423 uint32 vecIndex = 0;
424 size_t vecOffset = 0;
425 size_t bytesLeft = *_numBytes;
426 status_t status;
427
428 while (true) {
429 file_io_vec fileVecs[8];
430 size_t fileVecCount = 8;
431
432 status = file_map_translate(inode->Map(), pos, bytesLeft, fileVecs,
433 &fileVecCount, 0);
434 if (status != B_OK && status != B_BUFFER_OVERFLOW)
435 break;
436
437 bool bufferOverflow = status == B_BUFFER_OVERFLOW;
438
439 size_t bytes = bytesLeft;
440 status = read_file_io_vec_pages(volume->Device(), fileVecs,
441 fileVecCount, vecs, count, &vecIndex, &vecOffset, &bytes);
442 if (status != B_OK || !bufferOverflow)
443 break;
444
445 pos += bytes;
446 bytesLeft -= bytes;
447 }
448
449 return status;
450 }
451
452
453 static status_t
bfs_write_pages(fs_volume * _volume,fs_vnode * _node,void * _cookie,off_t pos,const iovec * vecs,size_t count,size_t * _numBytes)454 bfs_write_pages(fs_volume* _volume, fs_vnode* _node, void* _cookie,
455 off_t pos, const iovec* vecs, size_t count, size_t* _numBytes)
456 {
457 Volume* volume = (Volume*)_volume->private_volume;
458 Inode* inode = (Inode*)_node->private_node;
459
460 if (volume->IsReadOnly())
461 return B_READ_ONLY_DEVICE;
462
463 if (inode->FileCache() == NULL)
464 RETURN_ERROR(B_BAD_VALUE);
465
466 InodeReadLocker _(inode);
467
468 uint32 vecIndex = 0;
469 size_t vecOffset = 0;
470 size_t bytesLeft = *_numBytes;
471 status_t status;
472
473 while (true) {
474 file_io_vec fileVecs[8];
475 size_t fileVecCount = 8;
476
477 status = file_map_translate(inode->Map(), pos, bytesLeft, fileVecs,
478 &fileVecCount, 0);
479 if (status != B_OK && status != B_BUFFER_OVERFLOW)
480 break;
481
482 bool bufferOverflow = status == B_BUFFER_OVERFLOW;
483
484 size_t bytes = bytesLeft;
485 status = write_file_io_vec_pages(volume->Device(), fileVecs,
486 fileVecCount, vecs, count, &vecIndex, &vecOffset, &bytes);
487 if (status != B_OK || !bufferOverflow)
488 break;
489
490 pos += bytes;
491 bytesLeft -= bytes;
492 }
493
494 return status;
495 }
496
497
498 static status_t
bfs_io(fs_volume * _volume,fs_vnode * _node,void * _cookie,io_request * request)499 bfs_io(fs_volume* _volume, fs_vnode* _node, void* _cookie, io_request* request)
500 {
501 #if KDEBUG_RW_LOCK_DEBUG
502 // bfs_io depends on read-locks being implicitly transferrable across threads.
503 return B_UNSUPPORTED;
504 #endif
505
506 Volume* volume = (Volume*)_volume->private_volume;
507 Inode* inode = (Inode*)_node->private_node;
508
509 #ifndef FS_SHELL
510 if (io_request_is_write(request) && volume->IsReadOnly()) {
511 notify_io_request(request, B_READ_ONLY_DEVICE);
512 return B_READ_ONLY_DEVICE;
513 }
514 #endif
515
516 if (inode->FileCache() == NULL) {
517 #ifndef FS_SHELL
518 notify_io_request(request, B_BAD_VALUE);
519 #endif
520 RETURN_ERROR(B_BAD_VALUE);
521 }
522
523 // We lock the node here and will unlock it in the "finished" hook.
524 rw_lock_read_lock(&inode->Lock());
525
526 // Due to how I/O request notifications work, it is possible that
527 // some other thread could be notified that the request completed
528 // before we have a chance to release the read lock. We thus need
529 // our own reference to the vnode.
530 acquire_vnode(_volume, inode->ID());
531
532 return do_iterative_fd_io(volume->Device(), request,
533 iterative_io_get_vecs_hook, iterative_io_finished_hook, inode);
534 }
535
536
537 static status_t
bfs_get_file_map(fs_volume * _volume,fs_vnode * _node,off_t offset,size_t size,struct file_io_vec * vecs,size_t * _count)538 bfs_get_file_map(fs_volume* _volume, fs_vnode* _node, off_t offset, size_t size,
539 struct file_io_vec* vecs, size_t* _count)
540 {
541 Volume* volume = (Volume*)_volume->private_volume;
542 Inode* inode = (Inode*)_node->private_node;
543
544 int32 blockShift = volume->BlockShift();
545 uint32 index = 0, max = *_count;
546 block_run run;
547 off_t fileOffset;
548
549 //FUNCTION_START(("offset = %lld, size = %lu\n", offset, size));
550
551 while (true) {
552 status_t status = inode->FindBlockRun(offset, run, fileOffset);
553 if (status != B_OK)
554 return status;
555
556 vecs[index].offset = volume->ToOffset(run) + offset - fileOffset;
557 vecs[index].length = ((uint32)run.Length() << blockShift)
558 - offset + fileOffset;
559
560 // are we already done?
561 if ((uint64)size <= (uint64)vecs[index].length
562 || (uint64)offset + (uint64)vecs[index].length
563 >= (uint64)inode->Size()) {
564 if ((uint64)offset + (uint64)vecs[index].length
565 > (uint64)inode->Size()) {
566 // make sure the extent ends with the last official file
567 // block (without taking any preallocations into account)
568 vecs[index].length = round_up(inode->Size() - offset,
569 volume->BlockSize());
570 }
571 *_count = index + 1;
572 return B_OK;
573 }
574
575 offset += vecs[index].length;
576 size -= vecs[index].length;
577 index++;
578
579 if (index >= max) {
580 // we're out of file_io_vecs; let's bail out
581 *_count = index;
582 return B_BUFFER_OVERFLOW;
583 }
584 }
585
586 // can never get here
587 return B_ERROR;
588 }
589
590
591 // #pragma mark -
592
593
594 static status_t
bfs_lookup(fs_volume * _volume,fs_vnode * _directory,const char * file,ino_t * _vnodeID)595 bfs_lookup(fs_volume* _volume, fs_vnode* _directory, const char* file,
596 ino_t* _vnodeID)
597 {
598 Volume* volume = (Volume*)_volume->private_volume;
599 Inode* directory = (Inode*)_directory->private_node;
600
601 InodeReadLocker locker(directory);
602
603 // check access permissions
604 status_t status = directory->CheckPermissions(X_OK);
605 if (status != B_OK)
606 RETURN_ERROR(status);
607
608 BPlusTree* tree = directory->Tree();
609 if (tree == NULL)
610 RETURN_ERROR(B_BAD_VALUE);
611
612 status = tree->Find((uint8*)file, (uint16)strlen(file), _vnodeID);
613 if (status != B_OK) {
614 //PRINT(("bfs_walk() could not find %lld:\"%s\": %s\n", directory->BlockNumber(), file, strerror(status)));
615 if (status == B_ENTRY_NOT_FOUND)
616 entry_cache_add_missing(volume->ID(), directory->ID(), file);
617
618 return status;
619 }
620
621 entry_cache_add(volume->ID(), directory->ID(), file, *_vnodeID);
622
623 locker.Unlock();
624
625 Inode* inode;
626 status = get_vnode(volume->FSVolume(), *_vnodeID, (void**)&inode);
627 if (status != B_OK) {
628 REPORT_ERROR(status);
629 return B_ENTRY_NOT_FOUND;
630 }
631
632 return B_OK;
633 }
634
635
636 static status_t
bfs_get_vnode_name(fs_volume * _volume,fs_vnode * _node,char * buffer,size_t bufferSize)637 bfs_get_vnode_name(fs_volume* _volume, fs_vnode* _node, char* buffer,
638 size_t bufferSize)
639 {
640 Inode* inode = (Inode*)_node->private_node;
641
642 return inode->GetName(buffer, bufferSize);
643 }
644
645
646 static status_t
bfs_ioctl(fs_volume * _volume,fs_vnode * _node,void * _cookie,uint32 cmd,void * buffer,size_t bufferLength)647 bfs_ioctl(fs_volume* _volume, fs_vnode* _node, void* _cookie, uint32 cmd,
648 void* buffer, size_t bufferLength)
649 {
650 FUNCTION_START(("node = %p, cmd = %" B_PRIu32 ", buf = %p"
651 ", len = %" B_PRIuSIZE "\n", _node, cmd, buffer, bufferLength));
652
653 Volume* volume = (Volume*)_volume->private_volume;
654
655 switch (cmd) {
656 #ifndef FS_SHELL
657 case B_TRIM_DEVICE:
658 {
659 fs_trim_data* trimData;
660 MemoryDeleter deleter;
661 status_t status = get_trim_data_from_user(buffer, bufferLength,
662 deleter, trimData);
663 if (status != B_OK)
664 return status;
665
666 trimData->trimmed_size = 0;
667
668 for (uint32 i = 0; i < trimData->range_count; i++) {
669 uint64 trimmedSize = 0;
670 status_t status = volume->Allocator().Trim(
671 trimData->ranges[i].offset, trimData->ranges[i].size,
672 trimmedSize);
673 if (status != B_OK)
674 return status;
675
676 trimData->trimmed_size += trimmedSize;
677 }
678
679 return copy_trim_data_to_user(buffer, trimData);
680 }
681 #endif
682
683 case BFS_IOCTL_VERSION:
684 {
685 uint32 version = 0x10000;
686 return user_memcpy(buffer, &version, sizeof(uint32));
687 }
688 case BFS_IOCTL_START_CHECKING:
689 {
690 // start checking
691 status_t status = volume->CreateCheckVisitor();
692 if (status != B_OK)
693 return status;
694
695 CheckVisitor* checker = volume->CheckVisitor();
696
697 if (user_memcpy(&checker->Control(), buffer,
698 sizeof(check_control)) != B_OK) {
699 return B_BAD_ADDRESS;
700 }
701
702 status = checker->StartBitmapPass();
703 if (status == B_OK) {
704 file_cookie* cookie = (file_cookie*)_cookie;
705 cookie->open_mode |= BFS_OPEN_MODE_CHECKING;
706 }
707
708 return status;
709 }
710 case BFS_IOCTL_STOP_CHECKING:
711 {
712 // stop checking
713 CheckVisitor* checker = volume->CheckVisitor();
714 if (checker == NULL)
715 return B_NO_INIT;
716
717 status_t status = checker->StopChecking();
718
719 if (status == B_OK) {
720 file_cookie* cookie = (file_cookie*)_cookie;
721 cookie->open_mode &= ~BFS_OPEN_MODE_CHECKING;
722
723 status = user_memcpy(buffer, &checker->Control(),
724 sizeof(check_control));
725 }
726
727 volume->DeleteCheckVisitor();
728 volume->SetCheckingThread(-1);
729
730 return status;
731 }
732 case BFS_IOCTL_CHECK_NEXT_NODE:
733 {
734 // check next
735 CheckVisitor* checker = volume->CheckVisitor();
736 if (checker == NULL)
737 return B_NO_INIT;
738
739 volume->SetCheckingThread(find_thread(NULL));
740
741 checker->Control().errors = 0;
742
743 status_t status = checker->Next();
744 if (status == B_ENTRY_NOT_FOUND) {
745 checker->Control().status = B_ENTRY_NOT_FOUND;
746 // tells StopChecking() that we finished the pass
747
748 if (checker->Pass() == BFS_CHECK_PASS_BITMAP) {
749 if (checker->WriteBackCheckBitmap() == B_OK)
750 status = checker->StartIndexPass();
751 }
752 }
753
754 if (status == B_OK) {
755 status = user_memcpy(buffer, &checker->Control(),
756 sizeof(check_control));
757 }
758
759 return status;
760 }
761 case BFS_IOCTL_UPDATE_BOOT_BLOCK:
762 {
763 // let's makebootable (or anyone else) update the boot block
764 // while BFS is mounted
765 update_boot_block update;
766 if (bufferLength != sizeof(update_boot_block))
767 return B_BAD_VALUE;
768 if (user_memcpy(&update, buffer, sizeof(update_boot_block)) != B_OK)
769 return B_BAD_ADDRESS;
770
771 uint32 minOffset = offsetof(disk_super_block, pad_to_block);
772 if (update.offset < minOffset
773 || update.offset >= 512 || update.length > 512 - minOffset
774 || update.length + update.offset > 512) {
775 return B_BAD_VALUE;
776 }
777 if (user_memcpy((uint8*)&volume->SuperBlock() + update.offset,
778 update.data, update.length) != B_OK) {
779 return B_BAD_ADDRESS;
780 }
781
782 return volume->WriteSuperBlock();
783 }
784 case BFS_IOCTL_RESIZE:
785 {
786 if (bufferLength != sizeof(uint64))
787 return B_BAD_VALUE;
788
789 uint64 size;
790 if (user_memcpy((uint8*)&size, buffer, sizeof(uint64)) != B_OK)
791 return B_BAD_ADDRESS;
792
793 ResizeVisitor resizer(volume);
794 return resizer.Resize(size, -1);
795 }
796
797 #ifdef DEBUG_FRAGMENTER
798 case 56741:
799 {
800 BlockAllocator& allocator = volume->Allocator();
801 allocator.Fragment();
802 return B_OK;
803 }
804 #endif
805
806 #ifdef DEBUG
807 case 56742:
808 {
809 // allocate all free blocks and zero them out
810 // (a test for the BlockAllocator)!
811 BlockAllocator& allocator = volume->Allocator();
812 Transaction transaction(volume, 0);
813 CachedBlock cached(volume);
814 block_run run;
815 while (allocator.AllocateBlocks(transaction, 8, 0, 64, 1, run)
816 == B_OK) {
817 PRINT(("write block_run(%" B_PRId32 ", %" B_PRIu16
818 ", %" B_PRIu16 ")\n", run.allocation_group, run.start,
819 run.length));
820
821 for (int32 i = 0;i < run.length;i++) {
822 status_t status = cached.SetToWritable(transaction, run);
823 if (status == B_OK)
824 memset(cached.WritableBlock(), 0, volume->BlockSize());
825 }
826 }
827 return B_OK;
828 }
829 #endif
830 }
831 return B_DEV_INVALID_IOCTL;
832 }
833
834
835 /*! Sets the open-mode flags for the open file cookie - only
836 supports O_APPEND currently, but that should be sufficient
837 for a file system.
838 */
839 static status_t
bfs_set_flags(fs_volume * _volume,fs_vnode * _node,void * _cookie,int flags)840 bfs_set_flags(fs_volume* _volume, fs_vnode* _node, void* _cookie, int flags)
841 {
842 FUNCTION_START(("node = %p, flags = %d", _node, flags));
843
844 file_cookie* cookie = (file_cookie*)_cookie;
845 cookie->open_mode = (cookie->open_mode & ~O_APPEND) | (flags & O_APPEND);
846
847 return B_OK;
848 }
849
850
851 static status_t
bfs_fsync(fs_volume * _volume,fs_vnode * _node)852 bfs_fsync(fs_volume* _volume, fs_vnode* _node)
853 {
854 FUNCTION();
855
856 Inode* inode = (Inode*)_node->private_node;
857 return inode->Sync();
858 }
859
860
861 static status_t
bfs_read_stat(fs_volume * _volume,fs_vnode * _node,struct stat * stat)862 bfs_read_stat(fs_volume* _volume, fs_vnode* _node, struct stat* stat)
863 {
864 FUNCTION();
865
866 Inode* inode = (Inode*)_node->private_node;
867 fill_stat_buffer(inode, *stat);
868 return B_OK;
869 }
870
871
872 static status_t
bfs_write_stat(fs_volume * _volume,fs_vnode * _node,const struct stat * stat,uint32 mask)873 bfs_write_stat(fs_volume* _volume, fs_vnode* _node, const struct stat* stat,
874 uint32 mask)
875 {
876 FUNCTION();
877
878 Volume* volume = (Volume*)_volume->private_volume;
879 Inode* inode = (Inode*)_node->private_node;
880
881 if (volume->IsReadOnly())
882 return B_READ_ONLY_DEVICE;
883
884 // TODO: we should definitely check a bit more if the new stats are
885 // valid - or even better, the VFS should check this before calling us
886
887 bfs_inode& node = inode->Node();
888 bool updateTime = false;
889 uid_t uid = geteuid();
890
891 bool isOwnerOrRoot = uid == 0 || uid == (uid_t)node.UserID();
892 bool hasWriteAccess = inode->CheckPermissions(W_OK) == B_OK;
893
894 Transaction transaction(volume, inode->BlockNumber());
895 inode->WriteLockInTransaction(transaction);
896
897 if ((mask & B_STAT_SIZE) != 0 && inode->Size() != stat->st_size) {
898 // Since B_STAT_SIZE is the only thing that can fail directly, we
899 // do it first, so that the inode state will still be consistent
900 // with the on-disk version
901 if (inode->IsDirectory())
902 return B_IS_A_DIRECTORY;
903 if (!inode->IsFile())
904 return B_BAD_VALUE;
905 if (!hasWriteAccess)
906 RETURN_ERROR(B_NOT_ALLOWED);
907
908 off_t oldSize = inode->Size();
909
910 status_t status = inode->SetFileSize(transaction, stat->st_size);
911 if (status != B_OK)
912 return status;
913
914 // fill the new blocks (if any) with zeros
915 if ((mask & B_STAT_SIZE_INSECURE) == 0) {
916 // We must not keep the inode locked during a write operation,
917 // or else we might deadlock.
918 rw_lock_write_unlock(&inode->Lock());
919 inode->FillGapWithZeros(oldSize, inode->Size());
920 rw_lock_write_lock(&inode->Lock());
921 }
922
923 if (!inode->IsDeleted()) {
924 Index index(volume);
925 index.UpdateSize(transaction, inode);
926
927 updateTime = true;
928 }
929 }
930
931 if ((mask & B_STAT_UID) != 0) {
932 // only root should be allowed
933 if (uid != 0)
934 RETURN_ERROR(B_NOT_ALLOWED);
935 node.uid = HOST_ENDIAN_TO_BFS_INT32(stat->st_uid);
936 updateTime = true;
937 }
938
939 if ((mask & B_STAT_GID) != 0) {
940 // only the user or root can do that
941 if (!isOwnerOrRoot)
942 RETURN_ERROR(B_NOT_ALLOWED);
943 node.gid = HOST_ENDIAN_TO_BFS_INT32(stat->st_gid);
944 updateTime = true;
945 }
946
947 if ((mask & B_STAT_MODE) != 0) {
948 // only the user or root can do that
949 if (!isOwnerOrRoot)
950 RETURN_ERROR(B_NOT_ALLOWED);
951 PRINT(("original mode = %u, stat->st_mode = %u\n",
952 (unsigned int)node.Mode(), (unsigned int)stat->st_mode));
953 node.mode = HOST_ENDIAN_TO_BFS_INT32((node.Mode() & ~S_IUMSK)
954 | (stat->st_mode & S_IUMSK));
955 updateTime = true;
956 }
957
958 if ((mask & B_STAT_CREATION_TIME) != 0) {
959 // the user or root can do that or any user with write access
960 if (!isOwnerOrRoot && !hasWriteAccess)
961 RETURN_ERROR(B_NOT_ALLOWED);
962 node.create_time
963 = HOST_ENDIAN_TO_BFS_INT64(bfs_inode::ToInode(stat->st_crtim));
964 }
965
966 if ((mask & B_STAT_MODIFICATION_TIME) != 0) {
967 // the user or root can do that or any user with write access
968 if (!isOwnerOrRoot && !hasWriteAccess)
969 RETURN_ERROR(B_NOT_ALLOWED);
970 if (!inode->InLastModifiedIndex()) {
971 // directory modification times are not part of the index
972 node.last_modified_time
973 = HOST_ENDIAN_TO_BFS_INT64(bfs_inode::ToInode(stat->st_mtim));
974 } else if (!inode->IsDeleted()) {
975 // Index::UpdateLastModified() will set the new time in the inode
976 Index index(volume);
977 index.UpdateLastModified(transaction, inode,
978 bfs_inode::ToInode(stat->st_mtim));
979 }
980 }
981
982 if ((mask & B_STAT_CHANGE_TIME) != 0 || updateTime) {
983 // the user or root can do that or any user with write access
984 if (!isOwnerOrRoot && !hasWriteAccess)
985 RETURN_ERROR(B_NOT_ALLOWED);
986 bigtime_t newTime;
987 if ((mask & B_STAT_CHANGE_TIME) == 0)
988 newTime = bfs_inode::ToInode(real_time_clock_usecs());
989 else
990 newTime = bfs_inode::ToInode(stat->st_ctim);
991
992 node.status_change_time = HOST_ENDIAN_TO_BFS_INT64(newTime);
993 }
994
995 status_t status = inode->WriteBack(transaction);
996 if (status == B_OK)
997 status = transaction.Done();
998 if (status == B_OK)
999 notify_stat_changed(volume->ID(), inode->ParentID(), inode->ID(), mask);
1000
1001 return status;
1002 }
1003
1004
1005 status_t
bfs_create(fs_volume * _volume,fs_vnode * _directory,const char * name,int openMode,int mode,void ** _cookie,ino_t * _vnodeID)1006 bfs_create(fs_volume* _volume, fs_vnode* _directory, const char* name,
1007 int openMode, int mode, void** _cookie, ino_t* _vnodeID)
1008 {
1009 FUNCTION_START(("name = \"%s\", perms = %d, openMode = %d\n", name, mode,
1010 openMode));
1011
1012 Volume* volume = (Volume*)_volume->private_volume;
1013 Inode* directory = (Inode*)_directory->private_node;
1014
1015 if (volume->IsReadOnly())
1016 return B_READ_ONLY_DEVICE;
1017
1018 if (!directory->IsDirectory())
1019 RETURN_ERROR(B_BAD_TYPE);
1020
1021 // We are creating the cookie at this point, so that we don't have
1022 // to remove the inode if we don't have enough free memory later...
1023 file_cookie* cookie = new(std::nothrow) file_cookie;
1024 if (cookie == NULL)
1025 RETURN_ERROR(B_NO_MEMORY);
1026
1027 // initialize the cookie
1028 cookie->open_mode = openMode;
1029 cookie->last_size = 0;
1030 cookie->last_notification = system_time();
1031
1032 Transaction transaction(volume, directory->BlockNumber());
1033
1034 Inode* inode;
1035 bool created;
1036 status_t status = Inode::Create(transaction, directory, name,
1037 S_FILE | (mode & S_IUMSK), openMode, 0, &created, _vnodeID, &inode);
1038
1039 // Disable the file cache, if requested?
1040 if (status == B_OK && (openMode & O_NOCACHE) != 0
1041 && inode->FileCache() != NULL) {
1042 status = file_cache_disable(inode->FileCache());
1043 }
1044
1045 entry_cache_add(volume->ID(), directory->ID(), name, *_vnodeID);
1046
1047 if (status == B_OK)
1048 status = transaction.Done();
1049
1050 if (status == B_OK) {
1051 // register the cookie
1052 *_cookie = cookie;
1053
1054 if (created) {
1055 notify_entry_created(volume->ID(), directory->ID(), name,
1056 *_vnodeID);
1057 }
1058 } else {
1059 entry_cache_remove(volume->ID(), directory->ID(), name);
1060 delete cookie;
1061 }
1062
1063 return status;
1064 }
1065
1066
1067 static status_t
bfs_create_symlink(fs_volume * _volume,fs_vnode * _directory,const char * name,const char * path,int mode)1068 bfs_create_symlink(fs_volume* _volume, fs_vnode* _directory, const char* name,
1069 const char* path, int mode)
1070 {
1071 FUNCTION_START(("name = \"%s\", path = \"%s\"\n", name, path));
1072
1073 Volume* volume = (Volume*)_volume->private_volume;
1074 Inode* directory = (Inode*)_directory->private_node;
1075
1076 if (volume->IsReadOnly())
1077 return B_READ_ONLY_DEVICE;
1078
1079 if (!directory->IsDirectory())
1080 RETURN_ERROR(B_BAD_TYPE);
1081
1082 status_t status = directory->CheckPermissions(W_OK);
1083 if (status < B_OK)
1084 RETURN_ERROR(status);
1085
1086 Transaction transaction(volume, directory->BlockNumber());
1087
1088 Inode* link;
1089 off_t id;
1090 status = Inode::Create(transaction, directory, name, S_SYMLINK | 0777,
1091 0, 0, NULL, &id, &link);
1092 if (status < B_OK)
1093 RETURN_ERROR(status);
1094
1095 size_t length = strlen(path);
1096 if (length < SHORT_SYMLINK_NAME_LENGTH) {
1097 strcpy(link->Node().short_symlink, path);
1098 } else {
1099 link->Node().flags |= HOST_ENDIAN_TO_BFS_INT32(INODE_LONG_SYMLINK
1100 | INODE_LOGGED);
1101
1102 // links usually don't have a file cache attached - but we now need one
1103 link->SetFileCache(file_cache_create(volume->ID(), link->ID(), 0));
1104 link->SetMap(file_map_create(volume->ID(), link->ID(), 0));
1105
1106 // The following call will have to write the inode back, so
1107 // we don't have to do that here...
1108 status = link->WriteAt(transaction, 0, (const uint8*)path, &length);
1109 }
1110
1111 if (status == B_OK)
1112 status = link->WriteBack(transaction);
1113
1114 // Inode::Create() left the inode locked in memory, and also doesn't
1115 // publish links
1116 publish_vnode(volume->FSVolume(), id, link, &gBFSVnodeOps, link->Mode(), 0);
1117 put_vnode(volume->FSVolume(), id);
1118
1119 if (status == B_OK) {
1120 entry_cache_add(volume->ID(), directory->ID(), name, id);
1121
1122 status = transaction.Done();
1123 if (status == B_OK)
1124 notify_entry_created(volume->ID(), directory->ID(), name, id);
1125 else
1126 entry_cache_remove(volume->ID(), directory->ID(), name);
1127 }
1128
1129 return status;
1130 }
1131
1132
1133 status_t
bfs_link(fs_volume * _volume,fs_vnode * dir,const char * name,fs_vnode * node)1134 bfs_link(fs_volume* _volume, fs_vnode* dir, const char* name, fs_vnode* node)
1135 {
1136 FUNCTION_START(("name = \"%s\"\n", name));
1137
1138 // This one won't be implemented in a binary compatible BFS
1139 return B_UNSUPPORTED;
1140 }
1141
1142
1143 status_t
bfs_unlink(fs_volume * _volume,fs_vnode * _directory,const char * name)1144 bfs_unlink(fs_volume* _volume, fs_vnode* _directory, const char* name)
1145 {
1146 FUNCTION_START(("name = \"%s\"\n", name));
1147
1148 if (!strcmp(name, "..") || !strcmp(name, "."))
1149 return B_NOT_ALLOWED;
1150
1151 Volume* volume = (Volume*)_volume->private_volume;
1152 Inode* directory = (Inode*)_directory->private_node;
1153
1154 status_t status = directory->CheckPermissions(W_OK);
1155 if (status < B_OK)
1156 return status;
1157
1158 Transaction transaction(volume, directory->BlockNumber());
1159
1160 off_t id;
1161 status = directory->Remove(transaction, name, &id);
1162 if (status == B_OK) {
1163 entry_cache_remove(volume->ID(), directory->ID(), name);
1164
1165 status = transaction.Done();
1166 if (status == B_OK)
1167 notify_entry_removed(volume->ID(), directory->ID(), name, id);
1168 else
1169 entry_cache_add(volume->ID(), directory->ID(), name, id);
1170 }
1171 return status;
1172 }
1173
1174
1175 status_t
bfs_rename(fs_volume * _volume,fs_vnode * _oldDir,const char * oldName,fs_vnode * _newDir,const char * newName)1176 bfs_rename(fs_volume* _volume, fs_vnode* _oldDir, const char* oldName,
1177 fs_vnode* _newDir, const char* newName)
1178 {
1179 FUNCTION_START(("oldDir = %p, oldName = \"%s\", newDir = %p, newName = "
1180 "\"%s\"\n", _oldDir, oldName, _newDir, newName));
1181
1182 Volume* volume = (Volume*)_volume->private_volume;
1183 Inode* oldDirectory = (Inode*)_oldDir->private_node;
1184 Inode* newDirectory = (Inode*)_newDir->private_node;
1185
1186 // are we already done?
1187 if (oldDirectory == newDirectory && !strcmp(oldName, newName))
1188 return B_OK;
1189
1190 Transaction transaction(volume, oldDirectory->BlockNumber());
1191
1192 oldDirectory->WriteLockInTransaction(transaction);
1193 if (oldDirectory != newDirectory)
1194 newDirectory->WriteLockInTransaction(transaction);
1195
1196 // are we allowed to do what we've been told?
1197 status_t status = oldDirectory->CheckPermissions(W_OK);
1198 if (status == B_OK)
1199 status = newDirectory->CheckPermissions(W_OK);
1200 if (status != B_OK)
1201 return status;
1202
1203 // Get the directory's tree, and a pointer to the inode which should be
1204 // changed
1205 BPlusTree* tree = oldDirectory->Tree();
1206 if (tree == NULL)
1207 RETURN_ERROR(B_BAD_VALUE);
1208
1209 off_t id;
1210 status = tree->Find((const uint8*)oldName, strlen(oldName), &id);
1211 if (status != B_OK)
1212 RETURN_ERROR(status);
1213
1214 Vnode vnode(volume, id);
1215 Inode* inode;
1216 if (vnode.Get(&inode) != B_OK)
1217 return B_IO_ERROR;
1218
1219 // Don't move a directory into one of its children - we soar up
1220 // from the newDirectory to either the root node or the old
1221 // directory, whichever comes first.
1222 // If we meet our inode on that way, we have to bail out.
1223
1224 if (oldDirectory != newDirectory) {
1225 ino_t parent = newDirectory->ID();
1226 ino_t root = volume->RootNode()->ID();
1227
1228 while (true) {
1229 if (parent == id)
1230 return B_BAD_VALUE;
1231 else if (parent == root || parent == oldDirectory->ID())
1232 break;
1233
1234 Vnode vnode(volume, parent);
1235 Inode* parentNode;
1236 if (vnode.Get(&parentNode) != B_OK)
1237 return B_ERROR;
1238
1239 parent = volume->ToVnode(parentNode->Parent());
1240 }
1241 }
1242
1243 // Everything okay? Then lets get to work...
1244
1245 // First, try to make sure there is nothing that will stop us in
1246 // the target directory - since this is the only non-critical
1247 // failure, we will test this case first
1248 BPlusTree* newTree = tree;
1249 if (newDirectory != oldDirectory) {
1250 newTree = newDirectory->Tree();
1251 if (newTree == NULL)
1252 RETURN_ERROR(B_BAD_VALUE);
1253 }
1254
1255 status = newTree->Insert(transaction, (const uint8*)newName,
1256 strlen(newName), id);
1257 if (status == B_NAME_IN_USE) {
1258 // If there is already a file with that name, we have to remove
1259 // it, as long it's not a directory with files in it
1260 off_t clobber;
1261 if (newTree->Find((const uint8*)newName, strlen(newName), &clobber)
1262 < B_OK)
1263 return B_NAME_IN_USE;
1264 if (clobber == id)
1265 return B_BAD_VALUE;
1266
1267 Vnode vnode(volume, clobber);
1268 Inode* other;
1269 if (vnode.Get(&other) < B_OK)
1270 return B_NAME_IN_USE;
1271
1272 // only allowed, if either both nodes are directories or neither is
1273 if (inode->IsDirectory() != other->IsDirectory())
1274 return other->IsDirectory() ? B_IS_A_DIRECTORY : B_NOT_A_DIRECTORY;
1275
1276 status = newDirectory->Remove(transaction, newName, NULL,
1277 other->IsDirectory());
1278 if (status < B_OK)
1279 return status;
1280
1281 entry_cache_remove(volume->ID(), newDirectory->ID(), newName);
1282
1283 notify_entry_removed(volume->ID(), newDirectory->ID(), newName,
1284 clobber);
1285
1286 status = newTree->Insert(transaction, (const uint8*)newName,
1287 strlen(newName), id);
1288 }
1289 if (status != B_OK)
1290 return status;
1291
1292 inode->WriteLockInTransaction(transaction);
1293
1294 volume->UpdateLiveQueriesRenameMove(inode, oldDirectory->ID(), oldName,
1295 newDirectory->ID(), newName);
1296
1297 // update the name only when they differ
1298 if (strcmp(oldName, newName)) {
1299 status = inode->SetName(transaction, newName);
1300 if (status == B_OK) {
1301 Index index(volume);
1302 index.UpdateName(transaction, oldName, newName, inode);
1303 }
1304 }
1305
1306 if (status == B_OK) {
1307 status = tree->Remove(transaction, (const uint8*)oldName,
1308 strlen(oldName), id);
1309 if (status == B_OK) {
1310 inode->Parent() = newDirectory->BlockRun();
1311
1312 // if it's a directory, update the parent directory pointer
1313 // in its tree if necessary
1314 BPlusTree* movedTree = inode->Tree();
1315 if (oldDirectory != newDirectory
1316 && inode->IsDirectory()
1317 && movedTree != NULL) {
1318 status = movedTree->Replace(transaction, (const uint8*)"..",
1319 2, newDirectory->ID());
1320
1321 if (status == B_OK) {
1322 // update/add the cache entry for the parent
1323 entry_cache_add(volume->ID(), id, "..", newDirectory->ID());
1324 }
1325 }
1326
1327 if (status == B_OK && newDirectory != oldDirectory)
1328 status = oldDirectory->ContainerContentsChanged(transaction);
1329 if (status == B_OK)
1330 status = newDirectory->ContainerContentsChanged(transaction);
1331
1332 if (status == B_OK)
1333 status = inode->WriteBack(transaction);
1334
1335 if (status == B_OK) {
1336 entry_cache_remove(volume->ID(), oldDirectory->ID(), oldName);
1337 entry_cache_add(volume->ID(), newDirectory->ID(), newName, id);
1338
1339 status = transaction.Done();
1340 if (status == B_OK) {
1341 notify_entry_moved(volume->ID(), oldDirectory->ID(),
1342 oldName, newDirectory->ID(), newName, id);
1343 return B_OK;
1344 }
1345
1346 entry_cache_remove(volume->ID(), newDirectory->ID(), newName);
1347 entry_cache_add(volume->ID(), oldDirectory->ID(), oldName, id);
1348 }
1349 }
1350 }
1351
1352 return status;
1353 }
1354
1355
1356 static status_t
bfs_open(fs_volume * _volume,fs_vnode * _node,int openMode,void ** _cookie)1357 bfs_open(fs_volume* _volume, fs_vnode* _node, int openMode, void** _cookie)
1358 {
1359 FUNCTION();
1360
1361 Volume* volume = (Volume*)_volume->private_volume;
1362 Inode* inode = (Inode*)_node->private_node;
1363
1364 // Opening a directory read-only is allowed, although you can't read
1365 // any data from it.
1366 if (inode->IsDirectory() && (openMode & O_RWMASK) != O_RDONLY)
1367 return B_IS_A_DIRECTORY;
1368 if ((openMode & O_DIRECTORY) != 0 && !inode->IsDirectory())
1369 return B_NOT_A_DIRECTORY;
1370
1371 status_t status = inode->CheckPermissions(open_mode_to_access(openMode)
1372 | ((openMode & O_TRUNC) != 0 ? W_OK : 0));
1373 if (status != B_OK)
1374 RETURN_ERROR(status);
1375
1376 file_cookie* cookie = new(std::nothrow) file_cookie;
1377 if (cookie == NULL)
1378 RETURN_ERROR(B_NO_MEMORY);
1379 ObjectDeleter<file_cookie> cookieDeleter(cookie);
1380
1381 // initialize the cookie
1382 cookie->open_mode = openMode & BFS_OPEN_MODE_USER_MASK;
1383 cookie->last_size = inode->Size();
1384 cookie->last_notification = system_time();
1385
1386 // Disable the file cache, if requested?
1387 CObjectDeleter<void, void, file_cache_enable> fileCacheEnabler;
1388 if ((openMode & O_NOCACHE) != 0 && inode->FileCache() != NULL) {
1389 status = file_cache_disable(inode->FileCache());
1390 if (status != B_OK)
1391 return status;
1392 fileCacheEnabler.SetTo(inode->FileCache());
1393 }
1394
1395 // Should we truncate the file?
1396 if ((openMode & O_TRUNC) != 0) {
1397 if ((openMode & O_RWMASK) == O_RDONLY)
1398 return B_NOT_ALLOWED;
1399
1400 Transaction transaction(volume, inode->BlockNumber());
1401 inode->WriteLockInTransaction(transaction);
1402
1403 status_t status = inode->SetFileSize(transaction, 0);
1404 if (status == B_OK)
1405 status = inode->WriteBack(transaction);
1406 if (status == B_OK)
1407 status = transaction.Done();
1408 if (status != B_OK)
1409 return status;
1410 }
1411
1412 fileCacheEnabler.Detach();
1413 cookieDeleter.Detach();
1414 *_cookie = cookie;
1415 return B_OK;
1416 }
1417
1418
1419 static status_t
bfs_read(fs_volume * _volume,fs_vnode * _node,void * _cookie,off_t pos,void * buffer,size_t * _length)1420 bfs_read(fs_volume* _volume, fs_vnode* _node, void* _cookie, off_t pos,
1421 void* buffer, size_t* _length)
1422 {
1423 //FUNCTION();
1424 Inode* inode = (Inode*)_node->private_node;
1425
1426 if (!inode->HasUserAccessableStream()) {
1427 *_length = 0;
1428 return inode->IsDirectory() ? B_IS_A_DIRECTORY : B_BAD_VALUE;
1429 }
1430
1431 return inode->ReadAt(pos, (uint8*)buffer, _length);
1432 }
1433
1434
1435 static status_t
bfs_write(fs_volume * _volume,fs_vnode * _node,void * _cookie,off_t pos,const void * buffer,size_t * _length)1436 bfs_write(fs_volume* _volume, fs_vnode* _node, void* _cookie, off_t pos,
1437 const void* buffer, size_t* _length)
1438 {
1439 //FUNCTION();
1440 Volume* volume = (Volume*)_volume->private_volume;
1441 Inode* inode = (Inode*)_node->private_node;
1442
1443 if (volume->IsReadOnly())
1444 return B_READ_ONLY_DEVICE;
1445
1446 if (!inode->HasUserAccessableStream()) {
1447 *_length = 0;
1448 return inode->IsDirectory() ? B_IS_A_DIRECTORY : B_BAD_VALUE;
1449 }
1450
1451 file_cookie* cookie = (file_cookie*)_cookie;
1452
1453 if (cookie->open_mode & O_APPEND)
1454 pos = inode->Size();
1455
1456 Transaction transaction;
1457 // We are not starting the transaction here, since
1458 // it might not be needed at all (the contents of
1459 // regular files aren't logged)
1460
1461 status_t status = inode->WriteAt(transaction, pos, (const uint8*)buffer,
1462 _length);
1463 if (status == B_OK)
1464 status = transaction.Done();
1465 if (status == B_OK) {
1466 InodeReadLocker locker(inode);
1467
1468 // periodically notify if the file size has changed
1469 // TODO: should we better test for a change in the last_modified time only?
1470 if (!inode->IsDeleted() && cookie->last_size != inode->Size()
1471 && system_time() > cookie->last_notification
1472 + INODE_NOTIFICATION_INTERVAL) {
1473 notify_stat_changed(volume->ID(), inode->ParentID(), inode->ID(),
1474 B_STAT_MODIFICATION_TIME | B_STAT_SIZE | B_STAT_INTERIM_UPDATE);
1475 cookie->last_size = inode->Size();
1476 cookie->last_notification = system_time();
1477 }
1478 }
1479
1480 return status;
1481 }
1482
1483
1484 static status_t
bfs_close(fs_volume * _volume,fs_vnode * _node,void * _cookie)1485 bfs_close(fs_volume* _volume, fs_vnode* _node, void* _cookie)
1486 {
1487 FUNCTION();
1488 return B_OK;
1489 }
1490
1491
1492 static status_t
bfs_free_cookie(fs_volume * _volume,fs_vnode * _node,void * _cookie)1493 bfs_free_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie)
1494 {
1495 FUNCTION();
1496
1497 file_cookie* cookie = (file_cookie*)_cookie;
1498 Volume* volume = (Volume*)_volume->private_volume;
1499 Inode* inode = (Inode*)_node->private_node;
1500
1501 Transaction transaction;
1502 bool needsTrimming = false;
1503
1504 if (!volume->IsReadOnly() && !volume->IsCheckingThread()) {
1505 InodeReadLocker locker(inode);
1506 needsTrimming = inode->NeedsTrimming();
1507
1508 if ((cookie->open_mode & O_RWMASK) != 0
1509 && !inode->IsDeleted()
1510 && (needsTrimming
1511 || inode->OldLastModified() != inode->LastModified()
1512 || (inode->InSizeIndex()
1513 // TODO: this can prevent the size update notification
1514 // for nodes not in the index!
1515 && inode->OldSize() != inode->Size()))) {
1516 locker.Unlock();
1517 transaction.Start(volume, inode->BlockNumber());
1518 }
1519 }
1520
1521 status_t status = transaction.IsStarted() ? B_OK : B_ERROR;
1522
1523 if (status == B_OK) {
1524 inode->WriteLockInTransaction(transaction);
1525
1526 // trim the preallocated blocks and update the size,
1527 // and last_modified indices if needed
1528 bool changedSize = false, changedTime = false;
1529 Index index(volume);
1530
1531 if (needsTrimming) {
1532 status = inode->TrimPreallocation(transaction);
1533 if (status < B_OK) {
1534 FATAL(("Could not trim preallocated blocks: inode %" B_PRIdINO
1535 ", transaction %d: %s!\n", inode->ID(),
1536 (int)transaction.ID(), strerror(status)));
1537
1538 // we still want this transaction to succeed
1539 status = B_OK;
1540 }
1541 }
1542 if (inode->OldSize() != inode->Size()) {
1543 if (inode->InSizeIndex())
1544 index.UpdateSize(transaction, inode);
1545 changedSize = true;
1546 }
1547 if (inode->OldLastModified() != inode->LastModified()) {
1548 if (inode->InLastModifiedIndex()) {
1549 index.UpdateLastModified(transaction, inode,
1550 inode->LastModified());
1551 }
1552 changedTime = true;
1553
1554 // updating the index doesn't write back the inode
1555 inode->WriteBack(transaction);
1556 }
1557
1558 if (changedSize || changedTime) {
1559 notify_stat_changed(volume->ID(), inode->ParentID(), inode->ID(),
1560 (changedTime ? B_STAT_MODIFICATION_TIME : 0)
1561 | (changedSize ? B_STAT_SIZE : 0));
1562 }
1563 }
1564 if (status == B_OK)
1565 transaction.Done();
1566
1567 if ((cookie->open_mode & BFS_OPEN_MODE_CHECKING) != 0) {
1568 // "chkbfs" exited abnormally, so we have to stop it here...
1569 FATAL(("check process was aborted!\n"));
1570 volume->CheckVisitor()->StopChecking();
1571 volume->DeleteCheckVisitor();
1572 }
1573
1574 if ((cookie->open_mode & O_NOCACHE) != 0 && inode->FileCache() != NULL)
1575 file_cache_enable(inode->FileCache());
1576
1577 delete cookie;
1578 return B_OK;
1579 }
1580
1581
1582 /*! Checks access permissions, return B_NOT_ALLOWED if the action
1583 is not allowed.
1584 */
1585 static status_t
bfs_access(fs_volume * _volume,fs_vnode * _node,int accessMode)1586 bfs_access(fs_volume* _volume, fs_vnode* _node, int accessMode)
1587 {
1588 //FUNCTION();
1589
1590 Inode* inode = (Inode*)_node->private_node;
1591 status_t status = inode->CheckPermissions(accessMode);
1592 if (status < B_OK)
1593 RETURN_ERROR(status);
1594
1595 return B_OK;
1596 }
1597
1598
1599 static status_t
bfs_read_link(fs_volume * _volume,fs_vnode * _node,char * buffer,size_t * _bufferSize)1600 bfs_read_link(fs_volume* _volume, fs_vnode* _node, char* buffer,
1601 size_t* _bufferSize)
1602 {
1603 FUNCTION();
1604
1605 Inode* inode = (Inode*)_node->private_node;
1606
1607 if (!inode->IsSymLink())
1608 RETURN_ERROR(B_BAD_VALUE);
1609
1610 if ((inode->Flags() & INODE_LONG_SYMLINK) != 0) {
1611 status_t status = inode->ReadAt(0, (uint8*)buffer, _bufferSize);
1612 if (status < B_OK)
1613 RETURN_ERROR(status);
1614
1615 *_bufferSize = inode->Size();
1616 return B_OK;
1617 }
1618
1619 size_t linkLength = strlen(inode->Node().short_symlink);
1620
1621 size_t bytesToCopy = min_c(linkLength, *_bufferSize);
1622
1623 *_bufferSize = linkLength;
1624
1625 memcpy(buffer, inode->Node().short_symlink, bytesToCopy);
1626 return B_OK;
1627 }
1628
1629
1630 // #pragma mark - Directory functions
1631
1632
1633 static status_t
bfs_create_dir(fs_volume * _volume,fs_vnode * _directory,const char * name,int mode)1634 bfs_create_dir(fs_volume* _volume, fs_vnode* _directory, const char* name,
1635 int mode)
1636 {
1637 FUNCTION_START(("name = \"%s\", perms = %d\n", name, mode));
1638
1639 Volume* volume = (Volume*)_volume->private_volume;
1640 Inode* directory = (Inode*)_directory->private_node;
1641
1642 if (volume->IsReadOnly())
1643 return B_READ_ONLY_DEVICE;
1644
1645 if (!directory->IsDirectory())
1646 RETURN_ERROR(B_BAD_TYPE);
1647
1648 status_t status = directory->CheckPermissions(W_OK);
1649 if (status < B_OK)
1650 RETURN_ERROR(status);
1651
1652 Transaction transaction(volume, directory->BlockNumber());
1653
1654 // Inode::Create() locks the inode if we pass the "id" parameter, but we
1655 // need it anyway
1656 off_t id;
1657 status = Inode::Create(transaction, directory, name,
1658 S_DIRECTORY | (mode & S_IUMSK), 0, 0, NULL, &id);
1659 if (status == B_OK) {
1660 put_vnode(volume->FSVolume(), id);
1661
1662 entry_cache_add(volume->ID(), directory->ID(), name, id);
1663
1664 status = transaction.Done();
1665 if (status == B_OK)
1666 notify_entry_created(volume->ID(), directory->ID(), name, id);
1667 else
1668 entry_cache_remove(volume->ID(), directory->ID(), name);
1669 }
1670
1671 return status;
1672 }
1673
1674
1675 static status_t
bfs_remove_dir(fs_volume * _volume,fs_vnode * _directory,const char * name)1676 bfs_remove_dir(fs_volume* _volume, fs_vnode* _directory, const char* name)
1677 {
1678 FUNCTION_START(("name = \"%s\"\n", name));
1679
1680 Volume* volume = (Volume*)_volume->private_volume;
1681 Inode* directory = (Inode*)_directory->private_node;
1682
1683 Transaction transaction(volume, directory->BlockNumber());
1684
1685 off_t id;
1686 status_t status = directory->Remove(transaction, name, &id, true);
1687 if (status == B_OK) {
1688 // Remove the cache entry for the directory and potentially also
1689 // the parent entry still belonging to the directory
1690 entry_cache_remove(volume->ID(), directory->ID(), name);
1691 entry_cache_remove(volume->ID(), id, "..");
1692
1693 status = transaction.Done();
1694 if (status == B_OK)
1695 notify_entry_removed(volume->ID(), directory->ID(), name, id);
1696 else {
1697 entry_cache_add(volume->ID(), directory->ID(), name, id);
1698 entry_cache_add(volume->ID(), id, "..", id);
1699 }
1700 }
1701
1702 return status;
1703 }
1704
1705
1706 /*! Opens a directory ready to be traversed.
1707 bfs_open_dir() is also used by bfs_open_index_dir().
1708 */
1709 static status_t
bfs_open_dir(fs_volume * _volume,fs_vnode * _node,void ** _cookie)1710 bfs_open_dir(fs_volume* _volume, fs_vnode* _node, void** _cookie)
1711 {
1712 FUNCTION();
1713
1714 Inode* inode = (Inode*)_node->private_node;
1715 status_t status = inode->CheckPermissions(R_OK);
1716 if (status < B_OK)
1717 RETURN_ERROR(status);
1718
1719 // we don't ask here for directories only, because the bfs_open_index_dir()
1720 // function utilizes us (so we must be able to open indices as well)
1721 if (!inode->IsContainer())
1722 RETURN_ERROR(B_NOT_A_DIRECTORY);
1723
1724 BPlusTree* tree = inode->Tree();
1725 if (tree == NULL)
1726 RETURN_ERROR(B_BAD_VALUE);
1727
1728 TreeIterator* iterator = new(std::nothrow) TreeIterator(tree);
1729 if (iterator == NULL)
1730 RETURN_ERROR(B_NO_MEMORY);
1731
1732 *_cookie = iterator;
1733 return B_OK;
1734 }
1735
1736
1737 static status_t
bfs_read_dir(fs_volume * _volume,fs_vnode * _node,void * _cookie,struct dirent * dirent,size_t bufferSize,uint32 * _num)1738 bfs_read_dir(fs_volume* _volume, fs_vnode* _node, void* _cookie,
1739 struct dirent* dirent, size_t bufferSize, uint32* _num)
1740 {
1741 FUNCTION();
1742
1743 TreeIterator* iterator = (TreeIterator*)_cookie;
1744 Volume* volume = (Volume*)_volume->private_volume;
1745
1746 uint32 maxCount = *_num;
1747 uint32 count = 0;
1748
1749 while (count < maxCount && bufferSize > sizeof(struct dirent)) {
1750 ino_t id;
1751 uint16 length;
1752 size_t nameBufferSize = bufferSize - offsetof(struct dirent, d_name);
1753
1754 status_t status = iterator->GetNextEntry(dirent->d_name, &length,
1755 nameBufferSize, &id);
1756
1757 if (status == B_ENTRY_NOT_FOUND)
1758 break;
1759
1760 if (status == B_BUFFER_OVERFLOW) {
1761 // the remaining name buffer length was too small
1762 if (count == 0)
1763 RETURN_ERROR(B_BUFFER_OVERFLOW);
1764 break;
1765 }
1766
1767 if (status != B_OK)
1768 RETURN_ERROR(status);
1769
1770 dirent->d_dev = volume->ID();
1771 dirent->d_ino = id;
1772
1773 dirent = next_dirent(dirent, length, bufferSize);
1774 count++;
1775 }
1776
1777 *_num = count;
1778 return B_OK;
1779 }
1780
1781
1782 /*! Sets the TreeIterator back to the beginning of the directory. */
1783 static status_t
bfs_rewind_dir(fs_volume *,fs_vnode *,void * _cookie)1784 bfs_rewind_dir(fs_volume* /*_volume*/, fs_vnode* /*node*/, void* _cookie)
1785 {
1786 FUNCTION();
1787 TreeIterator* iterator = (TreeIterator*)_cookie;
1788
1789 return iterator->Rewind();
1790 }
1791
1792
1793 static status_t
bfs_close_dir(fs_volume *,fs_vnode *,void *)1794 bfs_close_dir(fs_volume* /*_volume*/, fs_vnode* /*node*/, void* /*_cookie*/)
1795 {
1796 FUNCTION();
1797 return B_OK;
1798 }
1799
1800
1801 static status_t
bfs_free_dir_cookie(fs_volume * _volume,fs_vnode * node,void * _cookie)1802 bfs_free_dir_cookie(fs_volume* _volume, fs_vnode* node, void* _cookie)
1803 {
1804 delete (TreeIterator*)_cookie;
1805 return B_OK;
1806 }
1807
1808
1809 // #pragma mark - Attribute functions
1810
1811
1812 static status_t
bfs_open_attr_dir(fs_volume * _volume,fs_vnode * _node,void ** _cookie)1813 bfs_open_attr_dir(fs_volume* _volume, fs_vnode* _node, void** _cookie)
1814 {
1815 Inode* inode = (Inode*)_node->private_node;
1816
1817 FUNCTION();
1818
1819 AttributeIterator* iterator = new(std::nothrow) AttributeIterator(inode);
1820 if (iterator == NULL)
1821 RETURN_ERROR(B_NO_MEMORY);
1822
1823 *_cookie = iterator;
1824 return B_OK;
1825 }
1826
1827
1828 static status_t
bfs_close_attr_dir(fs_volume * _volume,fs_vnode * node,void * cookie)1829 bfs_close_attr_dir(fs_volume* _volume, fs_vnode* node, void* cookie)
1830 {
1831 FUNCTION();
1832 return B_OK;
1833 }
1834
1835
1836 static status_t
bfs_free_attr_dir_cookie(fs_volume * _volume,fs_vnode * node,void * _cookie)1837 bfs_free_attr_dir_cookie(fs_volume* _volume, fs_vnode* node, void* _cookie)
1838 {
1839 FUNCTION();
1840 AttributeIterator* iterator = (AttributeIterator*)_cookie;
1841
1842 delete iterator;
1843 return B_OK;
1844 }
1845
1846
1847 static status_t
bfs_rewind_attr_dir(fs_volume * _volume,fs_vnode * _node,void * _cookie)1848 bfs_rewind_attr_dir(fs_volume* _volume, fs_vnode* _node, void* _cookie)
1849 {
1850 FUNCTION();
1851
1852 AttributeIterator* iterator = (AttributeIterator*)_cookie;
1853 RETURN_ERROR(iterator->Rewind());
1854 }
1855
1856
1857 static status_t
bfs_read_attr_dir(fs_volume * _volume,fs_vnode * node,void * _cookie,struct dirent * dirent,size_t bufferSize,uint32 * _num)1858 bfs_read_attr_dir(fs_volume* _volume, fs_vnode* node, void* _cookie,
1859 struct dirent* dirent, size_t bufferSize, uint32* _num)
1860 {
1861 FUNCTION();
1862 AttributeIterator* iterator = (AttributeIterator*)_cookie;
1863
1864 uint32 type;
1865 size_t length;
1866 status_t status = iterator->GetNext(dirent->d_name, &length, &type,
1867 &dirent->d_ino);
1868 if (status == B_ENTRY_NOT_FOUND) {
1869 *_num = 0;
1870 return B_OK;
1871 } else if (status != B_OK) {
1872 RETURN_ERROR(status);
1873 }
1874
1875 Volume* volume = (Volume*)_volume->private_volume;
1876
1877 dirent->d_dev = volume->ID();
1878 dirent->d_reclen = offsetof(struct dirent, d_name) + length + 1;
1879
1880 *_num = 1;
1881 return B_OK;
1882 }
1883
1884
1885 static status_t
bfs_create_attr(fs_volume * _volume,fs_vnode * _node,const char * name,uint32 type,int openMode,void ** _cookie)1886 bfs_create_attr(fs_volume* _volume, fs_vnode* _node, const char* name,
1887 uint32 type, int openMode, void** _cookie)
1888 {
1889 FUNCTION();
1890
1891 Volume* volume = (Volume*)_volume->private_volume;
1892 if (volume->IsReadOnly())
1893 return B_READ_ONLY_DEVICE;
1894
1895 Inode* inode = (Inode*)_node->private_node;
1896 Attribute attribute(inode);
1897
1898 return attribute.Create(name, type, openMode, (attr_cookie**)_cookie);
1899 }
1900
1901
1902 static status_t
bfs_open_attr(fs_volume * _volume,fs_vnode * _node,const char * name,int openMode,void ** _cookie)1903 bfs_open_attr(fs_volume* _volume, fs_vnode* _node, const char* name,
1904 int openMode, void** _cookie)
1905 {
1906 FUNCTION();
1907
1908 Inode* inode = (Inode*)_node->private_node;
1909 Attribute attribute(inode);
1910
1911 return attribute.Open(name, openMode, (attr_cookie**)_cookie);
1912 }
1913
1914
1915 static status_t
bfs_close_attr(fs_volume * _volume,fs_vnode * _file,void * cookie)1916 bfs_close_attr(fs_volume* _volume, fs_vnode* _file, void* cookie)
1917 {
1918 return B_OK;
1919 }
1920
1921
1922 static status_t
bfs_free_attr_cookie(fs_volume * _volume,fs_vnode * _file,void * cookie)1923 bfs_free_attr_cookie(fs_volume* _volume, fs_vnode* _file, void* cookie)
1924 {
1925 delete (attr_cookie*)cookie;
1926 return B_OK;
1927 }
1928
1929
1930 static status_t
bfs_read_attr(fs_volume * _volume,fs_vnode * _file,void * _cookie,off_t pos,void * buffer,size_t * _length)1931 bfs_read_attr(fs_volume* _volume, fs_vnode* _file, void* _cookie, off_t pos,
1932 void* buffer, size_t* _length)
1933 {
1934 FUNCTION();
1935
1936 attr_cookie* cookie = (attr_cookie*)_cookie;
1937 Inode* inode = (Inode*)_file->private_node;
1938
1939 Attribute attribute(inode, cookie);
1940
1941 return attribute.Read(cookie, pos, (uint8*)buffer, _length);
1942 }
1943
1944
1945 static status_t
bfs_write_attr(fs_volume * _volume,fs_vnode * _file,void * _cookie,off_t pos,const void * buffer,size_t * _length)1946 bfs_write_attr(fs_volume* _volume, fs_vnode* _file, void* _cookie,
1947 off_t pos, const void* buffer, size_t* _length)
1948 {
1949 FUNCTION();
1950
1951 attr_cookie* cookie = (attr_cookie*)_cookie;
1952 Volume* volume = (Volume*)_volume->private_volume;
1953 Inode* inode = (Inode*)_file->private_node;
1954
1955 Transaction transaction(volume, inode->BlockNumber());
1956 Attribute attribute(inode, cookie);
1957
1958 bool created;
1959 status_t status = attribute.Write(transaction, cookie, pos,
1960 (const uint8*)buffer, _length, &created);
1961 if (status == B_OK) {
1962 status = transaction.Done();
1963 if (status == B_OK) {
1964 notify_attribute_changed(volume->ID(), inode->ParentID(),
1965 inode->ID(), cookie->name,
1966 created ? B_ATTR_CREATED : B_ATTR_CHANGED);
1967 notify_stat_changed(volume->ID(), inode->ParentID(), inode->ID(),
1968 B_STAT_CHANGE_TIME);
1969 }
1970 }
1971
1972 return status;
1973 }
1974
1975
1976 static status_t
bfs_read_attr_stat(fs_volume * _volume,fs_vnode * _file,void * _cookie,struct stat * stat)1977 bfs_read_attr_stat(fs_volume* _volume, fs_vnode* _file, void* _cookie,
1978 struct stat* stat)
1979 {
1980 FUNCTION();
1981
1982 attr_cookie* cookie = (attr_cookie*)_cookie;
1983 Inode* inode = (Inode*)_file->private_node;
1984
1985 Attribute attribute(inode, cookie);
1986
1987 return attribute.Stat(*stat);
1988 }
1989
1990
1991 static status_t
bfs_write_attr_stat(fs_volume * _volume,fs_vnode * file,void * cookie,const struct stat * stat,int statMask)1992 bfs_write_attr_stat(fs_volume* _volume, fs_vnode* file, void* cookie,
1993 const struct stat* stat, int statMask)
1994 {
1995 // TODO: Implement (at least setting the size)!
1996 return EOPNOTSUPP;
1997 }
1998
1999
2000 static status_t
bfs_rename_attr(fs_volume * _volume,fs_vnode * fromFile,const char * fromName,fs_vnode * toFile,const char * toName)2001 bfs_rename_attr(fs_volume* _volume, fs_vnode* fromFile, const char* fromName,
2002 fs_vnode* toFile, const char* toName)
2003 {
2004 FUNCTION_START(("name = \"%s\", to = \"%s\"\n", fromName, toName));
2005
2006 // TODO: implement bfs_rename_attr()!
2007 // There will probably be an API to move one attribute to another file,
2008 // making that function much more complicated - oh joy ;-)
2009
2010 return EOPNOTSUPP;
2011 }
2012
2013
2014 static status_t
bfs_remove_attr(fs_volume * _volume,fs_vnode * _node,const char * name)2015 bfs_remove_attr(fs_volume* _volume, fs_vnode* _node, const char* name)
2016 {
2017 FUNCTION_START(("name = \"%s\"\n", name));
2018
2019 Volume* volume = (Volume*)_volume->private_volume;
2020 Inode* inode = (Inode*)_node->private_node;
2021
2022 status_t status = inode->CheckPermissions(W_OK);
2023 if (status != B_OK)
2024 return status;
2025
2026 Transaction transaction(volume, inode->BlockNumber());
2027
2028 status = inode->RemoveAttribute(transaction, name);
2029 if (status == B_OK)
2030 status = transaction.Done();
2031 if (status == B_OK) {
2032 notify_attribute_changed(volume->ID(), inode->ParentID(), inode->ID(),
2033 name, B_ATTR_REMOVED);
2034 }
2035
2036 return status;
2037 }
2038
2039
2040 // #pragma mark - Special Nodes
2041
2042
2043 status_t
bfs_create_special_node(fs_volume * _volume,fs_vnode * _directory,const char * name,fs_vnode * subVnode,mode_t mode,uint32 flags,fs_vnode * _superVnode,ino_t * _nodeID)2044 bfs_create_special_node(fs_volume* _volume, fs_vnode* _directory,
2045 const char* name, fs_vnode* subVnode, mode_t mode, uint32 flags,
2046 fs_vnode* _superVnode, ino_t* _nodeID)
2047 {
2048 // no need to support entry-less nodes
2049 if (name == NULL)
2050 return B_UNSUPPORTED;
2051
2052 FUNCTION_START(("name = \"%s\", mode = %u, flags = 0x%" B_PRIx32
2053 ", subVnode: %p\n", name, (unsigned int)mode, flags, subVnode));
2054
2055 Volume* volume = (Volume*)_volume->private_volume;
2056 Inode* directory = (Inode*)_directory->private_node;
2057
2058 if (volume->IsReadOnly())
2059 return B_READ_ONLY_DEVICE;
2060
2061 if (!directory->IsDirectory())
2062 RETURN_ERROR(B_BAD_TYPE);
2063
2064 status_t status = directory->CheckPermissions(W_OK);
2065 if (status < B_OK)
2066 RETURN_ERROR(status);
2067
2068 Transaction transaction(volume, directory->BlockNumber());
2069
2070 off_t id;
2071 Inode* inode;
2072 status = Inode::Create(transaction, directory, name, mode, O_EXCL, 0, NULL,
2073 &id, &inode, subVnode ? subVnode->ops : NULL, flags);
2074 if (status == B_OK) {
2075 _superVnode->private_node = inode;
2076 _superVnode->ops = &gBFSVnodeOps;
2077 *_nodeID = id;
2078
2079 entry_cache_add(volume->ID(), directory->ID(), name, id);
2080
2081 status = transaction.Done();
2082 if (status == B_OK)
2083 notify_entry_created(volume->ID(), directory->ID(), name, id);
2084 else
2085 entry_cache_remove(volume->ID(), directory->ID(), name);
2086 }
2087
2088 return status;
2089 }
2090
2091
2092 // #pragma mark - Index functions
2093
2094
2095 static status_t
bfs_open_index_dir(fs_volume * _volume,void ** _cookie)2096 bfs_open_index_dir(fs_volume* _volume, void** _cookie)
2097 {
2098 FUNCTION();
2099
2100 Volume* volume = (Volume*)_volume->private_volume;
2101
2102 if (volume->IndicesNode() == NULL) {
2103 // This volume does not have any indices
2104 RETURN_ERROR(B_ENTRY_NOT_FOUND);
2105 }
2106
2107 // Since the indices root node is just a directory, and we are storing
2108 // a pointer to it in our Volume object, we can just use the directory
2109 // traversal functions.
2110 // In fact we're storing it in the Volume object for that reason.
2111
2112 fs_vnode indicesNode;
2113 indicesNode.private_node = volume->IndicesNode();
2114
2115 RETURN_ERROR(bfs_open_dir(_volume, &indicesNode, _cookie));
2116 }
2117
2118
2119 static status_t
bfs_close_index_dir(fs_volume * _volume,void * _cookie)2120 bfs_close_index_dir(fs_volume* _volume, void* _cookie)
2121 {
2122 FUNCTION();
2123
2124 Volume* volume = (Volume*)_volume->private_volume;
2125
2126 fs_vnode indicesNode;
2127 indicesNode.private_node = volume->IndicesNode();
2128
2129 RETURN_ERROR(bfs_close_dir(_volume, &indicesNode, _cookie));
2130 }
2131
2132
2133 static status_t
bfs_free_index_dir_cookie(fs_volume * _volume,void * _cookie)2134 bfs_free_index_dir_cookie(fs_volume* _volume, void* _cookie)
2135 {
2136 FUNCTION();
2137
2138 Volume* volume = (Volume*)_volume->private_volume;
2139
2140 fs_vnode indicesNode;
2141 indicesNode.private_node = volume->IndicesNode();
2142
2143 RETURN_ERROR(bfs_free_dir_cookie(_volume, &indicesNode, _cookie));
2144 }
2145
2146
2147 static status_t
bfs_rewind_index_dir(fs_volume * _volume,void * _cookie)2148 bfs_rewind_index_dir(fs_volume* _volume, void* _cookie)
2149 {
2150 FUNCTION();
2151
2152 Volume* volume = (Volume*)_volume->private_volume;
2153
2154 fs_vnode indicesNode;
2155 indicesNode.private_node = volume->IndicesNode();
2156
2157 RETURN_ERROR(bfs_rewind_dir(_volume, &indicesNode, _cookie));
2158 }
2159
2160
2161 static status_t
bfs_read_index_dir(fs_volume * _volume,void * _cookie,struct dirent * dirent,size_t bufferSize,uint32 * _num)2162 bfs_read_index_dir(fs_volume* _volume, void* _cookie, struct dirent* dirent,
2163 size_t bufferSize, uint32* _num)
2164 {
2165 FUNCTION();
2166
2167 Volume* volume = (Volume*)_volume->private_volume;
2168
2169 fs_vnode indicesNode;
2170 indicesNode.private_node = volume->IndicesNode();
2171
2172 RETURN_ERROR(bfs_read_dir(_volume, &indicesNode, _cookie, dirent,
2173 bufferSize, _num));
2174 }
2175
2176
2177 static status_t
bfs_create_index(fs_volume * _volume,const char * name,uint32 type,uint32 flags)2178 bfs_create_index(fs_volume* _volume, const char* name, uint32 type,
2179 uint32 flags)
2180 {
2181 FUNCTION_START(("name = \"%s\", type = %" B_PRIu32
2182 ", flags = %" B_PRIu32 "\n", name, type, flags));
2183
2184 Volume* volume = (Volume*)_volume->private_volume;
2185
2186 if (volume->IsReadOnly())
2187 return B_READ_ONLY_DEVICE;
2188
2189 // only root users are allowed to create indices
2190 if (geteuid() != 0)
2191 return B_NOT_ALLOWED;
2192
2193 Transaction transaction(volume, volume->Indices());
2194
2195 Index index(volume);
2196 status_t status = index.Create(transaction, name, type);
2197
2198 if (status == B_OK)
2199 status = transaction.Done();
2200
2201 RETURN_ERROR(status);
2202 }
2203
2204
2205 static status_t
bfs_remove_index(fs_volume * _volume,const char * name)2206 bfs_remove_index(fs_volume* _volume, const char* name)
2207 {
2208 FUNCTION();
2209
2210 Volume* volume = (Volume*)_volume->private_volume;
2211
2212 if (volume->IsReadOnly())
2213 return B_READ_ONLY_DEVICE;
2214
2215 // only root users are allowed to remove indices
2216 if (geteuid() != 0)
2217 return B_NOT_ALLOWED;
2218
2219 Inode* indices = volume->IndicesNode();
2220 if (indices == NULL)
2221 return B_ENTRY_NOT_FOUND;
2222
2223 Transaction transaction(volume, volume->Indices());
2224
2225 status_t status = indices->Remove(transaction, name);
2226 if (status == B_OK)
2227 status = transaction.Done();
2228
2229 RETURN_ERROR(status);
2230 }
2231
2232
2233 static status_t
bfs_stat_index(fs_volume * _volume,const char * name,struct stat * stat)2234 bfs_stat_index(fs_volume* _volume, const char* name, struct stat* stat)
2235 {
2236 FUNCTION_START(("name = %s\n", name));
2237
2238 Volume* volume = (Volume*)_volume->private_volume;
2239
2240 Index index(volume);
2241 status_t status = index.SetTo(name);
2242 if (status < B_OK)
2243 RETURN_ERROR(status);
2244
2245 bfs_inode& node = index.Node()->Node();
2246
2247 stat->st_type = index.Type();
2248 stat->st_mode = node.Mode();
2249
2250 stat->st_size = node.data.Size();
2251 stat->st_blocks = index.Node()->AllocatedSize() / 512;
2252
2253 stat->st_nlink = 1;
2254 stat->st_blksize = 65536;
2255
2256 stat->st_uid = node.UserID();
2257 stat->st_gid = node.GroupID();
2258
2259 fill_stat_time(node, *stat);
2260
2261 return B_OK;
2262 }
2263
2264
2265 // #pragma mark - Query functions
2266
2267
2268 static status_t
bfs_open_query(fs_volume * _volume,const char * queryString,uint32 flags,port_id port,uint32 token,void ** _cookie)2269 bfs_open_query(fs_volume* _volume, const char* queryString, uint32 flags,
2270 port_id port, uint32 token, void** _cookie)
2271 {
2272 FUNCTION_START(("bfs_open_query(\"%s\", flags = %" B_PRIu32
2273 ", port_id = %" B_PRId32 ", token = %" B_PRIu32 ")\n",
2274 queryString, flags, port, token));
2275
2276 Volume* volume = (Volume*)_volume->private_volume;
2277
2278 Query* query;
2279 status_t error = Query::Create(volume, queryString, flags, port, token, query);
2280 if (error != B_OK)
2281 return error;
2282
2283 *_cookie = (void*)query;
2284
2285 return B_OK;
2286 }
2287
2288
2289 static status_t
bfs_close_query(fs_volume * _volume,void * cookie)2290 bfs_close_query(fs_volume* _volume, void* cookie)
2291 {
2292 FUNCTION();
2293 return B_OK;
2294 }
2295
2296
2297 static status_t
bfs_free_query_cookie(fs_volume * _volume,void * cookie)2298 bfs_free_query_cookie(fs_volume* _volume, void* cookie)
2299 {
2300 FUNCTION();
2301
2302 Query* query = (Query*)cookie;
2303 delete query;
2304
2305 return B_OK;
2306 }
2307
2308
2309 static status_t
bfs_read_query(fs_volume *,void * cookie,struct dirent * dirent,size_t bufferSize,uint32 * _num)2310 bfs_read_query(fs_volume* /*_volume*/, void* cookie, struct dirent* dirent,
2311 size_t bufferSize, uint32* _num)
2312 {
2313 FUNCTION();
2314 Query* query = (Query*)cookie;
2315 status_t status = query->GetNextEntry(dirent, bufferSize);
2316 if (status == B_OK)
2317 *_num = 1;
2318 else if (status == B_ENTRY_NOT_FOUND)
2319 *_num = 0;
2320 else
2321 return status;
2322
2323 return B_OK;
2324 }
2325
2326
2327 static status_t
bfs_rewind_query(fs_volume *,void * cookie)2328 bfs_rewind_query(fs_volume* /*_volume*/, void* cookie)
2329 {
2330 FUNCTION();
2331
2332 Query* query = (Query*)cookie;
2333 return query->Rewind();
2334 }
2335
2336
2337 // #pragma mark -
2338
2339
2340 static uint32
bfs_get_supported_operations(partition_data * partition,uint32 mask)2341 bfs_get_supported_operations(partition_data* partition, uint32 mask)
2342 {
2343 // TODO: We should at least check the partition size.
2344 return B_DISK_SYSTEM_SUPPORTS_INITIALIZING
2345 | B_DISK_SYSTEM_SUPPORTS_CONTENT_NAME
2346 | B_DISK_SYSTEM_SUPPORTS_WRITING;
2347 }
2348
2349
2350 static status_t
bfs_initialize(int fd,partition_id partitionID,const char * name,const char * parameterString,off_t,disk_job_id job)2351 bfs_initialize(int fd, partition_id partitionID, const char* name,
2352 const char* parameterString, off_t /*partitionSize*/, disk_job_id job)
2353 {
2354 // check name
2355 status_t status = check_volume_name(name);
2356 if (status != B_OK)
2357 return status;
2358
2359 // parse parameters
2360 initialize_parameters parameters;
2361 status = parse_initialize_parameters(parameterString, parameters);
2362 if (status != B_OK)
2363 return status;
2364
2365 update_disk_device_job_progress(job, 0);
2366
2367 // initialize the volume
2368 Volume volume(NULL);
2369 status = volume.Initialize(fd, name, parameters.blockSize,
2370 parameters.flags);
2371 if (status < B_OK) {
2372 INFORM(("Initializing volume failed: %s\n", strerror(status)));
2373 return status;
2374 }
2375
2376 // rescan partition
2377 status = scan_partition(partitionID);
2378 if (status != B_OK)
2379 return status;
2380
2381 update_disk_device_job_progress(job, 1);
2382
2383 // print some info, if desired
2384 if (parameters.verbose) {
2385 disk_super_block super = volume.SuperBlock();
2386
2387 INFORM(("Disk was initialized successfully.\n"));
2388 INFORM(("\tname: \"%s\"\n", super.name));
2389 INFORM(("\tnum blocks: %" B_PRIdOFF "\n", super.NumBlocks()));
2390 INFORM(("\tused blocks: %" B_PRIdOFF "\n", super.UsedBlocks()));
2391 INFORM(("\tblock size: %u bytes\n", (unsigned)super.BlockSize()));
2392 INFORM(("\tnum allocation groups: %d\n",
2393 (int)super.AllocationGroups()));
2394 INFORM(("\tallocation group size: %ld blocks\n",
2395 1L << super.AllocationGroupShift()));
2396 INFORM(("\tlog size: %u blocks\n", super.log_blocks.Length()));
2397 }
2398
2399 return B_OK;
2400 }
2401
2402
2403 static status_t
bfs_uninitialize(int fd,partition_id partitionID,off_t partitionSize,uint32 blockSize,disk_job_id job)2404 bfs_uninitialize(int fd, partition_id partitionID, off_t partitionSize,
2405 uint32 blockSize, disk_job_id job)
2406 {
2407 if (blockSize == 0)
2408 return B_BAD_VALUE;
2409
2410 update_disk_device_job_progress(job, 0.0);
2411
2412 // just overwrite the superblock
2413 disk_super_block superBlock;
2414 memset(&superBlock, 0, sizeof(superBlock));
2415
2416 if (write_pos(fd, 512, &superBlock, sizeof(superBlock)) < 0)
2417 return errno;
2418
2419 update_disk_device_job_progress(job, 1.0);
2420
2421 return B_OK;
2422 }
2423
2424
2425 // #pragma mark -
2426
2427
2428 static status_t
bfs_std_ops(int32 op,...)2429 bfs_std_ops(int32 op, ...)
2430 {
2431 switch (op) {
2432 case B_MODULE_INIT:
2433 #ifdef BFS_DEBUGGER_COMMANDS
2434 add_debugger_commands();
2435 #endif
2436 return B_OK;
2437 case B_MODULE_UNINIT:
2438 #ifdef BFS_DEBUGGER_COMMANDS
2439 remove_debugger_commands();
2440 #endif
2441 return B_OK;
2442
2443 default:
2444 return B_ERROR;
2445 }
2446 }
2447
2448 fs_volume_ops gBFSVolumeOps = {
2449 &bfs_unmount,
2450 &bfs_read_fs_stat,
2451 &bfs_write_fs_stat,
2452 &bfs_sync,
2453 &bfs_get_vnode,
2454
2455 /* index directory & index operations */
2456 &bfs_open_index_dir,
2457 &bfs_close_index_dir,
2458 &bfs_free_index_dir_cookie,
2459 &bfs_read_index_dir,
2460 &bfs_rewind_index_dir,
2461
2462 &bfs_create_index,
2463 &bfs_remove_index,
2464 &bfs_stat_index,
2465
2466 /* query operations */
2467 &bfs_open_query,
2468 &bfs_close_query,
2469 &bfs_free_query_cookie,
2470 &bfs_read_query,
2471 &bfs_rewind_query,
2472 };
2473
2474 fs_vnode_ops gBFSVnodeOps = {
2475 /* vnode operations */
2476 &bfs_lookup,
2477 &bfs_get_vnode_name,
2478 &bfs_put_vnode,
2479 &bfs_remove_vnode,
2480
2481 /* VM file access */
2482 &bfs_can_page,
2483 &bfs_read_pages,
2484 &bfs_write_pages,
2485
2486 &bfs_io,
2487 NULL, // cancel_io()
2488
2489 &bfs_get_file_map,
2490
2491 &bfs_ioctl,
2492 &bfs_set_flags,
2493 NULL, // fs_select
2494 NULL, // fs_deselect
2495 &bfs_fsync,
2496
2497 &bfs_read_link,
2498 &bfs_create_symlink,
2499
2500 &bfs_link,
2501 &bfs_unlink,
2502 &bfs_rename,
2503
2504 &bfs_access,
2505 &bfs_read_stat,
2506 &bfs_write_stat,
2507 NULL, // fs_preallocate
2508
2509 /* file operations */
2510 &bfs_create,
2511 &bfs_open,
2512 &bfs_close,
2513 &bfs_free_cookie,
2514 &bfs_read,
2515 &bfs_write,
2516
2517 /* directory operations */
2518 &bfs_create_dir,
2519 &bfs_remove_dir,
2520 &bfs_open_dir,
2521 &bfs_close_dir,
2522 &bfs_free_dir_cookie,
2523 &bfs_read_dir,
2524 &bfs_rewind_dir,
2525
2526 /* attribute directory operations */
2527 &bfs_open_attr_dir,
2528 &bfs_close_attr_dir,
2529 &bfs_free_attr_dir_cookie,
2530 &bfs_read_attr_dir,
2531 &bfs_rewind_attr_dir,
2532
2533 /* attribute operations */
2534 &bfs_create_attr,
2535 &bfs_open_attr,
2536 &bfs_close_attr,
2537 &bfs_free_attr_cookie,
2538 &bfs_read_attr,
2539 &bfs_write_attr,
2540
2541 &bfs_read_attr_stat,
2542 &bfs_write_attr_stat,
2543 &bfs_rename_attr,
2544 &bfs_remove_attr,
2545
2546 /* special nodes */
2547 &bfs_create_special_node
2548 };
2549
2550 static file_system_module_info sBeFileSystem = {
2551 {
2552 "file_systems/bfs" BFS_ENDIAN_SUFFIX B_CURRENT_FS_API_VERSION,
2553 0,
2554 bfs_std_ops,
2555 },
2556
2557 "bfs" BFS_ENDIAN_SUFFIX, // short_name
2558 "Be File System" BFS_ENDIAN_PRETTY_SUFFIX, // pretty_name
2559
2560 // DDM flags
2561 0
2562 // | B_DISK_SYSTEM_SUPPORTS_CHECKING
2563 // | B_DISK_SYSTEM_SUPPORTS_REPAIRING
2564 // | B_DISK_SYSTEM_SUPPORTS_RESIZING
2565 // | B_DISK_SYSTEM_SUPPORTS_MOVING
2566 // | B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_NAME
2567 // | B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_PARAMETERS
2568 | B_DISK_SYSTEM_SUPPORTS_INITIALIZING
2569 | B_DISK_SYSTEM_SUPPORTS_CONTENT_NAME
2570 // | B_DISK_SYSTEM_SUPPORTS_DEFRAGMENTING
2571 // | B_DISK_SYSTEM_SUPPORTS_DEFRAGMENTING_WHILE_MOUNTED
2572 // | B_DISK_SYSTEM_SUPPORTS_CHECKING_WHILE_MOUNTED
2573 // | B_DISK_SYSTEM_SUPPORTS_REPAIRING_WHILE_MOUNTED
2574 // | B_DISK_SYSTEM_SUPPORTS_RESIZING_WHILE_MOUNTED
2575 // | B_DISK_SYSTEM_SUPPORTS_MOVING_WHILE_MOUNTED
2576 // | B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_NAME_WHILE_MOUNTED
2577 // | B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_PARAMETERS_WHILE_MOUNTED
2578 | B_DISK_SYSTEM_SUPPORTS_WRITING
2579 ,
2580
2581 // scanning
2582 bfs_identify_partition,
2583 bfs_scan_partition,
2584 bfs_free_identify_partition_cookie,
2585 NULL, // free_partition_content_cookie()
2586
2587 &bfs_mount,
2588
2589 /* capability querying operations */
2590 &bfs_get_supported_operations,
2591
2592 NULL, // validate_resize
2593 NULL, // validate_move
2594 NULL, // validate_set_content_name
2595 NULL, // validate_set_content_parameters
2596 NULL, // validate_initialize,
2597
2598 /* shadow partition modification */
2599 NULL, // shadow_changed
2600
2601 /* writing */
2602 NULL, // defragment
2603 NULL, // repair
2604 NULL, // resize
2605 NULL, // move
2606 NULL, // set_content_name
2607 NULL, // set_content_parameters
2608 bfs_initialize,
2609 bfs_uninitialize
2610 };
2611
2612 module_info* modules[] = {
2613 (module_info*)&sBeFileSystem,
2614 NULL,
2615 };
2616