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.
9 * Axel Dörfler, axeld@pinc-software.de
10 * Jérôme Duval, korli@users.berlios.de
11 * John Scipione, jscipione@gmail.com
17 #include <util/kernel_cpp.h>
22 #include <AutoDeleter.h>
25 #include <io_requests.h>
26 #include <NodeMonitor.h>
27 #include <StorageDefs.h>
28 #include <util/AutoLock.h>
30 #include "DirectoryIterator.h"
38 # define TRACE(x...) dprintf("\33[34mexfat:\33[0m " x)
40 # define TRACE(x...) ;
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
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
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());
77 // #pragma mark - Scanning
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
);
88 identify_cookie
* cookie
= new (std::nothrow
)identify_cookie
;
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
))
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
));
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
;
146 exfat_free_identify_partition_cookie(partition_data
* partition
, void* _cookie
)
148 delete (identify_cookie
*)_cookie
;
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
);
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
));
177 *_rootID
= volume
->RootNode()->ID();
183 exfat_unmount(fs_volume
*_volume
)
185 Volume
* volume
= (Volume
*)_volume
->private_volume
;
187 status_t status
= volume
->Unmount();
195 exfat_read_fs_info(fs_volume
* _volume
, struct fs_info
* info
)
197 Volume
* volume
= (Volume
*)_volume
->private_volume
;
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();
208 strlcpy(info
->volume_name
, volume
->Name(), sizeof(info
->volume_name
));
211 strlcpy(info
->fsh_name
, "exfat", sizeof(info
->fsh_name
));
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
);
231 status_t status
= inode
->InitCheck();
235 if (status
== B_OK
) {
236 _node
->private_node
= inode
;
237 _node
->ops
= &gExfatVnodeOps
;
238 *_type
= inode
->Mode();
241 ERROR("get_vnode: InitCheck() failed. Error: %s\n", strerror(status
));
248 exfat_put_vnode(fs_volume
* _volume
, fs_vnode
* _node
, bool reenter
)
250 delete (Inode
*)_node
->private_node
;
256 exfat_can_page(fs_volume
* _volume
, fs_vnode
* _node
, void* _cookie
)
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
)
272 rw_lock_read_lock(inode
->Lock());
275 size_t vecOffset
= 0;
276 size_t bytesLeft
= *_numBytes
;
280 file_io_vec fileVecs
[8];
281 size_t fileVecCount
= 8;
283 status
= file_map_translate(inode
->Map(), pos
, bytesLeft
, fileVecs
,
285 if (status
!= B_OK
&& status
!= B_BUFFER_OVERFLOW
)
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
)
300 rw_lock_read_unlock(inode
->Lock());
307 exfat_io(fs_volume
* _volume
, fs_vnode
* _node
, void* _cookie
,
310 Volume
* volume
= (Volume
*)_volume
->private_volume
;
311 Inode
* inode
= (Inode
*)_node
->private_node
;
314 if (io_request_is_write(request
) && volume
->IsReadOnly()) {
315 notify_io_request(request
, B_READ_ONLY_DEVICE
);
316 return B_READ_ONLY_DEVICE
;
320 if (inode
->FileCache() == NULL
) {
322 notify_io_request(request
, 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
);
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
;
346 status_t status
= inode
->FindBlock(offset
, blockOffset
, &blockLength
);
350 if (index
> 0 && (vecs
[index
- 1].offset
351 == blockOffset
- vecs
[index
- 1].length
)) {
352 vecs
[index
- 1].length
+= blockLength
;
355 // we're out of file_io_vecs; let's bail out
357 return B_BUFFER_OVERFLOW
;
360 vecs
[index
].offset
= blockOffset
;
361 vecs
[index
].length
= blockLength
;
365 offset
+= blockLength
;
368 if ((off_t
)size
<= vecs
[index
- 1].length
|| offset
>= inode
->Size()) {
371 TRACE("exfat_get_file_map for inode %" B_PRIdINO
"\n", inode
->ID());
376 // can never get here
385 exfat_lookup(fs_volume
* _volume
, fs_vnode
* _directory
, const char* name
,
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
);
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
));
403 TRACE("exfat_lookup: ID %" B_PRIdINO
"\n", *_vnodeID
);
405 return get_vnode(volume
->FSVolume(), *_vnodeID
, NULL
);
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
;
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();
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();
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;
448 exfat_open(fs_volume
* /*_volume*/, fs_vnode
* _node
, int openMode
,
451 Inode
* inode
= (Inode
*)_node
->private_node
;
453 // opening a directory read-only is allowed, although you can't read
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));
463 // Prepare the cookie
464 file_cookie
* cookie
= new(std::nothrow
) file_cookie
;
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());
480 cookieDeleter
.Detach();
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()) {
495 return inode
->IsDirectory() ? B_IS_A_DIRECTORY
: B_BAD_VALUE
;
498 return inode
->ReadAt(pos
, (uint8
*)buffer
, _length
);
503 exfat_close(fs_volume
*_volume
, fs_vnode
*_node
, void *_cookie
)
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
);
525 exfat_access(fs_volume
* _volume
, fs_vnode
* _node
, int accessMode
)
527 Inode
* inode
= (Inode
*)_node
->private_node
;
528 return inode
->CheckPermissions(accessMode
);
533 exfat_read_link(fs_volume
*_volume
, fs_vnode
*_node
, char *buffer
,
536 Inode
* inode
= (Inode
*)_node
->private_node
;
537 return inode
->ReadAt(0, (uint8
*)buffer
, _bufferSize
);
541 // #pragma mark - Directory functions
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
);
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
) {
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
;
577 while (count
< maxCount
&& bufferSize
> sizeof(struct dirent
)) {
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
)
585 if (status
== B_BUFFER_OVERFLOW
) {
586 // the remaining name buffer length was too small
588 return B_BUFFER_OVERFLOW
;
595 dirent
->d_dev
= volume
->ID();
597 dirent
->d_reclen
= sizeof(struct dirent
) + length
;
599 bufferSize
-= dirent
->d_reclen
;
600 dirent
= (struct dirent
*)((uint8
*)dirent
+ dirent
->d_reclen
);
605 TRACE("exfat_read_dir end\n");
611 exfat_rewind_dir(fs_volume
* /*_volume*/, fs_vnode
* /*node*/, void *_cookie
)
613 DirectoryIterator
* iterator
= (DirectoryIterator
*)_cookie
;
615 return iterator
->Rewind();
620 exfat_close_dir(fs_volume
* /*_volume*/, fs_vnode
* /*node*/, void * /*_cookie*/)
627 exfat_free_dir_cookie(fs_volume
*_volume
, fs_vnode
*_node
, void *_cookie
)
629 delete (DirectoryIterator
*)_cookie
;
634 fs_volume_ops gExfatVolumeOps
= {
637 NULL
, // write_fs_info()
643 fs_vnode_ops gExfatVnodeOps
= {
644 /* vnode operations */
648 NULL
, // exfat_remove_vnode,
653 NULL
, // exfat_write_pages,
667 NULL
, // fs_create_symlink,
675 NULL
, // fs_write_stat,
676 NULL
, // fs_preallocate
678 /* file operations */
686 /* directory operations */
687 NULL
, // fs_create_dir,
688 NULL
, // fs_remove_dir,
691 &exfat_free_dir_cookie
,
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
,
723 "exfat", // short_name
724 "ExFAT File System", // pretty_name
728 exfat_identify_partition
,
729 exfat_scan_partition
,
730 exfat_free_identify_partition_cookie
,
731 NULL
, // free_partition_content_cookie()
739 module_info
*modules
[] = {
740 (module_info
*)&sExfatFileSystem
,