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