1 /*
2 * Copyright 1999, Be Incorporated. All Rights Reserved.
3 * This file may be used under the terms of the Be Sample Code License.
4 *
5 * Copyright 2001, pinc Software. All Rights Reserved.
6 *
7 * iso9960/multi-session, 1.0.0
8 */
9
10
11 #include <algorithm>
12 #include <ctype.h>
13
14 #ifndef FS_SHELL
15 # include <dirent.h>
16 # include <errno.h>
17 # include <fcntl.h>
18 # include <stdio.h>
19 # include <stdlib.h>
20 # include <string.h>
21 # include <sys/stat.h>
22 # include <time.h>
23 # include <unistd.h>
24
25 # include <KernelExport.h>
26 # include <NodeMonitor.h>
27 # include <fs_interface.h>
28 # include <fs_cache.h>
29
30 # include <fs_attr.h>
31 # include <fs_info.h>
32 # include <fs_index.h>
33 # include <fs_query.h>
34 # include <fs_volume.h>
35
36 # include <util/kernel_cpp.h>
37
38 // TODO: temporary solution as long as there is no public I/O requests API
39 # include <io_requests.h>
40 #endif
41
42 #include "iso9660.h"
43 #include "iso9660_identify.h"
44
45
46 //#define TRACE_ISO9660
47 #ifdef TRACE_ISO9660
48 # define TRACE(x) dprintf x
49 #else
50 # define TRACE(x) ;
51 #endif
52
53
54 struct identify_cookie {
55 iso9660_info info;
56 };
57
58 extern fs_volume_ops gISO9660VolumeOps;
59 extern fs_vnode_ops gISO9660VnodeOps;
60
61
62 //! fs_io() callback hook
63 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)64 iterative_io_get_vecs_hook(void* cookie, io_request* request, off_t offset,
65 size_t size, struct file_io_vec* vecs, size_t* _count)
66 {
67 iso9660_inode* node = (iso9660_inode*)cookie;
68
69 vecs->offset = offset + ((off_t)node->startLBN[FS_DATA_FORMAT]
70 * (off_t)node->volume->logicalBlkSize[FS_DATA_FORMAT]);
71 vecs->length = size;
72
73 *_count = 1;
74
75 return B_OK;
76 }
77
78
79 //! fs_io() callback hook
80 static status_t
iterative_io_finished_hook(void * cookie,io_request * request,status_t status,bool partialTransfer,size_t bytesTransferred)81 iterative_io_finished_hook(void* cookie, io_request* request, status_t status,
82 bool partialTransfer, size_t bytesTransferred)
83 {
84 // nothing to do here...
85 return B_OK;
86 }
87
88
89 // #pragma mark - Scanning
90
91
92 static float
fs_identify_partition(int fd,partition_data * partition,void ** _cookie)93 fs_identify_partition(int fd, partition_data* partition, void** _cookie)
94 {
95 iso9660_info* info = new iso9660_info;
96
97 status_t status = iso9660_fs_identify(fd, info);
98 if (status != B_OK) {
99 delete info;
100 return -1;
101 }
102
103 *_cookie = info;
104 return 0.6f;
105 }
106
107
108 static status_t
fs_scan_partition(int fd,partition_data * partition,void * _cookie)109 fs_scan_partition(int fd, partition_data* partition, void* _cookie)
110 {
111 iso9660_info* info = (iso9660_info*)_cookie;
112
113 partition->status = B_PARTITION_VALID;
114 partition->flags |= B_PARTITION_FILE_SYSTEM | B_PARTITION_READ_ONLY ;
115 partition->block_size = ISO_PVD_SIZE;
116 partition->content_size = ISO_PVD_SIZE * info->max_blocks;
117 partition->content_name = strdup(info->PreferredName());
118
119 if (partition->content_name == NULL)
120 return B_NO_MEMORY;
121
122 return B_OK;
123 }
124
125
126 static void
fs_free_identify_partition_cookie(partition_data * partition,void * _cookie)127 fs_free_identify_partition_cookie(partition_data* partition, void* _cookie)
128 {
129 delete (iso9660_info*)_cookie;
130 }
131
132
133 // #pragma mark - FS hooks
134
135
136 static status_t
fs_mount(fs_volume * _volume,const char * device,uint32 flags,const char * args,ino_t * _rootID)137 fs_mount(fs_volume* _volume, const char* device, uint32 flags,
138 const char* args, ino_t* _rootID)
139 {
140 bool allowJoliet = true;
141 iso9660_volume* volume;
142
143 // Check for a 'nojoliet' parm
144 // all we check for is the existance of 'nojoliet' in the parms.
145 if (args != NULL) {
146 uint32 i;
147 char* spot;
148 char* buf = strdup(args);
149
150 uint32 len = strlen(buf);
151 // lower case the parms data
152 for (i = 0; i < len + 1; i++)
153 buf[i] = tolower(buf[i]);
154
155 // look for nojoliet
156 spot = strstr(buf, "nojoliet");
157 if (spot != NULL)
158 allowJoliet = false;
159
160 free(buf);
161 }
162
163 // Try and mount volume as an ISO volume.
164 status_t result = ISOMount(device, O_RDONLY, &volume, allowJoliet);
165 if (result == B_OK) {
166 *_rootID = ISO_ROOTNODE_ID;
167
168 _volume->private_volume = volume;
169 _volume->ops = &gISO9660VolumeOps;
170 volume->volume = _volume;
171 volume->id = _volume->id;
172
173 result = publish_vnode(_volume, *_rootID, &volume->rootDirRec,
174 &gISO9660VnodeOps,
175 volume->rootDirRec.attr.stat[FS_DATA_FORMAT].st_mode, 0);
176 if (result != B_OK) {
177 block_cache_delete(volume->fBlockCache, false);
178 free(volume);
179 result = B_ERROR;
180 }
181 }
182 return result;
183 }
184
185
186 static status_t
fs_unmount(fs_volume * _volume)187 fs_unmount(fs_volume* _volume)
188 {
189 status_t result = B_NO_ERROR;
190 iso9660_volume* volume = (iso9660_volume*)_volume->private_volume;
191
192 TRACE(("fs_unmount - ENTER\n"));
193
194 // Unlike in BeOS, we need to put the reference to our root node ourselves
195 put_vnode(_volume, ISO_ROOTNODE_ID);
196
197 block_cache_delete(volume->fBlockCache, false);
198 close(volume->fdOfSession);
199 result = close(volume->fd);
200
201 free(volume);
202
203 TRACE(("fs_unmount - EXIT, result is %s\n", strerror(result)));
204 return result;
205 }
206
207
208 static status_t
fs_read_fs_stat(fs_volume * _volume,struct fs_info * info)209 fs_read_fs_stat(fs_volume* _volume, struct fs_info* info)
210 {
211 iso9660_volume* volume = (iso9660_volume*)_volume->private_volume;
212
213 info->flags = B_FS_IS_PERSISTENT | B_FS_IS_READONLY;
214 info->block_size = volume->logicalBlkSize[FS_DATA_FORMAT];
215 info->io_size = 65536;
216 info->total_blocks = volume->volSpaceSize[FS_DATA_FORMAT];
217 info->free_blocks = 0;
218
219 strlcpy(info->device_name, volume->devicePath, sizeof(info->device_name));
220 strlcpy(info->volume_name, volume->volIDString, sizeof(info->volume_name));
221
222 // strip trailing spaces
223 int i;
224 for (i = strlen(info->volume_name) - 1; i >=0 ; i--) {
225 if (info->volume_name[i] != ' ')
226 break;
227 }
228
229 if (i < 0)
230 strcpy(info->volume_name, "UNKNOWN");
231 else
232 info->volume_name[i + 1] = 0;
233
234 strcpy(info->fsh_name, "iso9660");
235 return B_OK;
236 }
237
238
239 static status_t
fs_get_vnode_name(fs_volume * _volume,fs_vnode * _node,char * buffer,size_t bufferSize)240 fs_get_vnode_name(fs_volume* _volume, fs_vnode* _node, char* buffer,
241 size_t bufferSize)
242 {
243 iso9660_inode* node = (iso9660_inode*)_node->private_node;
244
245 strlcpy(buffer, node->name, bufferSize);
246 return B_OK;
247 }
248
249
250 static status_t
fs_walk(fs_volume * _volume,fs_vnode * _base,const char * file,ino_t * _vnodeID)251 fs_walk(fs_volume* _volume, fs_vnode* _base, const char* file, ino_t* _vnodeID)
252 {
253 iso9660_volume* volume = (iso9660_volume*)_volume->private_volume;
254 iso9660_inode* baseNode = (iso9660_inode*)_base->private_node;
255 iso9660_inode* newNode = NULL;
256
257 TRACE(("fs_walk - looking for %s in dir file of length %d\n", file,
258 (int)baseNode->dataLen[FS_DATA_FORMAT]));
259
260 if (strcmp(file, ".") == 0) {
261 // base directory
262 TRACE(("fs_walk - found \".\" file.\n"));
263 *_vnodeID = baseNode->id;
264 return get_vnode(_volume, *_vnodeID, NULL);
265 } else if (strcmp(file, "..") == 0) {
266 // parent directory
267 TRACE(("fs_walk - found \"..\" file.\n"));
268 *_vnodeID = baseNode->parID;
269 return get_vnode(_volume, *_vnodeID, NULL);
270 }
271
272 // look up file in the directory
273 uint32 dataLength = baseNode->dataLen[FS_DATA_FORMAT];
274 status_t result = ENOENT;
275 size_t totalRead = 0;
276 off_t block = baseNode->startLBN[FS_DATA_FORMAT];
277 bool done = false;
278
279 while (totalRead < dataLength && !done) {
280 off_t cachedBlock = block;
281 char* blockData = (char*)block_cache_get(volume->fBlockCache, block);
282 if (blockData == NULL)
283 break;
284
285 size_t bytesRead = 0;
286 off_t blockBytesRead = 0;
287 iso9660_inode node;
288 int initResult;
289
290 TRACE(("fs_walk - read buffer from disk at LBN %lld into buffer "
291 "%p.\n", block, blockData));
292
293 // Move to the next block if necessary
294 // Don't go over end of buffer, if dir record sits on boundary.
295
296 while (blockBytesRead < volume->logicalBlkSize[FS_DATA_FORMAT]
297 && totalRead + blockBytesRead < dataLength
298 && blockData[0] != 0
299 && !done) {
300 initResult = InitNode(volume, &node, blockData, &bytesRead);
301 TRACE(("fs_walk - InitNode returned %s, filename %s, %u bytes "
302 "read\n", strerror(initResult), node.name, (unsigned)bytesRead));
303
304 if (initResult == B_OK) {
305 if ((node.flags & ISO_IS_ASSOCIATED_FILE) == 0
306 && !strcmp(node.name, file)) {
307 TRACE(("fs_walk - success, found vnode at block %lld, pos "
308 "%lld\n", block, blockBytesRead));
309
310 *_vnodeID = (block << 30) + (blockBytesRead & 0xffffffff);
311 TRACE(("fs_walk - New vnode id is %lld\n", *_vnodeID));
312
313 result = get_vnode(_volume, *_vnodeID, (void**)&newNode);
314 if (result == B_OK) {
315 newNode->parID = baseNode->id;
316 done = true;
317 }
318 } else {
319 free(node.name);
320 free(node.attr.slName);
321 }
322 } else {
323 result = initResult;
324 if (bytesRead == 0)
325 done = true;
326 }
327 blockData += bytesRead;
328 blockBytesRead += bytesRead;
329
330 TRACE(("fs_walk - Adding %u bytes to blockBytes read (total "
331 "%lld/%u).\n", (unsigned)bytesRead, blockBytesRead,
332 (unsigned)baseNode->dataLen[FS_DATA_FORMAT]));
333 }
334 totalRead += volume->logicalBlkSize[FS_DATA_FORMAT];
335 block++;
336
337 TRACE(("fs_walk - moving to next block %lld, total read %u\n",
338 block, (unsigned)totalRead));
339 block_cache_put(volume->fBlockCache, cachedBlock);
340 }
341
342 TRACE(("fs_walk - EXIT, result is %s, vnid is %Lu\n",
343 strerror(result), *_vnodeID));
344 return result;
345 }
346
347
348 static status_t
fs_read_vnode(fs_volume * _volume,ino_t vnodeID,fs_vnode * _node,int * _type,uint32 * _flags,bool reenter)349 fs_read_vnode(fs_volume* _volume, ino_t vnodeID, fs_vnode* _node,
350 int* _type, uint32* _flags, bool reenter)
351 {
352 iso9660_volume* volume = (iso9660_volume*)_volume->private_volume;
353
354 iso9660_inode* newNode = (iso9660_inode*)calloc(sizeof(iso9660_inode), 1);
355 if (newNode == NULL)
356 return B_NO_MEMORY;
357
358 uint32 pos = vnodeID & 0x3fffffff;
359 uint32 block = vnodeID >> 30;
360
361 TRACE(("fs_read_vnode - block = %u, pos = %u, raw = %Lu node %p\n",
362 (unsigned)block, (unsigned) pos, vnodeID, newNode));
363
364 if (pos > volume->logicalBlkSize[FS_DATA_FORMAT]) {
365 free(newNode);
366 return B_BAD_VALUE;
367 }
368
369 char* data = (char*)block_cache_get(volume->fBlockCache, block);
370 if (data == NULL) {
371 free(newNode);
372 return B_IO_ERROR;
373 }
374
375 status_t result = InitNode(volume, newNode, data + pos, NULL);
376 block_cache_put(volume->fBlockCache, block);
377
378 if (result < B_OK) {
379 free(newNode);
380 return result;
381 }
382
383 newNode->volume = volume;
384 newNode->id = vnodeID;
385
386 _node->private_node = newNode;
387 _node->ops = &gISO9660VnodeOps;
388 *_type = newNode->attr.stat[FS_DATA_FORMAT].st_mode
389 & ~(S_IWUSR | S_IWGRP | S_IWOTH);
390 *_flags = 0;
391
392 if ((newNode->flags & ISO_IS_DIR) == 0) {
393 newNode->cache = file_cache_create(volume->id, vnodeID,
394 newNode->dataLen[FS_DATA_FORMAT]);
395 }
396
397 return B_OK;
398 }
399
400
401 static status_t
fs_release_vnode(fs_volume *,fs_vnode * _node,bool)402 fs_release_vnode(fs_volume* /*_volume*/, fs_vnode* _node, bool /*reenter*/)
403 {
404 iso9660_inode* node = (iso9660_inode*)_node->private_node;
405
406 TRACE(("fs_release_vnode - ENTER (%p)\n", node));
407
408 if (node->id != ISO_ROOTNODE_ID) {
409 free(node->name);
410 free(node->attr.slName);
411
412 if (node->cache != NULL)
413 file_cache_delete(node->cache);
414
415 free(node);
416 }
417
418 TRACE(("fs_release_vnode - EXIT\n"));
419 return B_OK;
420 }
421
422
423 static status_t
fs_read_pages(fs_volume * _volume,fs_vnode * _node,void * _cookie,off_t pos,const iovec * vecs,size_t count,size_t * _numBytes)424 fs_read_pages(fs_volume* _volume, fs_vnode* _node, void* _cookie, off_t pos,
425 const iovec* vecs, size_t count, size_t* _numBytes)
426 {
427 iso9660_volume* volume = (iso9660_volume*)_volume->private_volume;
428 iso9660_inode* node = (iso9660_inode*)_node->private_node;
429
430 uint32 fileSize = node->dataLen[FS_DATA_FORMAT];
431 size_t bytesLeft = *_numBytes;
432
433 if (pos >= fileSize) {
434 *_numBytes = 0;
435 return B_OK;
436 }
437 if (pos + bytesLeft > fileSize) {
438 bytesLeft = fileSize - pos;
439 *_numBytes = bytesLeft;
440 }
441
442 file_io_vec fileVec;
443 fileVec.offset = pos + ((off_t)node->startLBN[FS_DATA_FORMAT]
444 * (off_t)volume->logicalBlkSize[FS_DATA_FORMAT]);
445 fileVec.length = bytesLeft;
446
447 uint32 vecIndex = 0;
448 size_t vecOffset = 0;
449 return read_file_io_vec_pages(volume->fd, &fileVec, 1, vecs, count,
450 &vecIndex, &vecOffset, &bytesLeft);
451 }
452
453
454 static status_t
fs_io(fs_volume * _volume,fs_vnode * _node,void * _cookie,io_request * request)455 fs_io(fs_volume* _volume, fs_vnode* _node, void* _cookie, io_request* request)
456 {
457 iso9660_volume* volume = (iso9660_volume*)_volume->private_volume;
458 iso9660_inode* node = (iso9660_inode*)_node->private_node;
459
460 #ifndef FS_SHELL
461 if (io_request_is_write(request)) {
462 notify_io_request(request, B_READ_ONLY_DEVICE);
463 return B_READ_ONLY_DEVICE;
464 }
465 #endif
466
467 if ((node->flags & ISO_IS_DIR) != 0) {
468 #ifndef FS_SHELL
469 notify_io_request(request, B_IS_A_DIRECTORY);
470 #endif
471 return B_IS_A_DIRECTORY;
472 }
473
474 return do_iterative_fd_io(volume->fd, request, iterative_io_get_vecs_hook,
475 iterative_io_finished_hook, node);
476 }
477
478
479 static status_t
fs_read_stat(fs_volume * _volume,fs_vnode * _node,struct stat * st)480 fs_read_stat(fs_volume* _volume, fs_vnode* _node, struct stat* st)
481 {
482 iso9660_volume* volume = (iso9660_volume*)_volume->private_volume;
483 iso9660_inode* node = (iso9660_inode*)_node->private_node;
484 status_t result = B_NO_ERROR;
485 time_t time;
486
487 TRACE(("fs_read_stat - ENTER\n"));
488
489 st->st_dev = volume->id;
490 st->st_ino = node->id;
491 st->st_nlink = node->attr.stat[FS_DATA_FORMAT].st_nlink;
492 st->st_uid = node->attr.stat[FS_DATA_FORMAT].st_uid;
493 st->st_gid = node->attr.stat[FS_DATA_FORMAT].st_gid;
494 st->st_blksize = 65536;
495 st->st_mode = node->attr.stat[FS_DATA_FORMAT].st_mode;
496
497 // Same for file/dir in ISO9660
498 st->st_size = node->dataLen[FS_DATA_FORMAT];
499 st->st_blocks = (st->st_size + 511) / 512;
500 if (ConvertRecDate(&(node->recordDate), &time) == B_NO_ERROR) {
501 st->st_ctim.tv_sec = st->st_mtim.tv_sec = st->st_atim.tv_sec = time;
502 st->st_ctim.tv_nsec = st->st_mtim.tv_nsec = st->st_atim.tv_nsec = 0;
503 }
504
505 TRACE(("fs_read_stat - EXIT, result is %s\n", strerror(result)));
506
507 return result;
508 }
509
510
511 static status_t
fs_open(fs_volume *,fs_vnode * _node,int openMode,void **)512 fs_open(fs_volume* /*_volume*/, fs_vnode* _node, int openMode, void** /*cookie*/)
513 {
514 // Do not allow any of the write-like open modes to get by
515 if ((openMode & O_RWMASK) == O_WRONLY || (openMode & O_RWMASK) == O_RDWR
516 || (openMode & O_TRUNC) != 0 || (openMode & O_CREAT) != 0)
517 return EROFS;
518
519 return B_OK;
520 }
521
522
523 static status_t
fs_read(fs_volume * _volume,fs_vnode * _node,void * cookie,off_t pos,void * buffer,size_t * _length)524 fs_read(fs_volume* _volume, fs_vnode* _node, void* cookie, off_t pos,
525 void* buffer, size_t* _length)
526 {
527 iso9660_inode* node = (iso9660_inode*)_node->private_node;
528
529 if ((node->flags & ISO_IS_DIR) != 0)
530 return EISDIR;
531
532 return file_cache_read(node->cache, NULL, pos, buffer, _length);
533 }
534
535
536 static status_t
fs_close(fs_volume *,fs_vnode *,void *)537 fs_close(fs_volume* /*_volume*/, fs_vnode* /*_node*/, void* /*cookie*/)
538 {
539 return B_OK;
540 }
541
542
543 static status_t
fs_free_cookie(fs_volume *,fs_vnode *,void *)544 fs_free_cookie(fs_volume* /*_volume*/, fs_vnode* /*_node*/, void* /*cookie*/)
545 {
546 return B_OK;
547 }
548
549
550 static status_t
fs_access(fs_volume *,fs_vnode *,int)551 fs_access(fs_volume* /*_volume*/, fs_vnode* /*_node*/, int /*mode*/)
552 {
553 return B_OK;
554 }
555
556
557 static status_t
fs_read_link(fs_volume * _volume,fs_vnode * _node,char * buffer,size_t * _bufferSize)558 fs_read_link(fs_volume* _volume, fs_vnode* _node, char* buffer,
559 size_t* _bufferSize)
560 {
561 iso9660_inode* node = (iso9660_inode*)_node->private_node;
562
563 if (!S_ISLNK(node->attr.stat[FS_DATA_FORMAT].st_mode))
564 return B_BAD_VALUE;
565
566 size_t length = strlen(node->attr.slName);
567
568 size_t bytesToCopy = std::min(length, *_bufferSize);
569
570 *_bufferSize = length;
571
572 memcpy(buffer, node->attr.slName, bytesToCopy);
573 return B_OK;
574 }
575
576
577 static status_t
fs_open_dir(fs_volume *,fs_vnode * _node,void ** _cookie)578 fs_open_dir(fs_volume* /*_volume*/, fs_vnode* _node, void** _cookie)
579 {
580 iso9660_inode* node = (iso9660_inode*)_node->private_node;
581
582 TRACE(("fs_open_dir - node is %p\n", node));
583
584 if ((node->flags & ISO_IS_DIR) == 0)
585 return B_NOT_A_DIRECTORY;
586
587 dircookie* dirCookie = (dircookie*)malloc(sizeof(dircookie));
588 if (dirCookie == NULL)
589 return B_NO_MEMORY;
590
591 dirCookie->startBlock = node->startLBN[FS_DATA_FORMAT];
592 dirCookie->block = node->startLBN[FS_DATA_FORMAT];
593 dirCookie->totalSize = node->dataLen[FS_DATA_FORMAT];
594 dirCookie->pos = 0;
595 dirCookie->id = node->id;
596 *_cookie = (void*)dirCookie;
597
598 return B_OK;
599 }
600
601
602 static status_t
fs_read_dir(fs_volume * _volume,fs_vnode * _node,void * _cookie,struct dirent * buffer,size_t bufferSize,uint32 * num)603 fs_read_dir(fs_volume* _volume, fs_vnode* _node, void* _cookie,
604 struct dirent* buffer, size_t bufferSize, uint32* num)
605 {
606 iso9660_volume* volume = (iso9660_volume*)_volume->private_volume;
607 dircookie* dirCookie = (dircookie*)_cookie;
608
609 TRACE(("fs_read_dir - ENTER\n"));
610
611 status_t result = ISOReadDirEnt(volume, dirCookie, buffer, bufferSize);
612
613 // If we succeeded, return 1, the number of dirents we read.
614 if (result == B_OK)
615 *num = 1;
616 else
617 *num = 0;
618
619 // When you get to the end, don't return an error, just return
620 // a zero in *num.
621
622 if (result == B_ENTRY_NOT_FOUND)
623 result = B_OK;
624
625 TRACE(("fs_read_dir - EXIT, result is %s\n", strerror(result)));
626 return result;
627 }
628
629
630 static status_t
fs_rewind_dir(fs_volume * _volume,fs_vnode * _node,void * _cookie)631 fs_rewind_dir(fs_volume* _volume, fs_vnode* _node, void* _cookie)
632 {
633 dircookie* cookie = (dircookie*)_cookie;
634
635 cookie->block = cookie->startBlock;
636 cookie->pos = 0;
637 return B_OK;
638 }
639
640
641 static status_t
fs_close_dir(fs_volume * _volume,fs_vnode * _node,void * cookie)642 fs_close_dir(fs_volume* _volume, fs_vnode* _node, void* cookie)
643 {
644 return B_OK;
645 }
646
647
648 static status_t
fs_free_dir_cookie(fs_volume * _volume,fs_vnode * _node,void * cookie)649 fs_free_dir_cookie(fs_volume* _volume, fs_vnode* _node, void* cookie)
650 {
651 free(cookie);
652 return B_OK;
653 }
654
655
656 // #pragma mark -
657
658
659 static status_t
iso_std_ops(int32 op,...)660 iso_std_ops(int32 op, ...)
661 {
662 switch (op) {
663 case B_MODULE_INIT:
664 case B_MODULE_UNINIT:
665 return B_OK;
666 default:
667 return B_ERROR;
668 }
669 }
670
671
672 fs_volume_ops gISO9660VolumeOps = {
673 &fs_unmount,
674 &fs_read_fs_stat,
675 NULL,
676 NULL,
677 &fs_read_vnode,
678
679 /* index and index directory ops */
680 NULL,
681 NULL,
682 NULL,
683 NULL,
684 NULL,
685 NULL,
686 NULL,
687 NULL,
688
689 /* query ops */
690 NULL,
691 NULL,
692 NULL,
693 NULL,
694 NULL,
695
696 /* FS layer ops */
697 NULL,
698 NULL,
699 };
700
701 fs_vnode_ops gISO9660VnodeOps = {
702 &fs_walk,
703 &fs_get_vnode_name,
704 &fs_release_vnode,
705 NULL,
706
707 /* vm-related ops */
708 NULL,
709 &fs_read_pages,
710 NULL,
711
712 &fs_io,
713 NULL, // cancel_io()
714
715 /* cache file access */
716 NULL,
717
718 /* common */
719 NULL,
720 NULL,
721 NULL,
722 NULL,
723 NULL,
724 &fs_read_link,
725 NULL,
726 NULL,
727 NULL,
728 NULL,
729 &fs_access,
730 &fs_read_stat,
731 NULL,
732 NULL,
733
734 /* file */
735 NULL,
736 &fs_open,
737 &fs_close,
738 &fs_free_cookie,
739 &fs_read,
740 NULL,
741
742 /* dir */
743 NULL,
744 NULL,
745 &fs_open_dir,
746 &fs_close_dir,
747 &fs_free_dir_cookie,
748 &fs_read_dir,
749 &fs_rewind_dir,
750
751 /* attribute directory ops */
752 NULL,
753 NULL,
754 NULL,
755 NULL,
756 NULL,
757
758 /* attribute ops */
759 NULL,
760 NULL,
761 NULL,
762 NULL,
763 NULL,
764 NULL,
765 NULL,
766 NULL,
767 NULL,
768 NULL,
769
770 /* node and FS layer support */
771 NULL,
772 NULL,
773 };
774
775 static file_system_module_info sISO660FileSystem = {
776 {
777 "file_systems/iso9660" B_CURRENT_FS_API_VERSION,
778 0,
779 iso_std_ops,
780 },
781
782 "iso9660", // short_name
783 "ISO9660 File System", // pretty_name
784 0, // DDM flags
785
786 // scanning
787 fs_identify_partition,
788 fs_scan_partition,
789 fs_free_identify_partition_cookie,
790 NULL, // free_partition_content_cookie()
791
792 &fs_mount,
793
794 /* capability querying */
795 NULL,
796 NULL,
797 NULL,
798 NULL,
799 NULL,
800 NULL,
801
802 /* shadow partition modifications */
803 NULL,
804
805 /* writing */
806 NULL,
807 NULL,
808 NULL,
809 NULL,
810 NULL,
811 NULL,
812 NULL,
813 };
814
815 module_info* modules[] = {
816 (module_info*)&sISO660FileSystem,
817 NULL,
818 };
819
820