1 /*
2 * Copyright 2019, Bharathi Ramana Joshi, joshibharathiramana@gmail.com
3 * Copyright 2019, Les De Ridder, les@lesderid.net
4 * Copyright 2017, Chế Vũ Gia Hy, cvghy116@gmail.com.
5 * Copyright 2011, Jérôme Duval, korli@users.berlios.de.
6 * Copyright 2008, Axel Dörfler, axeld@pinc-software.de.
7 *
8 * This file may be used under the terms of the MIT License.
9 */
10
11
12 #include "Attribute.h"
13 #include "AttributeIterator.h"
14 #include "btrfs.h"
15 #include "btrfs_disk_system.h"
16 #include "DirectoryIterator.h"
17 #include "Inode.h"
18 #include "system_dependencies.h"
19 #include "Utility.h"
20
21
22 #ifdef FS_SHELL
23 #define ERROR(x...) TRACE(x)
24 #define INFORM(x...) TRACE(x)
25 #define init_debugging()
26 #define exit_debugging()
27 #define FUNCTION() dprintf("\33[34mbtrfs:\33[0m %s()\n",__PRETTY_FUNCTION__);
28 #define REPORT_ERROR(status) \
29 dprintf("btrfs: %s:%d: %s\n", __FUNCTION__, __LINE__, strerror(status));
30 #define RETURN_ERROR(err) \
31 { status_t _status = err; if (_status < B_OK) REPORT_ERROR(_status); return _status;}
32 #define PRINT(x) { dprintf("btrfs: "); dprintf x; }
33 #else
34 #include <DebugSupport.h>
35 #endif
36
37
38 //#define TRACE_BTRFS
39 #ifdef TRACE_BTRFS
40 # define TRACE(x...) dprintf("\33[34mbtrfs:\33[0m " x)
41 #else
42 # define TRACE(x...) ;
43 #endif
44
45 #define BTRFS_IO_SIZE 65536
46
47
48 struct identify_cookie {
49 btrfs_super_block super_block;
50 };
51
52
53 //! btrfs_io() callback hook
54 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)55 iterative_io_get_vecs_hook(void* cookie, io_request* request, off_t offset,
56 size_t size, struct file_io_vec* vecs, size_t* _count)
57 {
58 Inode* inode = (Inode*)cookie;
59
60 return file_map_translate(inode->Map(), offset, size, vecs, _count,
61 inode->GetVolume()->BlockSize());
62 }
63
64
65 //! btrfs_io() callback hook
66 static status_t
iterative_io_finished_hook(void * cookie,io_request * request,status_t status,bool partialTransfer,size_t bytesTransferred)67 iterative_io_finished_hook(void* cookie, io_request* request, status_t status,
68 bool partialTransfer, size_t bytesTransferred)
69 {
70 Inode* inode = (Inode*)cookie;
71 rw_lock_read_unlock(inode->Lock());
72 return B_OK;
73 }
74
75
76 // #pragma mark - Scanning
77
78
79 static float
btrfs_identify_partition(int fd,partition_data * partition,void ** _cookie)80 btrfs_identify_partition(int fd, partition_data* partition, void** _cookie)
81 {
82 btrfs_super_block superBlock;
83 status_t status = Volume::Identify(fd, &superBlock);
84 if (status != B_OK)
85 return -1;
86
87 identify_cookie* cookie = new identify_cookie;
88 memcpy(&cookie->super_block, &superBlock, sizeof(btrfs_super_block));
89
90 *_cookie = cookie;
91 return 0.8f;
92 }
93
94
95 static status_t
btrfs_scan_partition(int fd,partition_data * partition,void * _cookie)96 btrfs_scan_partition(int fd, partition_data* partition, void* _cookie)
97 {
98 identify_cookie* cookie = (identify_cookie*)_cookie;
99
100 partition->status = B_PARTITION_VALID;
101 partition->flags |= B_PARTITION_FILE_SYSTEM;
102 partition->content_size = cookie->super_block.TotalSize();
103 partition->block_size = cookie->super_block.BlockSize();
104 partition->content_name = strdup(cookie->super_block.label);
105 if (partition->content_name == NULL)
106 return B_NO_MEMORY;
107
108 return B_OK;
109 }
110
111
112 static void
btrfs_free_identify_partition_cookie(partition_data * partition,void * _cookie)113 btrfs_free_identify_partition_cookie(partition_data* partition, void* _cookie)
114 {
115 delete (identify_cookie*)_cookie;
116 }
117
118
119 // #pragma mark -
120
121
122 static status_t
btrfs_mount(fs_volume * _volume,const char * device,uint32 flags,const char * args,ino_t * _rootID)123 btrfs_mount(fs_volume* _volume, const char* device, uint32 flags,
124 const char* args, ino_t* _rootID)
125 {
126 Volume* volume = new(std::nothrow) Volume(_volume);
127 if (volume == NULL)
128 return B_NO_MEMORY;
129
130 // TODO: this is a bit hacky: we can't use publish_vnode() to publish
131 // the root node, or else its file cache cannot be created (we could
132 // create it later, though). Therefore we're using get_vnode() in Mount(),
133 // but that requires us to export our volume data before calling it.
134 _volume->private_volume = volume;
135 _volume->ops = &gBtrfsVolumeOps;
136
137 status_t status = volume->Mount(device, flags);
138 if (status != B_OK) {
139 ERROR("Failed mounting the volume. Error: %s\n", strerror(status));
140 delete volume;
141 return status;
142 }
143
144 *_rootID = volume->RootNode()->ID();
145 return B_OK;
146 }
147
148
149 static status_t
btrfs_unmount(fs_volume * _volume)150 btrfs_unmount(fs_volume* _volume)
151 {
152 Volume* volume = (Volume*)_volume->private_volume;
153
154 status_t status = volume->Unmount();
155 delete volume;
156
157 return status;
158 }
159
160
161 static status_t
btrfs_read_fs_info(fs_volume * _volume,struct fs_info * info)162 btrfs_read_fs_info(fs_volume* _volume, struct fs_info* info)
163 {
164 Volume* volume = (Volume*)_volume->private_volume;
165
166 // File system flags
167 info->flags = B_FS_IS_PERSISTENT | B_FS_HAS_ATTR
168 | (volume->IsReadOnly() ? B_FS_IS_READONLY : 0);
169 info->io_size = BTRFS_IO_SIZE;
170 info->block_size = volume->BlockSize();
171 info->total_blocks = volume->SuperBlock().TotalSize() / volume->BlockSize();
172 info->free_blocks = 0; //volume->NumFreeBlocks();
173
174 // Volume name
175 strlcpy(info->volume_name, volume->Name(), sizeof(info->volume_name));
176
177 // File system name
178 strlcpy(info->fsh_name, "btrfs", sizeof(info->fsh_name));
179
180 return B_OK;
181 }
182
183
184 static status_t
btrfs_write_fs_info(fs_volume * _volume,const struct fs_info * info,uint32 mask)185 btrfs_write_fs_info(fs_volume* _volume, const struct fs_info* info, uint32 mask)
186 {
187 Volume* volume = (Volume*)_volume->private_volume;
188 if (volume->IsReadOnly())
189 return B_READ_ONLY_DEVICE;
190
191 if (mask & ~FS_WRITE_FSINFO_NAME != 0)
192 return B_NOT_SUPPORTED;
193
194 MutexLocker locker(volume->GetLock());
195 status_t status = B_BAD_VALUE;
196
197 if (mask & FS_WRITE_FSINFO_NAME) {
198 btrfs_super_block& superBlock = volume->SuperBlock();
199
200 strncpy(superBlock.label, info->volume_name,
201 sizeof(superBlock.label) - 1);
202 superBlock.label[sizeof(superBlock.label) - 1] = '\0';
203
204 status = volume->WriteSuperBlock();
205 }
206
207 return status;
208 }
209
210
211 // #pragma mark -
212
213
214 static status_t
btrfs_get_vnode(fs_volume * _volume,ino_t id,fs_vnode * _node,int * _type,uint32 * _flags,bool reenter)215 btrfs_get_vnode(fs_volume* _volume, ino_t id, fs_vnode* _node, int* _type,
216 uint32* _flags, bool reenter)
217 {
218 Volume* volume = (Volume*)_volume->private_volume;
219
220 Inode* inode = new(std::nothrow) Inode(volume, id);
221 if (inode == NULL)
222 return B_NO_MEMORY;
223
224 status_t status = inode->InitCheck();
225 if (status != B_OK) {
226 delete inode;
227 ERROR("get_vnode: InitCheck() failed. Error: %s\n", strerror(status));
228 return status;
229 }
230
231 _node->private_node = inode;
232 _node->ops = &gBtrfsVnodeOps;
233 *_type = inode->Mode();
234 *_flags = 0;
235
236 return B_OK;
237 }
238
239
240 static status_t
btrfs_put_vnode(fs_volume * _volume,fs_vnode * _node,bool reenter)241 btrfs_put_vnode(fs_volume* _volume, fs_vnode* _node, bool reenter)
242 {
243 delete (Inode*)_node->private_node;
244 return B_OK;
245 }
246
247
248 static bool
btrfs_can_page(fs_volume * _volume,fs_vnode * _node,void * _cookie)249 btrfs_can_page(fs_volume* _volume, fs_vnode* _node, void* _cookie)
250 {
251 return true;
252 }
253
254
255 static status_t
btrfs_read_pages(fs_volume * _volume,fs_vnode * _node,void * _cookie,off_t pos,const iovec * vecs,size_t count,size_t * _numBytes)256 btrfs_read_pages(fs_volume* _volume, fs_vnode* _node, void* _cookie,
257 off_t pos, const iovec* vecs, size_t count, size_t* _numBytes)
258 {
259 Volume* volume = (Volume*)_volume->private_volume;
260 Inode* inode = (Inode*)_node->private_node;
261
262 if (inode->FileCache() == NULL)
263 return B_BAD_VALUE;
264
265 rw_lock_read_lock(inode->Lock());
266
267 uint32 vecIndex = 0;
268 size_t vecOffset = 0;
269 size_t bytesLeft = *_numBytes;
270 status_t status;
271
272 while (true) {
273 file_io_vec fileVecs[8];
274 size_t fileVecCount = 8;
275
276 status = file_map_translate(inode->Map(), pos, bytesLeft, fileVecs,
277 &fileVecCount, 0);
278 if (status != B_OK && status != B_BUFFER_OVERFLOW)
279 break;
280
281 bool bufferOverflow = status == B_BUFFER_OVERFLOW;
282
283 size_t bytes = bytesLeft;
284 status = read_file_io_vec_pages(volume->Device(), fileVecs,
285 fileVecCount, vecs, count, &vecIndex, &vecOffset, &bytes);
286 if (status != B_OK || !bufferOverflow)
287 break;
288
289 pos += bytes;
290 bytesLeft -= bytes;
291 }
292
293 rw_lock_read_unlock(inode->Lock());
294
295 return status;
296 }
297
298
299 static status_t
btrfs_io(fs_volume * _volume,fs_vnode * _node,void * _cookie,io_request * request)300 btrfs_io(fs_volume* _volume, fs_vnode* _node, void* _cookie,
301 io_request* request)
302 {
303 Volume* volume = (Volume*)_volume->private_volume;
304 Inode* inode = (Inode*)_node->private_node;
305
306 #ifndef FS_SHELL
307 if (io_request_is_write(request) && volume->IsReadOnly()) {
308 notify_io_request(request, B_READ_ONLY_DEVICE);
309 return B_READ_ONLY_DEVICE;
310 }
311 #endif
312
313 if (inode->FileCache() == NULL) {
314 #ifndef FS_SHELL
315 notify_io_request(request, B_BAD_VALUE);
316 #endif
317 return B_BAD_VALUE;
318 }
319
320 // We lock the node here and will unlock it in the "finished" hook.
321 rw_lock_read_lock(inode->Lock());
322
323 return do_iterative_fd_io(volume->Device(), request,
324 iterative_io_get_vecs_hook, iterative_io_finished_hook, inode);
325 }
326
327
328 static status_t
btrfs_get_file_map(fs_volume * _volume,fs_vnode * _node,off_t offset,size_t size,struct file_io_vec * vecs,size_t * _count)329 btrfs_get_file_map(fs_volume* _volume, fs_vnode* _node, off_t offset,
330 size_t size, struct file_io_vec* vecs, size_t* _count)
331 {
332 TRACE("btrfs_get_file_map()\n");
333 Inode* inode = (Inode*)_node->private_node;
334 size_t index = 0, max = *_count;
335
336 while (true) {
337 off_t blockOffset;
338 off_t blockLength;
339 status_t status = inode->FindBlock(offset, blockOffset, &blockLength);
340 if (status != B_OK)
341 return status;
342
343 if (index > 0 && (vecs[index - 1].offset
344 == blockOffset - vecs[index - 1].length)) {
345 vecs[index - 1].length += blockLength;
346 } else {
347 if (index >= max) {
348 // we're out of file_io_vecs; let's bail out
349 *_count = index;
350 return B_BUFFER_OVERFLOW;
351 }
352
353 vecs[index].offset = blockOffset;
354 vecs[index].length = blockLength;
355 index++;
356 }
357
358 offset += blockLength;
359 size -= blockLength;
360
361 if ((off_t)size <= vecs[index - 1].length || offset >= inode->Size()) {
362 // We're done!
363 *_count = index;
364 TRACE("btrfs_get_file_map for inode %" B_PRIdINO "\n", inode->ID());
365 return B_OK;
366 }
367 }
368
369 // can never get here
370 return B_ERROR;
371 }
372
373
374 // #pragma mark -
375
376
377 static status_t
btrfs_lookup(fs_volume * _volume,fs_vnode * _directory,const char * name,ino_t * _vnodeID)378 btrfs_lookup(fs_volume* _volume, fs_vnode* _directory, const char* name,
379 ino_t* _vnodeID)
380 {
381 TRACE("btrfs_lookup: name address: %p (%s)\n", name, name);
382 Volume* volume = (Volume*)_volume->private_volume;
383 Inode* directory = (Inode*)_directory->private_node;
384
385 // check access permissions
386 status_t status = directory->CheckPermissions(X_OK);
387 if (status < B_OK)
388 return status;
389
390 status = DirectoryIterator(directory).Lookup(name, strlen(name), _vnodeID);
391 if (status != B_OK) {
392 if (status == B_ENTRY_NOT_FOUND)
393 entry_cache_add_missing(volume->ID(), directory->ID(), name);
394 return status;
395 }
396 entry_cache_add(volume->ID(), directory->ID(), name, *_vnodeID);
397
398 return get_vnode(volume->FSVolume(), *_vnodeID, NULL);
399 }
400
401
402 static status_t
btrfs_ioctl(fs_volume * _volume,fs_vnode * _node,void * _cookie,uint32 cmd,void * buffer,size_t bufferLength)403 btrfs_ioctl(fs_volume* _volume, fs_vnode* _node, void* _cookie, uint32 cmd,
404 void* buffer, size_t bufferLength)
405 {
406 TRACE("ioctl: %" B_PRIu32 "\n", cmd);
407
408 /*Volume* volume = (Volume*)_volume->private_volume;*/
409 return B_DEV_INVALID_IOCTL;
410 }
411
412
413 static status_t
btrfs_read_stat(fs_volume * _volume,fs_vnode * _node,struct stat * stat)414 btrfs_read_stat(fs_volume* _volume, fs_vnode* _node, struct stat* stat)
415 {
416 Inode* inode = (Inode*)_node->private_node;
417
418 stat->st_dev = inode->GetVolume()->ID();
419 stat->st_ino = inode->ID();
420 stat->st_nlink = 1;
421 stat->st_blksize = BTRFS_IO_SIZE;
422
423 stat->st_uid = inode->UserID();
424 stat->st_gid = inode->GroupID();
425 stat->st_mode = inode->Mode();
426 stat->st_type = 0;
427
428 inode->GetAccessTime(stat->st_atim);
429 inode->GetModificationTime(stat->st_mtim);
430 inode->GetChangeTime(stat->st_ctim);
431 inode->GetCreationTime(stat->st_crtim);
432
433 stat->st_size = inode->Size();
434 stat->st_blocks = (inode->Size() + 511) / 512;
435
436 return B_OK;
437 }
438
439
440 static status_t
btrfs_write_stat(fs_volume * _volume,fs_vnode * _node,const struct stat * stat,uint32 mask)441 btrfs_write_stat(fs_volume* _volume, fs_vnode* _node, const struct stat* stat,
442 uint32 mask)
443 {
444 FUNCTION();
445
446 Volume* volume = (Volume*)_volume->private_volume;
447 Inode* inode = (Inode*)_node->private_node;
448
449 if (volume->IsReadOnly())
450 return B_READ_ONLY_DEVICE;
451
452 btrfs_inode& node = inode->Node();
453 bool updateTime = false;
454 uid_t uid = geteuid();
455
456 bool isOwnerOrRoot = uid == 0 || uid == (uid_t)node.UserID();
457 bool hasWriteAccess = inode->CheckPermissions(W_OK) == B_OK;
458
459 Transaction transaction(volume);
460
461 if ((mask & B_STAT_SIZE) != 0 && inode->Size() != stat->st_size) {
462 if (inode->IsDirectory())
463 return B_IS_A_DIRECTORY;
464 if (!inode->IsFile())
465 return B_BAD_VALUE;
466 if (!hasWriteAccess)
467 RETURN_ERROR(B_NOT_ALLOWED);
468
469 //TODO: implement file shrinking/growing
470 return B_NOT_SUPPORTED;
471 }
472
473 if ((mask & B_STAT_UID) != 0) {
474 if (uid != 0)
475 RETURN_ERROR(B_NOT_ALLOWED);
476 node.uid = B_HOST_TO_LENDIAN_INT32(stat->st_uid);
477 updateTime = true;
478 }
479
480 if ((mask & B_STAT_GID) != 0) {
481 if (!isOwnerOrRoot)
482 RETURN_ERROR(B_NOT_ALLOWED);
483 node.gid = B_HOST_TO_LENDIAN_INT32(stat->st_gid);
484 updateTime = true;
485 }
486
487 if ((mask & B_STAT_MODE) != 0) {
488 if (!isOwnerOrRoot)
489 RETURN_ERROR(B_NOT_ALLOWED);
490 PRINT(("original mode = %ld, stat->st_mode = %d\n", node.Mode(),
491 stat->st_mode));
492 node.mode = B_HOST_TO_LENDIAN_INT32((node.Mode() & ~S_IUMSK)
493 | (stat->st_mode & S_IUMSK));
494 updateTime = true;
495 }
496
497 if ((mask & B_STAT_CREATION_TIME) != 0) {
498 if (!isOwnerOrRoot && !hasWriteAccess)
499 RETURN_ERROR(B_NOT_ALLOWED);
500 btrfs_inode::SetTime(node.change_time, stat->st_crtim);
501 }
502
503 if ((mask & B_STAT_MODIFICATION_TIME) != 0) {
504 if (!isOwnerOrRoot && !hasWriteAccess)
505 RETURN_ERROR(B_NOT_ALLOWED);
506 btrfs_inode::SetTime(node.change_time, stat->st_mtim);
507 }
508
509 if ((mask & B_STAT_CHANGE_TIME) != 0 || updateTime) {
510 if (!isOwnerOrRoot && !hasWriteAccess)
511 RETURN_ERROR(B_NOT_ALLOWED);
512 if ((mask & B_STAT_CHANGE_TIME) == 0) {
513 uint64_t microseconds = real_time_clock_usecs();
514 struct timespec t;
515 t.tv_sec = microseconds / 1000000;
516 t.tv_nsec = microseconds % 1000000;
517 btrfs_inode::SetTime(node.change_time, t);
518 } else
519 btrfs_inode::SetTime(node.change_time, stat->st_ctim);
520 }
521
522 status_t status = transaction.Done();
523 if (status == B_OK) {
524 ino_t pid;
525 inode->FindParent(&pid);
526 notify_stat_changed(volume->ID(), pid, inode->ID(), mask);
527 }
528
529 return status;
530 }
531
532
533 static status_t
btrfs_open(fs_volume *,fs_vnode * _node,int openMode,void ** _cookie)534 btrfs_open(fs_volume* /*_volume*/, fs_vnode* _node, int openMode,
535 void** _cookie)
536 {
537 Inode* inode = (Inode*)_node->private_node;
538
539 // opening a directory read-only is allowed, although you can't read
540 // any data from it.
541 if (inode->IsDirectory() && (openMode & O_RWMASK) != 0)
542 return B_IS_A_DIRECTORY;
543
544 status_t status = inode->CheckPermissions(open_mode_to_access(openMode)
545 | (openMode & O_TRUNC ? W_OK : 0));
546 if (status != B_OK)
547 return status;
548
549 // Prepare the cookie
550 file_cookie* cookie = new(std::nothrow) file_cookie;
551 if (cookie == NULL)
552 return B_NO_MEMORY;
553 ObjectDeleter<file_cookie> cookieDeleter(cookie);
554
555 cookie->open_mode = openMode & BTRFS_OPEN_MODE_USER_MASK;
556 cookie->last_size = inode->Size();
557 cookie->last_notification = system_time();
558
559 if ((openMode & O_NOCACHE) != 0 && inode->FileCache() != NULL) {
560 // Disable the file cache, if requested?
561 status = file_cache_disable(inode->FileCache());
562 if (status != B_OK)
563 return status;
564 }
565
566 cookieDeleter.Detach();
567 *_cookie = cookie;
568
569 return B_OK;
570 }
571
572
573 status_t
btrfs_write(fs_volume * _volume,fs_vnode * _node,void * _cookie,off_t pos,const void * buffer,size_t * _length)574 btrfs_write(fs_volume* _volume, fs_vnode* _node, void* _cookie, off_t pos,
575 const void* buffer, size_t* _length)
576 {
577 Volume* volume = (Volume*)_volume->private_volume;
578 Inode* inode = (Inode*)_node->private_node;
579
580 if (volume->IsReadOnly())
581 return B_READ_ONLY_DEVICE;
582
583 if (pos < 0)
584 return B_BAD_VALUE;
585
586 if (!inode->IsFile())
587 return B_BAD_VALUE;
588
589 return B_NOT_SUPPORTED;
590 }
591
592
593 static status_t
btrfs_read(fs_volume * _volume,fs_vnode * _node,void * _cookie,off_t pos,void * buffer,size_t * _length)594 btrfs_read(fs_volume* _volume, fs_vnode* _node, void* _cookie, off_t pos,
595 void* buffer, size_t* _length)
596 {
597 FUNCTION();
598 Inode* inode = (Inode*)_node->private_node;
599
600 if (!inode->IsFile()) {
601 *_length = 0;
602 return inode->IsDirectory() ? B_IS_A_DIRECTORY : B_BAD_VALUE;
603 }
604
605 return inode->ReadAt(pos, (uint8*)buffer, _length);
606 }
607
608
609 static status_t
btrfs_close(fs_volume * _volume,fs_vnode * _node,void * _cookie)610 btrfs_close(fs_volume* _volume, fs_vnode* _node, void* _cookie)
611 {
612 return B_OK;
613 }
614
615
616 static status_t
btrfs_free_cookie(fs_volume * _volume,fs_vnode * _node,void * _cookie)617 btrfs_free_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie)
618 {
619 file_cookie* cookie = (file_cookie*)_cookie;
620 Volume* volume = (Volume*)_volume->private_volume;
621 Inode* inode = (Inode*)_node->private_node;
622
623 if (inode->Size() != cookie->last_size)
624 notify_stat_changed(volume->ID(), -1, inode->ID(), B_STAT_SIZE);
625
626 delete cookie;
627 return B_OK;
628 }
629
630
631 static status_t
btrfs_access(fs_volume * _volume,fs_vnode * _node,int accessMode)632 btrfs_access(fs_volume* _volume, fs_vnode* _node, int accessMode)
633 {
634 Inode* inode = (Inode*)_node->private_node;
635 return inode->CheckPermissions(accessMode);
636 }
637
638
639 static status_t
btrfs_read_link(fs_volume * _volume,fs_vnode * _node,char * buffer,size_t * _bufferSize)640 btrfs_read_link(fs_volume* _volume, fs_vnode* _node, char* buffer,
641 size_t* _bufferSize)
642 {
643 FUNCTION();
644 Inode* inode = (Inode*)_node->private_node;
645
646 if (!inode->IsSymLink())
647 return B_BAD_VALUE;
648
649 status_t result = inode->ReadAt(0, reinterpret_cast<uint8*>(buffer),
650 _bufferSize);
651 if (result != B_OK)
652 return result;
653
654 *_bufferSize = inode->Size();
655 return B_OK;
656 }
657
658
659 status_t
btrfs_unlink(fs_volume * _volume,fs_vnode * _directory,const char * name)660 btrfs_unlink(fs_volume* _volume, fs_vnode* _directory, const char* name)
661 {
662 if (!strcmp(name, "..") || !strcmp(name, "."))
663 return B_NOT_ALLOWED;
664
665 Volume* volume = (Volume*)_volume->private_volume;
666 Inode* directory = (Inode*)_directory->private_node;
667
668 status_t status = directory->CheckPermissions(W_OK);
669 if (status < B_OK)
670 return status;
671
672 Transaction transaction(volume);
673 BTree::Path path(volume->FSTree());
674
675 ino_t id;
676 status = DirectoryIterator(directory).Lookup(name, strlen(name), &id);
677 if (status != B_OK)
678 return status;
679
680 Inode inode(volume, id);
681 status = inode.InitCheck();
682 if (status != B_OK)
683 return status;
684
685 status = inode.Remove(transaction, &path);
686 if (status != B_OK)
687 return status;
688 status = inode.Dereference(transaction, &path, directory->ID(), name);
689 if (status != B_OK)
690 return status;
691
692 entry_cache_remove(volume->ID(), directory->ID(), name);
693
694 status = transaction.Done();
695 if (status == B_OK)
696 notify_entry_removed(volume->ID(), directory->ID(), name, id);
697 else
698 entry_cache_add(volume->ID(), directory->ID(), name, id);
699
700 return status;
701 }
702
703
704 // #pragma mark - Directory functions
705
706
707 static status_t
btrfs_create_dir(fs_volume * _volume,fs_vnode * _directory,const char * name,int mode)708 btrfs_create_dir(fs_volume* _volume, fs_vnode* _directory, const char* name,
709 int mode)
710 {
711 Volume* volume = (Volume*)_volume->private_volume;
712 Inode* directory = (Inode*)_directory->private_node;
713 BTree::Path path(volume->FSTree());
714
715 if (volume->IsReadOnly())
716 return B_READ_ONLY_DEVICE;
717
718 if (!directory->IsDirectory())
719 return B_NOT_A_DIRECTORY;
720
721 status_t status = directory->CheckPermissions(W_OK);
722 if (status < B_OK)
723 return status;
724
725 Transaction transaction(volume);
726 ino_t id = volume->GetNextInodeID();
727 mode = S_DIRECTORY | (mode & S_IUMSK);
728 Inode* inode = Inode::Create(transaction, id, directory, mode);
729 if (inode == NULL)
730 return B_NO_MEMORY;
731
732 status = inode->Insert(transaction, &path);
733 if (status != B_OK)
734 return status;
735
736 status = inode->MakeReference(transaction, &path, directory, name, mode);
737 if (status != B_OK)
738 return status;
739
740 put_vnode(volume->FSVolume(), inode->ID());
741 entry_cache_add(volume->ID(), directory->ID(), name, inode->ID());
742
743 status = transaction.Done();
744 if (status == B_OK)
745 notify_entry_created(volume->ID(), directory->ID(), name, inode->ID());
746 else
747 entry_cache_remove(volume->ID(), directory->ID(), name);
748
749 return status;
750 }
751
752
753 static status_t
btrfs_remove_dir(fs_volume * _volume,fs_vnode * _directory,const char * name)754 btrfs_remove_dir(fs_volume* _volume, fs_vnode* _directory, const char* name)
755 {
756 Volume* volume = (Volume*)_volume->private_volume;
757 Inode* directory = (Inode*)_directory->private_node;
758
759 Transaction transaction(volume);
760 BTree::Path path(volume->FSTree());
761
762 ino_t id;
763 status_t status = DirectoryIterator(directory).Lookup(name, strlen(name),
764 &id);
765 if (status != B_OK)
766 return status;
767
768 Inode inode(volume, id);
769 status = inode.InitCheck();
770 if (status != B_OK)
771 return status;
772
773 status = inode.Remove(transaction, &path);
774 if (status != B_OK)
775 return status;
776 status = inode.Dereference(transaction, &path, directory->ID(), name);
777 if (status != B_OK)
778 return status;
779
780 entry_cache_remove(volume->ID(), directory->ID(), name);
781 entry_cache_remove(volume->ID(), id, "..");
782
783 status = transaction.Done();
784 if (status == B_OK)
785 notify_entry_removed(volume->ID(), directory->ID(), name, id);
786 else {
787 entry_cache_add(volume->ID(), directory->ID(), name, id);
788 entry_cache_add(volume->ID(), id, "..", id);
789 }
790
791 return status;
792 }
793
794
795 static status_t
btrfs_open_dir(fs_volume *,fs_vnode * _node,void ** _cookie)796 btrfs_open_dir(fs_volume* /*_volume*/, fs_vnode* _node, void** _cookie)
797 {
798 Inode* inode = (Inode*)_node->private_node;
799 status_t status = inode->CheckPermissions(R_OK);
800 if (status < B_OK)
801 return status;
802
803 if (!inode->IsDirectory())
804 return B_NOT_A_DIRECTORY;
805
806 DirectoryIterator* iterator = new(std::nothrow) DirectoryIterator(inode);
807 if (iterator == NULL || iterator->InitCheck() != B_OK) {
808 delete iterator;
809 return B_NO_MEMORY;
810 }
811
812 *_cookie = iterator;
813 return B_OK;
814 }
815
816
817 static status_t
btrfs_read_dir(fs_volume * _volume,fs_vnode * _node,void * _cookie,struct dirent * dirent,size_t bufferSize,uint32 * _num)818 btrfs_read_dir(fs_volume* _volume, fs_vnode* _node, void* _cookie,
819 struct dirent* dirent, size_t bufferSize, uint32* _num)
820 {
821 DirectoryIterator* iterator = (DirectoryIterator*)_cookie;
822 Volume* volume = (Volume*)_volume->private_volume;
823
824 uint32 maxCount = *_num;
825 uint32 count = 0;
826
827 while (count < maxCount && bufferSize > sizeof(struct dirent)) {
828 ino_t id;
829 size_t length = bufferSize - offsetof(struct dirent, d_name);
830
831 status_t status = iterator->GetNext(dirent->d_name, &length,
832 &id);
833
834 if (status == B_ENTRY_NOT_FOUND)
835 break;
836
837 if (status == B_BUFFER_OVERFLOW) {
838 // the remaining name buffer length was too small
839 if (count == 0)
840 return B_BUFFER_OVERFLOW;
841 break;
842 }
843
844 if (status != B_OK)
845 return status;
846
847 dirent->d_dev = volume->ID();
848 dirent->d_ino = id;
849 dirent->d_reclen = offsetof(struct dirent, d_name) + length + 1;
850
851 bufferSize -= dirent->d_reclen;
852 dirent = (struct dirent*)((uint8*)dirent + dirent->d_reclen);
853 count++;
854 }
855
856 *_num = count;
857 return B_OK;
858 }
859
860
861 static status_t
btrfs_rewind_dir(fs_volume *,fs_vnode *,void * _cookie)862 btrfs_rewind_dir(fs_volume* /*_volume*/, fs_vnode* /*node*/, void* _cookie)
863 {
864 DirectoryIterator* iterator = (DirectoryIterator*)_cookie;
865
866 return iterator->Rewind();
867 }
868
869
870 static status_t
btrfs_close_dir(fs_volume *,fs_vnode *,void *)871 btrfs_close_dir(fs_volume * /*_volume*/, fs_vnode * /*node*/,
872 void * /*_cookie*/)
873 {
874 return B_OK;
875 }
876
877
878 static status_t
btrfs_free_dir_cookie(fs_volume * _volume,fs_vnode * _node,void * _cookie)879 btrfs_free_dir_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie)
880 {
881 delete (DirectoryIterator*)_cookie;
882 return B_OK;
883 }
884
885
886 static status_t
btrfs_open_attr_dir(fs_volume * _volume,fs_vnode * _node,void ** _cookie)887 btrfs_open_attr_dir(fs_volume* _volume, fs_vnode* _node, void** _cookie)
888 {
889 Inode* inode = (Inode*)_node->private_node;
890 TRACE("%s()\n", __FUNCTION__);
891
892 // on directories too ?
893 if (!inode->IsFile())
894 return EINVAL;
895
896 AttributeIterator* iterator = new(std::nothrow) AttributeIterator(inode);
897 if (iterator == NULL || iterator->InitCheck() != B_OK) {
898 delete iterator;
899 return B_NO_MEMORY;
900 }
901
902 *_cookie = iterator;
903 return B_OK;
904 }
905
906
907 static status_t
btrfs_close_attr_dir(fs_volume * _volume,fs_vnode * _node,void * cookie)908 btrfs_close_attr_dir(fs_volume* _volume, fs_vnode* _node, void* cookie)
909 {
910 TRACE("%s()\n", __FUNCTION__);
911 return B_OK;
912 }
913
914
915 static status_t
btrfs_free_attr_dir_cookie(fs_volume * _volume,fs_vnode * _node,void * _cookie)916 btrfs_free_attr_dir_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie)
917 {
918 TRACE("%s()\n", __FUNCTION__);
919 delete (AttributeIterator*)_cookie;
920 return B_OK;
921 }
922
923
924 static status_t
btrfs_read_attr_dir(fs_volume * _volume,fs_vnode * _node,void * _cookie,struct dirent * dirent,size_t bufferSize,uint32 * _num)925 btrfs_read_attr_dir(fs_volume* _volume, fs_vnode* _node,
926 void* _cookie, struct dirent* dirent, size_t bufferSize, uint32* _num)
927 {
928 TRACE("%s()\n", __FUNCTION__);
929 AttributeIterator* iterator = (AttributeIterator*)_cookie;
930
931 size_t length = bufferSize;
932 status_t status = iterator->GetNext(dirent->d_name, &length);
933 if (status == B_ENTRY_NOT_FOUND) {
934 *_num = 0;
935 return B_OK;
936 }
937
938 if (status != B_OK)
939 return status;
940
941 Volume* volume = (Volume*)_volume->private_volume;
942 dirent->d_dev = volume->ID();
943 dirent->d_reclen = offsetof(struct dirent, d_name) + length + 1;
944 *_num = 1;
945
946 return B_OK;
947 }
948
949
950 static status_t
btrfs_rewind_attr_dir(fs_volume * _volume,fs_vnode * _node,void * _cookie)951 btrfs_rewind_attr_dir(fs_volume* _volume, fs_vnode* _node, void* _cookie)
952 {
953 AttributeIterator* iterator = (AttributeIterator*)_cookie;
954 return iterator->Rewind();
955 }
956
957
958 /* attribute operations */
959 static status_t
btrfs_create_attr(fs_volume * _volume,fs_vnode * _node,const char * name,uint32 type,int openMode,void ** _cookie)960 btrfs_create_attr(fs_volume* _volume, fs_vnode* _node,
961 const char* name, uint32 type, int openMode, void** _cookie)
962 {
963 return EROFS;
964 }
965
966
967 static status_t
btrfs_open_attr(fs_volume * _volume,fs_vnode * _node,const char * name,int openMode,void ** _cookie)968 btrfs_open_attr(fs_volume* _volume, fs_vnode* _node, const char* name,
969 int openMode, void** _cookie)
970 {
971 TRACE("%s()\n", __FUNCTION__);
972
973 Inode* inode = (Inode*)_node->private_node;
974 Attribute attribute(inode);
975
976 return attribute.Open(name, openMode, (attr_cookie**)_cookie);
977 }
978
979
980 static status_t
btrfs_close_attr(fs_volume * _volume,fs_vnode * _node,void * cookie)981 btrfs_close_attr(fs_volume* _volume, fs_vnode* _node,
982 void* cookie)
983 {
984 return B_OK;
985 }
986
987
988 static status_t
btrfs_free_attr_cookie(fs_volume * _volume,fs_vnode * _node,void * cookie)989 btrfs_free_attr_cookie(fs_volume* _volume, fs_vnode* _node,
990 void* cookie)
991 {
992 delete (attr_cookie*)cookie;
993 return B_OK;
994 }
995
996
997 static status_t
btrfs_read_attr(fs_volume * _volume,fs_vnode * _node,void * _cookie,off_t pos,void * buffer,size_t * _length)998 btrfs_read_attr(fs_volume* _volume, fs_vnode* _node, void* _cookie,
999 off_t pos, void* buffer, size_t* _length)
1000 {
1001 TRACE("%s()\n", __FUNCTION__);
1002
1003 attr_cookie* cookie = (attr_cookie*)_cookie;
1004 Inode* inode = (Inode*)_node->private_node;
1005
1006 Attribute attribute(inode, cookie);
1007
1008 return attribute.Read(cookie, pos, (uint8*)buffer, _length);
1009 }
1010
1011
1012 static status_t
btrfs_write_attr(fs_volume * _volume,fs_vnode * _node,void * cookie,off_t pos,const void * buffer,size_t * length)1013 btrfs_write_attr(fs_volume* _volume, fs_vnode* _node, void* cookie,
1014 off_t pos, const void* buffer, size_t* length)
1015 {
1016 return EROFS;
1017 }
1018
1019
1020 static status_t
btrfs_read_attr_stat(fs_volume * _volume,fs_vnode * _node,void * _cookie,struct stat * stat)1021 btrfs_read_attr_stat(fs_volume* _volume, fs_vnode* _node,
1022 void* _cookie, struct stat* stat)
1023 {
1024 attr_cookie* cookie = (attr_cookie*)_cookie;
1025 Inode* inode = (Inode*)_node->private_node;
1026
1027 Attribute attribute(inode, cookie);
1028
1029 return attribute.Stat(*stat);
1030 }
1031
1032
1033 static status_t
btrfs_write_attr_stat(fs_volume * _volume,fs_vnode * _node,void * cookie,const struct stat * stat,int statMask)1034 btrfs_write_attr_stat(fs_volume* _volume, fs_vnode* _node,
1035 void* cookie, const struct stat* stat, int statMask)
1036 {
1037 return EROFS;
1038 }
1039
1040
1041 static status_t
btrfs_rename_attr(fs_volume * _volume,fs_vnode * fromVnode,const char * fromName,fs_vnode * toVnode,const char * toName)1042 btrfs_rename_attr(fs_volume* _volume, fs_vnode* fromVnode,
1043 const char* fromName, fs_vnode* toVnode, const char* toName)
1044 {
1045 return EROFS;
1046 }
1047
1048
1049 static status_t
btrfs_remove_attr(fs_volume * _volume,fs_vnode * vnode,const char * name)1050 btrfs_remove_attr(fs_volume* _volume, fs_vnode* vnode,
1051 const char* name)
1052 {
1053 return EROFS;
1054 }
1055
1056 static uint32
btrfs_get_supported_operations(partition_data * partition,uint32 mask)1057 btrfs_get_supported_operations(partition_data* partition, uint32 mask)
1058 {
1059 // TODO: We should at least check the partition size.
1060 return B_DISK_SYSTEM_SUPPORTS_INITIALIZING
1061 | B_DISK_SYSTEM_SUPPORTS_CONTENT_NAME
1062 // | B_DISK_SYSTEM_SUPPORTS_WRITING
1063 ;
1064 }
1065
1066
1067 static status_t
btrfs_initialize(int fd,partition_id partitionID,const char * name,const char * parameterString,off_t partitionSize,disk_job_id job)1068 btrfs_initialize(int fd, partition_id partitionID, const char* name,
1069 const char* parameterString, off_t partitionSize, disk_job_id job)
1070 {
1071 // check name
1072 status_t status = check_volume_name(name);
1073 if (status != B_OK)
1074 return status;
1075
1076 // parse parameters
1077 initialize_parameters parameters;
1078 status = parse_initialize_parameters(parameterString, parameters);
1079 if (status != B_OK)
1080 return status;
1081
1082 update_disk_device_job_progress(job, 0);
1083
1084 // initialize the volume
1085 Volume volume(NULL);
1086 status = volume.Initialize(fd, name, parameters.blockSize,
1087 parameters.sectorSize);
1088 if (status < B_OK) {
1089 INFORM("Initializing volume failed: %s\n", strerror(status));
1090 return status;
1091 }
1092
1093 // rescan partition
1094 status = scan_partition(partitionID);
1095 if (status != B_OK)
1096 return status;
1097
1098 update_disk_device_job_progress(job, 1);
1099
1100 // print some info, if desired
1101 if (parameters.verbose) {
1102 btrfs_super_block super = volume.SuperBlock();
1103
1104 INFORM("Disk was initialized successfully.\n");
1105 INFORM("\tlabel: \"%s\"\n", super.label);
1106 INFORM("\tblock size: %u bytes\n", (unsigned)super.BlockSize());
1107 INFORM("\tsector size: %u bytes\n", (unsigned)super.SectorSize());
1108 }
1109
1110 return B_OK;
1111 }
1112
1113
1114 static status_t
btrfs_uninitialize(int fd,partition_id partitionID,off_t partitionSize,uint32 blockSize,disk_job_id job)1115 btrfs_uninitialize(int fd, partition_id partitionID, off_t partitionSize,
1116 uint32 blockSize, disk_job_id job)
1117 {
1118 if (blockSize == 0)
1119 return B_BAD_VALUE;
1120
1121 update_disk_device_job_progress(job, 0.0);
1122
1123 // just overwrite the superblock
1124 btrfs_super_block superBlock;
1125 memset(&superBlock, 0, sizeof(superBlock));
1126
1127 if (write_pos(fd, BTRFS_SUPER_BLOCK_OFFSET, &superBlock,
1128 sizeof(superBlock)) < 0)
1129 return errno;
1130
1131 update_disk_device_job_progress(job, 1.0);
1132
1133 return B_OK;
1134 }
1135
1136 // #pragma mark -
1137
1138
1139 static status_t
btrfs_std_ops(int32 op,...)1140 btrfs_std_ops(int32 op, ...)
1141 {
1142 switch (op) {
1143 case B_MODULE_INIT:
1144 init_debugging();
1145
1146 return B_OK;
1147 case B_MODULE_UNINIT:
1148 exit_debugging();
1149
1150 return B_OK;
1151
1152 default:
1153 return B_ERROR;
1154 }
1155 }
1156
1157
1158 fs_volume_ops gBtrfsVolumeOps = {
1159 &btrfs_unmount,
1160 &btrfs_read_fs_info,
1161 &btrfs_write_fs_info,
1162 NULL, // fs_sync,
1163 &btrfs_get_vnode,
1164 };
1165
1166
1167 fs_vnode_ops gBtrfsVnodeOps = {
1168 /* vnode operations */
1169 &btrfs_lookup,
1170 NULL, // btrfs_get_vnode_name - optional, and we can't do better than the
1171 // fallback implementation, so leave as NULL.
1172 &btrfs_put_vnode,
1173 NULL, // btrfs_remove_vnode,
1174
1175 /* VM file access */
1176 &btrfs_can_page,
1177 &btrfs_read_pages,
1178 NULL, // btrfs_write_pages,
1179
1180 NULL, // io()
1181 NULL, // cancel_io()
1182
1183 &btrfs_get_file_map,
1184
1185 &btrfs_ioctl,
1186 NULL,
1187 NULL, // fs_select
1188 NULL, // fs_deselect
1189 NULL, // fs_fsync,
1190
1191 &btrfs_read_link,
1192 NULL, // fs_create_symlink,
1193
1194 NULL, // fs_link,
1195 &btrfs_unlink,
1196 NULL, // fs_rename,
1197
1198 &btrfs_access,
1199 &btrfs_read_stat,
1200 &btrfs_write_stat,
1201 NULL, // fs_preallocate
1202
1203 /* file operations */
1204 NULL, // fs_create,
1205 &btrfs_open,
1206 &btrfs_close,
1207 &btrfs_free_cookie,
1208 &btrfs_read,
1209 &btrfs_write,
1210
1211 /* directory operations */
1212 &btrfs_create_dir,
1213 &btrfs_remove_dir,
1214 &btrfs_open_dir,
1215 &btrfs_close_dir,
1216 &btrfs_free_dir_cookie,
1217 &btrfs_read_dir,
1218 &btrfs_rewind_dir,
1219
1220 /* attribute directory operations */
1221 &btrfs_open_attr_dir,
1222 &btrfs_close_attr_dir,
1223 &btrfs_free_attr_dir_cookie,
1224 &btrfs_read_attr_dir,
1225 &btrfs_rewind_attr_dir,
1226
1227 /* attribute operations */
1228 &btrfs_create_attr,
1229 &btrfs_open_attr,
1230 &btrfs_close_attr,
1231 &btrfs_free_attr_cookie,
1232 &btrfs_read_attr,
1233 &btrfs_write_attr,
1234 &btrfs_read_attr_stat,
1235 &btrfs_write_attr_stat,
1236 &btrfs_rename_attr,
1237 &btrfs_remove_attr,
1238 };
1239
1240
1241 static file_system_module_info sBtrfsFileSystem = {
1242 {
1243 "file_systems/btrfs" B_CURRENT_FS_API_VERSION,
1244 0,
1245 btrfs_std_ops,
1246 },
1247
1248 "btrfs", // short_name
1249 "Btrfs File System", // pretty_name
1250
1251 // DDM flags
1252 0
1253 | B_DISK_SYSTEM_SUPPORTS_INITIALIZING
1254 | B_DISK_SYSTEM_SUPPORTS_CONTENT_NAME
1255 // | B_DISK_SYSTEM_SUPPORTS_WRITING
1256 ,
1257
1258 // scanning
1259 btrfs_identify_partition,
1260 btrfs_scan_partition,
1261 btrfs_free_identify_partition_cookie,
1262 NULL, // free_partition_content_cookie()
1263
1264 &btrfs_mount,
1265
1266
1267 /* capability querying operations */
1268 &btrfs_get_supported_operations,
1269
1270 NULL, // validate_resize
1271 NULL, // validate_move
1272 NULL, // validate_set_content_name
1273 NULL, // validate_set_content_parameters
1274 NULL, // validate_initialize,
1275
1276 /* shadow partition modification */
1277 NULL, // shadow_changed
1278
1279 /* writing */
1280 NULL, // defragment
1281 NULL, // repair
1282 NULL, // resize
1283 NULL, // move
1284 NULL, // set_content_name
1285 NULL, // set_content_parameters
1286 btrfs_initialize,
1287 btrfs_uninitialize
1288 };
1289
1290
1291 module_info* modules[] = {
1292 (module_info*)&sBtrfsFileSystem,
1293 NULL,
1294 };
1295