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.
5 *
6 * Distributed under the terms of the MIT License.
7 *
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
12 */
13
14
15#include <dirent.h>
16#include <unistd.h>
17#include <util/kernel_cpp.h>
18#include <string.h>
19
20#include <new>
21
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>
29#include <file_systems/fs_ops_support.h>
30
31#include "DirectoryIterator.h"
32#include "exfat.h"
33#include "Inode.h"
34#include "Utility.h"
35
36
37//#define TRACE_EXFAT
38#ifdef TRACE_EXFAT
39#	define TRACE(x...) dprintf("\33[34mexfat:\33[0m " x)
40#else
41#	define TRACE(x...) ;
42#endif
43#define ERROR(x...) dprintf("\33[34mexfat:\33[0m " x)
44
45
46#define EXFAT_IO_SIZE	65536
47
48
49struct identify_cookie {
50	exfat_super_block super_block;
51	char name[B_FILE_NAME_LENGTH];
52};
53
54
55//!	exfat_io() callback hook
56static status_t
57iterative_io_get_vecs_hook(void* cookie, io_request* request, off_t offset,
58	size_t size, struct file_io_vec* vecs, size_t* _count)
59{
60	Inode* inode = (Inode*)cookie;
61
62	return file_map_translate(inode->Map(), offset, size, vecs, _count,
63		inode->GetVolume()->BlockSize());
64}
65
66
67//!	exfat_io() callback hook
68static status_t
69iterative_io_finished_hook(void* cookie, io_request* request, status_t status,
70	bool partialTransfer, size_t bytesTransferred)
71{
72	Inode* inode = (Inode*)cookie;
73	rw_lock_read_unlock(inode->Lock());
74	return B_OK;
75}
76
77
78//	#pragma mark - Scanning
79
80
81static float
82exfat_identify_partition(int fd, partition_data* partition, void** _cookie)
83{
84	struct exfat_super_block superBlock;
85	status_t status = Volume::Identify(fd, &superBlock);
86	if (status != B_OK)
87		return -1;
88
89	identify_cookie* cookie = new (std::nothrow)identify_cookie;
90	if (cookie == NULL)
91		return -1;
92
93	memcpy(&cookie->super_block, &superBlock, sizeof(exfat_super_block));
94	memset(cookie->name, 0, sizeof(cookie->name));
95		// zero out volume name
96
97	uint32 rootDirCluster = superBlock.RootDirCluster();
98	uint32 blockSize = 1 << superBlock.BlockShift();
99	uint32 clusterSize = blockSize << superBlock.BlocksPerClusterShift();
100	uint64 rootDirectoryOffset = EXFAT_SUPER_BLOCK_OFFSET
101		+ (uint64)superBlock.FirstDataBlock() * blockSize
102		+ (rootDirCluster - 2) * clusterSize;
103	struct exfat_entry entry;
104	size_t entrySize = sizeof(struct exfat_entry);
105	for (uint32 i = 0; read_pos(fd, rootDirectoryOffset + i * entrySize,
106			&entry, entrySize) == (ssize_t)entrySize; i++) {
107		if (entry.type == EXFAT_ENTRY_TYPE_NOT_IN_USE
108			|| entry.type == EXFAT_ENTRY_TYPE_LABEL) {
109			if (get_volume_name(&entry, cookie->name, sizeof(cookie->name))
110					!= B_OK) {
111				delete cookie;
112				return -1;
113			}
114			break;
115		}
116	}
117
118	if (cookie->name[0] == '\0') {
119		off_t fileSystemSize = (off_t)superBlock.NumBlocks()
120			<< superBlock.BlockShift();
121		get_default_volume_name(fileSystemSize, cookie->name,
122			sizeof(cookie->name));
123	}
124
125	*_cookie = cookie;
126	return 0.8f;
127}
128
129
130static status_t
131exfat_scan_partition(int fd, partition_data* partition, void* _cookie)
132{
133	identify_cookie* cookie = (identify_cookie*)_cookie;
134
135	partition->status = B_PARTITION_VALID;
136	partition->flags |= B_PARTITION_FILE_SYSTEM;
137	partition->content_size = cookie->super_block.NumBlocks()
138		<< cookie->super_block.BlockShift();
139	partition->block_size = 1 << cookie->super_block.BlockShift();
140	partition->content_name = strdup(cookie->name);
141
142	return partition->content_name != NULL ? B_OK : B_NO_MEMORY;
143}
144
145
146static void
147exfat_free_identify_partition_cookie(partition_data* partition, void* _cookie)
148{
149	delete (identify_cookie*)_cookie;
150}
151
152
153//	#pragma mark -
154
155
156static status_t
157exfat_mount(fs_volume* _volume, const char* device, uint32 flags,
158	const char* args, ino_t* _rootID)
159{
160	Volume* volume = new(std::nothrow) Volume(_volume);
161	if (volume == NULL)
162		return B_NO_MEMORY;
163
164	// TODO: this is a bit hacky: we can't use publish_vnode() to publish
165	// the root node, or else its file cache cannot be created (we could
166	// create it later, though). Therefore we're using get_vnode() in Mount(),
167	// but that requires us to export our volume data before calling it.
168	_volume->private_volume = volume;
169	_volume->ops = &gExfatVolumeOps;
170
171	status_t status = volume->Mount(device, flags);
172	if (status != B_OK) {
173		ERROR("Failed mounting the volume. Error: %s\n", strerror(status));
174		delete volume;
175		return status;
176	}
177
178	*_rootID = volume->RootNode()->ID();
179	return B_OK;
180}
181
182
183static status_t
184exfat_unmount(fs_volume *_volume)
185{
186	Volume* volume = (Volume *)_volume->private_volume;
187
188	status_t status = volume->Unmount();
189	delete volume;
190
191	return status;
192}
193
194
195static status_t
196exfat_read_fs_info(fs_volume* _volume, struct fs_info* info)
197{
198	Volume* volume = (Volume*)_volume->private_volume;
199
200	// File system flags
201	info->flags = B_FS_IS_PERSISTENT
202		| (volume->IsReadOnly() ? B_FS_IS_READONLY : 0);
203	info->io_size = EXFAT_IO_SIZE;
204	info->block_size = volume->BlockSize();
205	info->total_blocks = volume->SuperBlock().NumBlocks();
206	info->free_blocks = 0; //volume->NumFreeBlocks();
207
208	// Volume name
209	strlcpy(info->volume_name, volume->Name(), sizeof(info->volume_name));
210
211	// File system name
212	strlcpy(info->fsh_name, "exfat", sizeof(info->fsh_name));
213
214	return B_OK;
215}
216
217
218//	#pragma mark -
219
220
221static status_t
222exfat_get_vnode(fs_volume* _volume, ino_t id, fs_vnode* _node, int* _type,
223	uint32* _flags, bool reenter)
224{
225	TRACE("get_vnode %" B_PRIdINO "\n", id);
226	Volume* volume = (Volume*)_volume->private_volume;
227
228	Inode* inode = new(std::nothrow) Inode(volume, id);
229	if (inode == NULL)
230		return B_NO_MEMORY;
231
232	status_t status = inode->InitCheck();
233	if (status != B_OK)
234		delete inode;
235
236	if (status == B_OK) {
237		_node->private_node = inode;
238		_node->ops = &gExfatVnodeOps;
239		*_type = inode->Mode();
240		*_flags = 0;
241	} else
242		ERROR("get_vnode: InitCheck() failed. Error: %s\n", strerror(status));
243
244	return status;
245}
246
247
248static status_t
249exfat_put_vnode(fs_volume* _volume, fs_vnode* _node, bool reenter)
250{
251	delete (Inode*)_node->private_node;
252	return B_OK;
253}
254
255
256static bool
257exfat_can_page(fs_volume* _volume, fs_vnode* _node, void* _cookie)
258{
259	return true;
260}
261
262
263static status_t
264exfat_read_pages(fs_volume* _volume, fs_vnode* _node, void* _cookie,
265	off_t pos, const iovec* vecs, size_t count, size_t* _numBytes)
266{
267	Volume* volume = (Volume*)_volume->private_volume;
268	Inode* inode = (Inode*)_node->private_node;
269
270	if (inode->FileCache() == NULL)
271		return B_BAD_VALUE;
272
273	rw_lock_read_lock(inode->Lock());
274
275	uint32 vecIndex = 0;
276	size_t vecOffset = 0;
277	size_t bytesLeft = *_numBytes;
278	status_t status;
279
280	while (true) {
281		file_io_vec fileVecs[8];
282		size_t fileVecCount = 8;
283
284		status = file_map_translate(inode->Map(), pos, bytesLeft, fileVecs,
285			&fileVecCount, 0);
286		if (status != B_OK && status != B_BUFFER_OVERFLOW)
287			break;
288
289		bool bufferOverflow = status == B_BUFFER_OVERFLOW;
290
291		size_t bytes = bytesLeft;
292		status = read_file_io_vec_pages(volume->Device(), fileVecs,
293			fileVecCount, vecs, count, &vecIndex, &vecOffset, &bytes);
294		if (status != B_OK || !bufferOverflow)
295			break;
296
297		pos += bytes;
298		bytesLeft -= bytes;
299	}
300
301	rw_lock_read_unlock(inode->Lock());
302
303	return status;
304}
305
306
307static status_t
308exfat_io(fs_volume* _volume, fs_vnode* _node, void* _cookie,
309	io_request* request)
310{
311	Volume* volume = (Volume*)_volume->private_volume;
312	Inode* inode = (Inode*)_node->private_node;
313
314#ifndef EXFAT_SHELL
315	if (io_request_is_write(request) && volume->IsReadOnly()) {
316		notify_io_request(request, B_READ_ONLY_DEVICE);
317		return B_READ_ONLY_DEVICE;
318	}
319#endif
320
321	if (inode->FileCache() == NULL) {
322#ifndef EXFAT_SHELL
323		notify_io_request(request, B_BAD_VALUE);
324#endif
325		return B_BAD_VALUE;
326	}
327
328	// We lock the node here and will unlock it in the "finished" hook.
329	rw_lock_read_lock(inode->Lock());
330
331	return do_iterative_fd_io(volume->Device(), request,
332		iterative_io_get_vecs_hook, iterative_io_finished_hook, inode);
333}
334
335
336static status_t
337exfat_get_file_map(fs_volume* _volume, fs_vnode* _node, off_t offset,
338	size_t size, struct file_io_vec* vecs, size_t* _count)
339{
340	TRACE("exfat_get_file_map()\n");
341	Inode* inode = (Inode*)_node->private_node;
342	size_t index = 0, max = *_count;
343
344	while (true) {
345		off_t blockOffset;
346		off_t blockLength;
347		status_t status = inode->FindBlock(offset, blockOffset, &blockLength);
348		if (status != B_OK)
349			return status;
350
351		if (index > 0 && (vecs[index - 1].offset
352				== blockOffset - vecs[index - 1].length)) {
353			vecs[index - 1].length += blockLength;
354		} else {
355			if (index >= max) {
356				// we're out of file_io_vecs; let's bail out
357				*_count = index;
358				return B_BUFFER_OVERFLOW;
359			}
360
361			vecs[index].offset = blockOffset;
362			vecs[index].length = blockLength;
363			index++;
364		}
365
366		offset += blockLength;
367		size -= blockLength;
368
369		if ((off_t)size <= vecs[index - 1].length || offset >= inode->Size()) {
370			// We're done!
371			*_count = index;
372			TRACE("exfat_get_file_map for inode %" B_PRIdINO"\n", inode->ID());
373			return B_OK;
374		}
375	}
376
377	// can never get here
378	return B_ERROR;
379}
380
381
382//	#pragma mark -
383
384
385static status_t
386exfat_lookup(fs_volume* _volume, fs_vnode* _directory, const char* name,
387	ino_t* _vnodeID)
388{
389	TRACE("exfat_lookup: name address: %p (%s)\n", name, name);
390	Volume* volume = (Volume*)_volume->private_volume;
391	Inode* directory = (Inode*)_directory->private_node;
392
393	// check access permissions
394	status_t status = directory->CheckPermissions(X_OK);
395	if (status < B_OK)
396		return status;
397
398	status = DirectoryIterator(directory).Lookup(name, strlen(name), _vnodeID);
399	if (status != B_OK) {
400		ERROR("exfat_lookup: name %s (%s)\n", name, strerror(status));
401		if (status == B_ENTRY_NOT_FOUND)
402			entry_cache_add_missing(volume->ID(), directory->ID(), name);
403		return status;
404	}
405
406	TRACE("exfat_lookup: ID %" B_PRIdINO "\n", *_vnodeID);
407	entry_cache_add(volume->ID(), directory->ID(), name, *_vnodeID);
408
409	return get_vnode(volume->FSVolume(), *_vnodeID, NULL);
410}
411
412
413static status_t
414exfat_ioctl(fs_volume* _volume, fs_vnode* _node, void* _cookie, uint32 cmd,
415	void* buffer, size_t bufferLength)
416{
417	TRACE("ioctl: %" B_PRIu32 "\n", cmd);
418
419	/*Volume* volume = (Volume*)_volume->private_volume;*/
420	return B_DEV_INVALID_IOCTL;
421}
422
423
424static status_t
425exfat_read_stat(fs_volume* _volume, fs_vnode* _node, struct stat* stat)
426{
427	Inode* inode = (Inode*)_node->private_node;
428
429	stat->st_dev = inode->GetVolume()->ID();
430	stat->st_ino = inode->ID();
431	stat->st_nlink = 1;
432	stat->st_blksize = EXFAT_IO_SIZE;
433
434	stat->st_uid = inode->UserID();
435	stat->st_gid = inode->GroupID();
436	stat->st_mode = inode->Mode();
437	stat->st_type = 0;
438
439	inode->GetAccessTime(stat->st_atim);
440	inode->GetModificationTime(stat->st_mtim);
441	inode->GetChangeTime(stat->st_ctim);
442	inode->GetCreationTime(stat->st_crtim);
443
444	stat->st_size = inode->Size();
445	stat->st_blocks = (inode->Size() + 511) / 512;
446
447	return B_OK;
448}
449
450
451static status_t
452exfat_open(fs_volume* /*_volume*/, fs_vnode* _node, int openMode,
453	void** _cookie)
454{
455	Inode* inode = (Inode*)_node->private_node;
456
457	// opening a directory read-only is allowed, although you can't read
458	// any data from it.
459	if (inode->IsDirectory() && (openMode & O_RWMASK) != 0)
460		return B_IS_A_DIRECTORY;
461
462	status_t status =  inode->CheckPermissions(open_mode_to_access(openMode)
463		| (openMode & O_TRUNC ? W_OK : 0));
464	if (status != B_OK)
465		return status;
466
467	// Prepare the cookie
468	file_cookie* cookie = new(std::nothrow) file_cookie;
469	if (cookie == NULL)
470		return B_NO_MEMORY;
471	ObjectDeleter<file_cookie> cookieDeleter(cookie);
472
473	cookie->open_mode = openMode & EXFAT_OPEN_MODE_USER_MASK;
474	cookie->last_size = inode->Size();
475	cookie->last_notification = system_time();
476
477	if ((openMode & O_NOCACHE) != 0 && inode->FileCache() != NULL) {
478		// Disable the file cache, if requested?
479		status = file_cache_disable(inode->FileCache());
480		if (status != B_OK)
481			return status;
482	}
483
484	cookieDeleter.Detach();
485	*_cookie = cookie;
486
487	return B_OK;
488}
489
490
491static status_t
492exfat_read(fs_volume* _volume, fs_vnode* _node, void* _cookie, off_t pos,
493	void* buffer, size_t* _length)
494{
495	Inode* inode = (Inode*)_node->private_node;
496
497	if (!inode->IsFile()) {
498		*_length = 0;
499		return inode->IsDirectory() ? B_IS_A_DIRECTORY : B_BAD_VALUE;
500	}
501
502	return inode->ReadAt(pos, (uint8*)buffer, _length);
503}
504
505
506static status_t
507exfat_close(fs_volume *_volume, fs_vnode *_node, void *_cookie)
508{
509	return B_OK;
510}
511
512
513static status_t
514exfat_free_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie)
515{
516	file_cookie* cookie = (file_cookie*)_cookie;
517	Volume* volume = (Volume*)_volume->private_volume;
518	Inode* inode = (Inode*)_node->private_node;
519
520	if (inode->Size() != cookie->last_size)
521		notify_stat_changed(volume->ID(), -1, inode->ID(), B_STAT_SIZE);
522
523	delete cookie;
524	return B_OK;
525}
526
527
528static status_t
529exfat_access(fs_volume* _volume, fs_vnode* _node, int accessMode)
530{
531	Inode* inode = (Inode*)_node->private_node;
532	return inode->CheckPermissions(accessMode);
533}
534
535
536static status_t
537exfat_read_link(fs_volume *_volume, fs_vnode *_node, char *buffer,
538	size_t *_bufferSize)
539{
540	Inode* inode = (Inode*)_node->private_node;
541
542	if (!inode->IsSymLink())
543		return B_BAD_VALUE;
544
545	status_t result = inode->ReadAt(0, reinterpret_cast<uint8*>(buffer),
546		_bufferSize);
547	if (result != B_OK)
548		return result;
549
550	*_bufferSize = inode->Size();
551
552	return B_OK;
553}
554
555
556//	#pragma mark - Directory functions
557
558
559static status_t
560exfat_open_dir(fs_volume* /*_volume*/, fs_vnode* _node, void** _cookie)
561{
562	Inode* inode = (Inode*)_node->private_node;
563	status_t status = inode->CheckPermissions(R_OK);
564	if (status < B_OK)
565		return status;
566
567	if (!inode->IsDirectory())
568		return B_NOT_A_DIRECTORY;
569
570	DirectoryIterator* iterator = new(std::nothrow) DirectoryIterator(inode);
571	if (iterator == NULL || iterator->InitCheck() != B_OK) {
572		delete iterator;
573		return B_NO_MEMORY;
574	}
575
576	*_cookie = iterator;
577	return B_OK;
578}
579
580
581static status_t
582exfat_read_dir(fs_volume *_volume, fs_vnode *_node, void *_cookie,
583	struct dirent *dirent, size_t bufferSize, uint32 *_num)
584{
585	TRACE("exfat_read_dir\n");
586	DirectoryIterator* iterator = (DirectoryIterator*)_cookie;
587	Volume* volume = (Volume*)_volume->private_volume;
588
589	uint32 maxCount = *_num;
590	uint32 count = 0;
591
592	while (count < maxCount && bufferSize > sizeof(struct dirent)) {
593		ino_t id;
594		size_t length = bufferSize - offsetof(struct dirent, d_name);
595
596		status_t status = iterator->GetNext(dirent->d_name, &length, &id);
597		if (status == B_ENTRY_NOT_FOUND)
598			break;
599
600		if (status == B_BUFFER_OVERFLOW) {
601			// the remaining name buffer length was too small
602			if (count == 0)
603				return B_BUFFER_OVERFLOW;
604			break;
605		}
606
607		if (status != B_OK)
608			return status;
609
610		dirent->d_dev = volume->ID();
611		dirent->d_ino = id;
612
613		dirent = next_dirent(dirent, length, bufferSize);
614		count++;
615	}
616
617	*_num = count;
618	TRACE("exfat_read_dir end\n");
619	return B_OK;
620}
621
622
623static status_t
624exfat_rewind_dir(fs_volume * /*_volume*/, fs_vnode * /*node*/, void *_cookie)
625{
626	DirectoryIterator* iterator = (DirectoryIterator*)_cookie;
627
628	return iterator->Rewind();
629}
630
631
632static status_t
633exfat_close_dir(fs_volume * /*_volume*/, fs_vnode * /*node*/, void * /*_cookie*/)
634{
635	return B_OK;
636}
637
638
639static status_t
640exfat_free_dir_cookie(fs_volume *_volume, fs_vnode *_node, void *_cookie)
641{
642	delete (DirectoryIterator*)_cookie;
643	return B_OK;
644}
645
646
647fs_volume_ops gExfatVolumeOps = {
648	&exfat_unmount,
649	&exfat_read_fs_info,
650	NULL,	// write_fs_info()
651	NULL,	// fs_sync,
652	&exfat_get_vnode,
653};
654
655
656fs_vnode_ops gExfatVnodeOps = {
657	/* vnode operations */
658	&exfat_lookup,
659	NULL,
660	&exfat_put_vnode,
661	NULL,	// exfat_remove_vnode,
662
663	/* VM file access */
664	&exfat_can_page,
665	&exfat_read_pages,
666	NULL,	// exfat_write_pages,
667
668	NULL,	// io()
669	NULL,	// cancel_io()
670
671	&exfat_get_file_map,
672
673	&exfat_ioctl,
674	NULL,
675	NULL,	// fs_select
676	NULL,	// fs_deselect
677	NULL,	// fs_fsync,
678
679	&exfat_read_link,
680	NULL,	// fs_create_symlink,
681
682	NULL,	// fs_link,
683	NULL,	// fs_unlink,
684	NULL,	// fs_rename,
685
686	&exfat_access,
687	&exfat_read_stat,
688	NULL,	// fs_write_stat,
689	NULL,	// fs_preallocate
690
691	/* file operations */
692	NULL,	// fs_create,
693	&exfat_open,
694	&exfat_close,
695	&exfat_free_cookie,
696	&exfat_read,
697	NULL,	//	fs_write,
698
699	/* directory operations */
700	NULL, 	// fs_create_dir,
701	NULL, 	// fs_remove_dir,
702	&exfat_open_dir,
703	&exfat_close_dir,
704	&exfat_free_dir_cookie,
705	&exfat_read_dir,
706	&exfat_rewind_dir,
707
708	/* attribute directory operations */
709	NULL, 	// fs_open_attr_dir,
710	NULL,	// fs_close_attr_dir,
711	NULL,	// fs_free_attr_dir_cookie,
712	NULL,	// fs_read_attr_dir,
713	NULL,	// fs_rewind_attr_dir,
714
715	/* attribute operations */
716	NULL,	// fs_create_attr,
717	NULL,	// fs_open_attr,
718	NULL,	// fs_close_attr,
719	NULL,	// fs_free_attr_cookie,
720	NULL,	// fs_read_attr,
721	NULL,	// fs_write_attr,
722	NULL,	// fs_read_attr_stat,
723	NULL,	// fs_write_attr_stat,
724	NULL,	// fs_rename_attr,
725	NULL,	// fs_remove_attr,
726};
727
728
729static file_system_module_info sExfatFileSystem = {
730	{
731		"file_systems/exfat" B_CURRENT_FS_API_VERSION,
732		0,
733		NULL,
734	},
735
736	"exfat",						// short_name
737	"ExFAT File System",			// pretty_name
738	0,								// DDM flags
739
740	// scanning
741	exfat_identify_partition,
742	exfat_scan_partition,
743	exfat_free_identify_partition_cookie,
744	NULL,	// free_partition_content_cookie()
745
746	&exfat_mount,
747
748	NULL,
749};
750
751
752module_info *modules[] = {
753	(module_info *)&sExfatFileSystem,
754	NULL,
755};
756