btrfs: [] on the end of a struct field is a variable length array.
[haiku.git] / src / add-ons / kernel / file_systems / exfat / kernel_interface.cpp
blob517f1a2c0431cec4555f40af693213f282e4c96e
1 /*
2 * Copyright 2008-2010, Axel Dörfler, axeld@pinc-software.de.
3 * Copyright 2011, Jérôme Duval, korli@users.berlios.de.
4 * Copyright 2014 Haiku, Inc. All rights reserved.
6 * Distributed under the terms of the MIT License.
8 * Authors:
9 * Axel Dörfler, axeld@pinc-software.de
10 * Jérôme Duval, korli@users.berlios.de
11 * John Scipione, jscipione@gmail.com
15 #include <dirent.h>
16 #include <unistd.h>
17 #include <util/kernel_cpp.h>
18 #include <string.h>
20 #include <new>
22 #include <AutoDeleter.h>
23 #include <fs_cache.h>
24 #include <fs_info.h>
25 #include <io_requests.h>
26 #include <NodeMonitor.h>
27 #include <StorageDefs.h>
28 #include <util/AutoLock.h>
30 #include "DirectoryIterator.h"
31 #include "exfat.h"
32 #include "Inode.h"
33 #include "Utility.h"
36 //#define TRACE_EXFAT
37 #ifdef TRACE_EXFAT
38 # define TRACE(x...) dprintf("\33[34mexfat:\33[0m " x)
39 #else
40 # define TRACE(x...) ;
41 #endif
42 #define ERROR(x...) dprintf("\33[34mexfat:\33[0m " x)
45 #define EXFAT_IO_SIZE 65536
48 struct identify_cookie {
49 exfat_super_block super_block;
50 char name[B_FILE_NAME_LENGTH];
54 //! exfat_io() callback hook
55 static status_t
56 iterative_io_get_vecs_hook(void* cookie, io_request* request, off_t offset,
57 size_t size, struct file_io_vec* vecs, size_t* _count)
59 Inode* inode = (Inode*)cookie;
61 return file_map_translate(inode->Map(), offset, size, vecs, _count,
62 inode->GetVolume()->BlockSize());
66 //! exfat_io() callback hook
67 static status_t
68 iterative_io_finished_hook(void* cookie, io_request* request, status_t status,
69 bool partialTransfer, size_t bytesTransferred)
71 Inode* inode = (Inode*)cookie;
72 rw_lock_read_unlock(inode->Lock());
73 return B_OK;
77 // #pragma mark - Scanning
80 static float
81 exfat_identify_partition(int fd, partition_data* partition, void** _cookie)
83 struct exfat_super_block superBlock;
84 status_t status = Volume::Identify(fd, &superBlock);
85 if (status != B_OK)
86 return -1;
88 identify_cookie* cookie = new (std::nothrow)identify_cookie;
89 if (cookie == NULL)
90 return -1;
92 memcpy(&cookie->super_block, &superBlock, sizeof(exfat_super_block));
93 memset(cookie->name, 0, sizeof(cookie->name));
94 // zero out volume name
96 uint32 rootDirCluster = superBlock.RootDirCluster();
97 uint32 blockSize = 1 << superBlock.BlockShift();
98 uint32 clusterSize = blockSize << superBlock.BlocksPerClusterShift();
99 uint64 rootDirectoryOffset = (uint64)(EXFAT_SUPER_BLOCK_OFFSET
100 + superBlock.FirstDataBlock() * blockSize
101 + (rootDirCluster - 2) * clusterSize);
102 struct exfat_entry entry;
103 size_t entrySize = sizeof(struct exfat_entry);
104 for (uint32 i = 0; read_pos(fd, rootDirectoryOffset + i * entrySize,
105 &entry, entrySize) == (ssize_t)entrySize; i++) {
106 if (entry.type == EXFAT_ENTRY_TYPE_NOT_IN_USE
107 || entry.type == EXFAT_ENTRY_TYPE_LABEL) {
108 if (get_volume_name(&entry, cookie->name, sizeof(cookie->name))
109 != B_OK) {
110 delete cookie;
111 return -1;
113 break;
117 if (cookie->name[0] == '\0') {
118 off_t fileSystemSize = (off_t)superBlock.NumBlocks()
119 << superBlock.BlockShift();
120 get_default_volume_name(fileSystemSize, cookie->name,
121 sizeof(cookie->name));
124 *_cookie = cookie;
125 return 0.8f;
129 static status_t
130 exfat_scan_partition(int fd, partition_data* partition, void* _cookie)
132 identify_cookie* cookie = (identify_cookie*)_cookie;
134 partition->status = B_PARTITION_VALID;
135 partition->flags |= B_PARTITION_FILE_SYSTEM;
136 partition->content_size = cookie->super_block.NumBlocks()
137 << cookie->super_block.BlockShift();
138 partition->block_size = 1 << cookie->super_block.BlockShift();
139 partition->content_name = strdup(cookie->name);
141 return partition->content_name != NULL ? B_OK : B_NO_MEMORY;
145 static void
146 exfat_free_identify_partition_cookie(partition_data* partition, void* _cookie)
148 delete (identify_cookie*)_cookie;
152 // #pragma mark -
155 static status_t
156 exfat_mount(fs_volume* _volume, const char* device, uint32 flags,
157 const char* args, ino_t* _rootID)
159 Volume* volume = new(std::nothrow) Volume(_volume);
160 if (volume == NULL)
161 return B_NO_MEMORY;
163 // TODO: this is a bit hacky: we can't use publish_vnode() to publish
164 // the root node, or else its file cache cannot be created (we could
165 // create it later, though). Therefore we're using get_vnode() in Mount(),
166 // but that requires us to export our volume data before calling it.
167 _volume->private_volume = volume;
168 _volume->ops = &gExfatVolumeOps;
170 status_t status = volume->Mount(device, flags);
171 if (status != B_OK) {
172 ERROR("Failed mounting the volume. Error: %s\n", strerror(status));
173 delete volume;
174 return status;
177 *_rootID = volume->RootNode()->ID();
178 return B_OK;
182 static status_t
183 exfat_unmount(fs_volume *_volume)
185 Volume* volume = (Volume *)_volume->private_volume;
187 status_t status = volume->Unmount();
188 delete volume;
190 return status;
194 static status_t
195 exfat_read_fs_info(fs_volume* _volume, struct fs_info* info)
197 Volume* volume = (Volume*)_volume->private_volume;
199 // File system flags
200 info->flags = B_FS_IS_PERSISTENT
201 | (volume->IsReadOnly() ? B_FS_IS_READONLY : 0);
202 info->io_size = EXFAT_IO_SIZE;
203 info->block_size = volume->BlockSize();
204 info->total_blocks = volume->SuperBlock().NumBlocks();
205 info->free_blocks = 0; //volume->NumFreeBlocks();
207 // Volume name
208 strlcpy(info->volume_name, volume->Name(), sizeof(info->volume_name));
210 // File system name
211 strlcpy(info->fsh_name, "exfat", sizeof(info->fsh_name));
213 return B_OK;
217 // #pragma mark -
220 static status_t
221 exfat_get_vnode(fs_volume* _volume, ino_t id, fs_vnode* _node, int* _type,
222 uint32* _flags, bool reenter)
224 TRACE("get_vnode %" B_PRIdINO "\n", id);
225 Volume* volume = (Volume*)_volume->private_volume;
227 Inode* inode = new(std::nothrow) Inode(volume, id);
228 if (inode == NULL)
229 return B_NO_MEMORY;
231 status_t status = inode->InitCheck();
232 if (status != B_OK)
233 delete inode;
235 if (status == B_OK) {
236 _node->private_node = inode;
237 _node->ops = &gExfatVnodeOps;
238 *_type = inode->Mode();
239 *_flags = 0;
240 } else
241 ERROR("get_vnode: InitCheck() failed. Error: %s\n", strerror(status));
243 return status;
247 static status_t
248 exfat_put_vnode(fs_volume* _volume, fs_vnode* _node, bool reenter)
250 delete (Inode*)_node->private_node;
251 return B_OK;
255 static bool
256 exfat_can_page(fs_volume* _volume, fs_vnode* _node, void* _cookie)
258 return true;
262 static status_t
263 exfat_read_pages(fs_volume* _volume, fs_vnode* _node, void* _cookie,
264 off_t pos, const iovec* vecs, size_t count, size_t* _numBytes)
266 Volume* volume = (Volume*)_volume->private_volume;
267 Inode* inode = (Inode*)_node->private_node;
269 if (inode->FileCache() == NULL)
270 return B_BAD_VALUE;
272 rw_lock_read_lock(inode->Lock());
274 uint32 vecIndex = 0;
275 size_t vecOffset = 0;
276 size_t bytesLeft = *_numBytes;
277 status_t status;
279 while (true) {
280 file_io_vec fileVecs[8];
281 size_t fileVecCount = 8;
283 status = file_map_translate(inode->Map(), pos, bytesLeft, fileVecs,
284 &fileVecCount, 0);
285 if (status != B_OK && status != B_BUFFER_OVERFLOW)
286 break;
288 bool bufferOverflow = status == B_BUFFER_OVERFLOW;
290 size_t bytes = bytesLeft;
291 status = read_file_io_vec_pages(volume->Device(), fileVecs,
292 fileVecCount, vecs, count, &vecIndex, &vecOffset, &bytes);
293 if (status != B_OK || !bufferOverflow)
294 break;
296 pos += bytes;
297 bytesLeft -= bytes;
300 rw_lock_read_unlock(inode->Lock());
302 return status;
306 static status_t
307 exfat_io(fs_volume* _volume, fs_vnode* _node, void* _cookie,
308 io_request* request)
310 Volume* volume = (Volume*)_volume->private_volume;
311 Inode* inode = (Inode*)_node->private_node;
313 #ifndef EXFAT_SHELL
314 if (io_request_is_write(request) && volume->IsReadOnly()) {
315 notify_io_request(request, B_READ_ONLY_DEVICE);
316 return B_READ_ONLY_DEVICE;
318 #endif
320 if (inode->FileCache() == NULL) {
321 #ifndef EXFAT_SHELL
322 notify_io_request(request, B_BAD_VALUE);
323 #endif
324 return B_BAD_VALUE;
327 // We lock the node here and will unlock it in the "finished" hook.
328 rw_lock_read_lock(inode->Lock());
330 return do_iterative_fd_io(volume->Device(), request,
331 iterative_io_get_vecs_hook, iterative_io_finished_hook, inode);
335 static status_t
336 exfat_get_file_map(fs_volume* _volume, fs_vnode* _node, off_t offset,
337 size_t size, struct file_io_vec* vecs, size_t* _count)
339 TRACE("exfat_get_file_map()\n");
340 Inode* inode = (Inode*)_node->private_node;
341 size_t index = 0, max = *_count;
343 while (true) {
344 off_t blockOffset;
345 off_t blockLength;
346 status_t status = inode->FindBlock(offset, blockOffset, &blockLength);
347 if (status != B_OK)
348 return status;
350 if (index > 0 && (vecs[index - 1].offset
351 == blockOffset - vecs[index - 1].length)) {
352 vecs[index - 1].length += blockLength;
353 } else {
354 if (index >= max) {
355 // we're out of file_io_vecs; let's bail out
356 *_count = index;
357 return B_BUFFER_OVERFLOW;
360 vecs[index].offset = blockOffset;
361 vecs[index].length = blockLength;
362 index++;
365 offset += blockLength;
366 size -= blockLength;
368 if ((off_t)size <= vecs[index - 1].length || offset >= inode->Size()) {
369 // We're done!
370 *_count = index;
371 TRACE("exfat_get_file_map for inode %" B_PRIdINO"\n", inode->ID());
372 return B_OK;
376 // can never get here
377 return B_ERROR;
381 // #pragma mark -
384 static status_t
385 exfat_lookup(fs_volume* _volume, fs_vnode* _directory, const char* name,
386 ino_t* _vnodeID)
388 TRACE("exfat_lookup: name address: %p (%s)\n", name, name);
389 Volume* volume = (Volume*)_volume->private_volume;
390 Inode* directory = (Inode*)_directory->private_node;
392 // check access permissions
393 status_t status = directory->CheckPermissions(X_OK);
394 if (status < B_OK)
395 return status;
397 status = DirectoryIterator(directory).Lookup(name, strlen(name), _vnodeID);
398 if (status != B_OK) {
399 ERROR("exfat_lookup: name %s (%s)\n", name, strerror(status));
400 return status;
403 TRACE("exfat_lookup: ID %" B_PRIdINO "\n", *_vnodeID);
405 return get_vnode(volume->FSVolume(), *_vnodeID, NULL);
409 static status_t
410 exfat_ioctl(fs_volume* _volume, fs_vnode* _node, void* _cookie, uint32 cmd,
411 void* buffer, size_t bufferLength)
413 TRACE("ioctl: %" B_PRIu32 "\n", cmd);
415 /*Volume* volume = (Volume*)_volume->private_volume;*/
416 return B_DEV_INVALID_IOCTL;
420 static status_t
421 exfat_read_stat(fs_volume* _volume, fs_vnode* _node, struct stat* stat)
423 Inode* inode = (Inode*)_node->private_node;
425 stat->st_dev = inode->GetVolume()->ID();
426 stat->st_ino = inode->ID();
427 stat->st_nlink = 1;
428 stat->st_blksize = EXFAT_IO_SIZE;
430 stat->st_uid = inode->UserID();
431 stat->st_gid = inode->GroupID();
432 stat->st_mode = inode->Mode();
433 stat->st_type = 0;
435 inode->GetAccessTime(stat->st_atim);
436 inode->GetModificationTime(stat->st_mtim);
437 inode->GetChangeTime(stat->st_ctim);
438 inode->GetCreationTime(stat->st_crtim);
440 stat->st_size = inode->Size();
441 stat->st_blocks = (inode->Size() + 511) / 512;
443 return B_OK;
447 static status_t
448 exfat_open(fs_volume* /*_volume*/, fs_vnode* _node, int openMode,
449 void** _cookie)
451 Inode* inode = (Inode*)_node->private_node;
453 // opening a directory read-only is allowed, although you can't read
454 // any data from it.
455 if (inode->IsDirectory() && (openMode & O_RWMASK) != 0)
456 return B_IS_A_DIRECTORY;
458 status_t status = inode->CheckPermissions(open_mode_to_access(openMode)
459 | (openMode & O_TRUNC ? W_OK : 0));
460 if (status != B_OK)
461 return status;
463 // Prepare the cookie
464 file_cookie* cookie = new(std::nothrow) file_cookie;
465 if (cookie == NULL)
466 return B_NO_MEMORY;
467 ObjectDeleter<file_cookie> cookieDeleter(cookie);
469 cookie->open_mode = openMode & EXFAT_OPEN_MODE_USER_MASK;
470 cookie->last_size = inode->Size();
471 cookie->last_notification = system_time();
473 if ((openMode & O_NOCACHE) != 0 && inode->FileCache() != NULL) {
474 // Disable the file cache, if requested?
475 status = file_cache_disable(inode->FileCache());
476 if (status != B_OK)
477 return status;
480 cookieDeleter.Detach();
481 *_cookie = cookie;
483 return B_OK;
487 static status_t
488 exfat_read(fs_volume* _volume, fs_vnode* _node, void* _cookie, off_t pos,
489 void* buffer, size_t* _length)
491 Inode* inode = (Inode*)_node->private_node;
493 if (!inode->IsFile()) {
494 *_length = 0;
495 return inode->IsDirectory() ? B_IS_A_DIRECTORY : B_BAD_VALUE;
498 return inode->ReadAt(pos, (uint8*)buffer, _length);
502 static status_t
503 exfat_close(fs_volume *_volume, fs_vnode *_node, void *_cookie)
505 return B_OK;
509 static status_t
510 exfat_free_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie)
512 file_cookie* cookie = (file_cookie*)_cookie;
513 Volume* volume = (Volume*)_volume->private_volume;
514 Inode* inode = (Inode*)_node->private_node;
516 if (inode->Size() != cookie->last_size)
517 notify_stat_changed(volume->ID(), -1, inode->ID(), B_STAT_SIZE);
519 delete cookie;
520 return B_OK;
524 static status_t
525 exfat_access(fs_volume* _volume, fs_vnode* _node, int accessMode)
527 Inode* inode = (Inode*)_node->private_node;
528 return inode->CheckPermissions(accessMode);
532 static status_t
533 exfat_read_link(fs_volume *_volume, fs_vnode *_node, char *buffer,
534 size_t *_bufferSize)
536 Inode* inode = (Inode*)_node->private_node;
537 return inode->ReadAt(0, (uint8*)buffer, _bufferSize);
541 // #pragma mark - Directory functions
544 static status_t
545 exfat_open_dir(fs_volume* /*_volume*/, fs_vnode* _node, void** _cookie)
547 Inode* inode = (Inode*)_node->private_node;
548 status_t status = inode->CheckPermissions(R_OK);
549 if (status < B_OK)
550 return status;
552 if (!inode->IsDirectory())
553 return B_NOT_A_DIRECTORY;
555 DirectoryIterator* iterator = new(std::nothrow) DirectoryIterator(inode);
556 if (iterator == NULL || iterator->InitCheck() != B_OK) {
557 delete iterator;
558 return B_NO_MEMORY;
561 *_cookie = iterator;
562 return B_OK;
566 static status_t
567 exfat_read_dir(fs_volume *_volume, fs_vnode *_node, void *_cookie,
568 struct dirent *dirent, size_t bufferSize, uint32 *_num)
570 TRACE("exfat_read_dir\n");
571 DirectoryIterator* iterator = (DirectoryIterator*)_cookie;
572 Volume* volume = (Volume*)_volume->private_volume;
574 uint32 maxCount = *_num;
575 uint32 count = 0;
577 while (count < maxCount && bufferSize > sizeof(struct dirent)) {
578 ino_t id;
579 size_t length = bufferSize - sizeof(struct dirent) + 1;
581 status_t status = iterator->GetNext(dirent->d_name, &length, &id);
582 if (status == B_ENTRY_NOT_FOUND)
583 break;
585 if (status == B_BUFFER_OVERFLOW) {
586 // the remaining name buffer length was too small
587 if (count == 0)
588 return B_BUFFER_OVERFLOW;
589 break;
592 if (status != B_OK)
593 return status;
595 dirent->d_dev = volume->ID();
596 dirent->d_ino = id;
597 dirent->d_reclen = sizeof(struct dirent) + length;
599 bufferSize -= dirent->d_reclen;
600 dirent = (struct dirent*)((uint8*)dirent + dirent->d_reclen);
601 count++;
604 *_num = count;
605 TRACE("exfat_read_dir end\n");
606 return B_OK;
610 static status_t
611 exfat_rewind_dir(fs_volume * /*_volume*/, fs_vnode * /*node*/, void *_cookie)
613 DirectoryIterator* iterator = (DirectoryIterator*)_cookie;
615 return iterator->Rewind();
619 static status_t
620 exfat_close_dir(fs_volume * /*_volume*/, fs_vnode * /*node*/, void * /*_cookie*/)
622 return B_OK;
626 static status_t
627 exfat_free_dir_cookie(fs_volume *_volume, fs_vnode *_node, void *_cookie)
629 delete (DirectoryIterator*)_cookie;
630 return B_OK;
634 fs_volume_ops gExfatVolumeOps = {
635 &exfat_unmount,
636 &exfat_read_fs_info,
637 NULL, // write_fs_info()
638 NULL, // fs_sync,
639 &exfat_get_vnode,
643 fs_vnode_ops gExfatVnodeOps = {
644 /* vnode operations */
645 &exfat_lookup,
646 NULL,
647 &exfat_put_vnode,
648 NULL, // exfat_remove_vnode,
650 /* VM file access */
651 &exfat_can_page,
652 &exfat_read_pages,
653 NULL, // exfat_write_pages,
655 NULL, // io()
656 NULL, // cancel_io()
658 &exfat_get_file_map,
660 &exfat_ioctl,
661 NULL,
662 NULL, // fs_select
663 NULL, // fs_deselect
664 NULL, // fs_fsync,
666 &exfat_read_link,
667 NULL, // fs_create_symlink,
669 NULL, // fs_link,
670 NULL, // fs_unlink,
671 NULL, // fs_rename,
673 &exfat_access,
674 &exfat_read_stat,
675 NULL, // fs_write_stat,
676 NULL, // fs_preallocate
678 /* file operations */
679 NULL, // fs_create,
680 &exfat_open,
681 &exfat_close,
682 &exfat_free_cookie,
683 &exfat_read,
684 NULL, // fs_write,
686 /* directory operations */
687 NULL, // fs_create_dir,
688 NULL, // fs_remove_dir,
689 &exfat_open_dir,
690 &exfat_close_dir,
691 &exfat_free_dir_cookie,
692 &exfat_read_dir,
693 &exfat_rewind_dir,
695 /* attribute directory operations */
696 NULL, // fs_open_attr_dir,
697 NULL, // fs_close_attr_dir,
698 NULL, // fs_free_attr_dir_cookie,
699 NULL, // fs_read_attr_dir,
700 NULL, // fs_rewind_attr_dir,
702 /* attribute operations */
703 NULL, // fs_create_attr,
704 NULL, // fs_open_attr,
705 NULL, // fs_close_attr,
706 NULL, // fs_free_attr_cookie,
707 NULL, // fs_read_attr,
708 NULL, // fs_write_attr,
709 NULL, // fs_read_attr_stat,
710 NULL, // fs_write_attr_stat,
711 NULL, // fs_rename_attr,
712 NULL, // fs_remove_attr,
716 static file_system_module_info sExfatFileSystem = {
718 "file_systems/exfat" B_CURRENT_FS_API_VERSION,
720 NULL,
723 "exfat", // short_name
724 "ExFAT File System", // pretty_name
725 0, // DDM flags
727 // scanning
728 exfat_identify_partition,
729 exfat_scan_partition,
730 exfat_free_identify_partition_cookie,
731 NULL, // free_partition_content_cookie()
733 &exfat_mount,
735 NULL,
739 module_info *modules[] = {
740 (module_info *)&sExfatFileSystem,
741 NULL,