2 * Copyright 1999, Be Incorporated. All Rights Reserved.
3 * This file may be used under the terms of the Be Sample Code License.
5 * Copyright 2001, pinc Software. All Rights Reserved.
7 * iso9960/multi-session, 1.0.0
20 # include <sys/stat.h>
24 # include <KernelExport.h>
25 # include <NodeMonitor.h>
26 # include <fs_interface.h>
27 # include <fs_cache.h>
31 # include <fs_index.h>
32 # include <fs_query.h>
33 # include <fs_volume.h>
35 # include <util/kernel_cpp.h>
39 #include "iso9660_identify.h"
41 // TODO: temporary solution as long as there is no public I/O requests API
42 #include <io_requests.h>
45 //#define TRACE_ISO9660
47 # define TRACE(x) dprintf x
53 struct identify_cookie
{
57 extern fs_volume_ops gISO9660VolumeOps
;
58 extern fs_vnode_ops gISO9660VnodeOps
;
61 //! fs_io() callback hook
63 iterative_io_get_vecs_hook(void* cookie
, io_request
* request
, off_t offset
,
64 size_t size
, struct file_io_vec
* vecs
, size_t* _count
)
66 iso9660_inode
* node
= (iso9660_inode
*)cookie
;
68 vecs
->offset
= offset
+ ((off_t
)node
->startLBN
[FS_DATA_FORMAT
]
69 * (off_t
)node
->volume
->logicalBlkSize
[FS_DATA_FORMAT
]);
78 //! fs_io() callback hook
80 iterative_io_finished_hook(void* cookie
, io_request
* request
, status_t status
,
81 bool partialTransfer
, size_t bytesTransferred
)
83 // nothing to do here...
88 // #pragma mark - Scanning
92 fs_identify_partition(int fd
, partition_data
* partition
, void** _cookie
)
94 iso9660_info
* info
= new iso9660_info
;
96 status_t status
= iso9660_fs_identify(fd
, info
);
108 fs_scan_partition(int fd
, partition_data
* partition
, void* _cookie
)
110 iso9660_info
* info
= (iso9660_info
*)_cookie
;
112 partition
->status
= B_PARTITION_VALID
;
113 partition
->flags
|= B_PARTITION_FILE_SYSTEM
| B_PARTITION_READ_ONLY
;
114 partition
->block_size
= ISO_PVD_SIZE
;
115 partition
->content_size
= ISO_PVD_SIZE
* info
->max_blocks
;
116 partition
->content_name
= strdup(info
->PreferredName());
118 if (partition
->content_name
== NULL
)
126 fs_free_identify_partition_cookie(partition_data
* partition
, void* _cookie
)
128 delete (iso9660_info
*)_cookie
;
132 // #pragma mark - FS hooks
136 fs_mount(fs_volume
* _volume
, const char* device
, uint32 flags
,
137 const char* args
, ino_t
* _rootID
)
139 bool allowJoliet
= true;
140 iso9660_volume
* volume
;
142 // Check for a 'nojoliet' parm
143 // all we check for is the existance of 'nojoliet' in the parms.
147 char* buf
= strdup(args
);
149 uint32 len
= strlen(buf
);
150 // lower case the parms data
151 for (i
= 0; i
< len
+ 1; i
++)
152 buf
[i
] = tolower(buf
[i
]);
155 spot
= strstr(buf
, "nojoliet");
162 // Try and mount volume as an ISO volume.
163 status_t result
= ISOMount(device
, O_RDONLY
, &volume
, allowJoliet
);
164 if (result
== B_OK
) {
165 *_rootID
= ISO_ROOTNODE_ID
;
167 _volume
->private_volume
= volume
;
168 _volume
->ops
= &gISO9660VolumeOps
;
169 volume
->volume
= _volume
;
170 volume
->id
= _volume
->id
;
172 result
= publish_vnode(_volume
, *_rootID
, &volume
->rootDirRec
,
174 volume
->rootDirRec
.attr
.stat
[FS_DATA_FORMAT
].st_mode
, 0);
175 if (result
!= B_OK
) {
176 block_cache_delete(volume
->fBlockCache
, false);
186 fs_unmount(fs_volume
* _volume
)
188 status_t result
= B_NO_ERROR
;
189 iso9660_volume
* volume
= (iso9660_volume
*)_volume
->private_volume
;
191 TRACE(("fs_unmount - ENTER\n"));
193 // Unlike in BeOS, we need to put the reference to our root node ourselves
194 put_vnode(_volume
, ISO_ROOTNODE_ID
);
196 block_cache_delete(volume
->fBlockCache
, false);
197 close(volume
->fdOfSession
);
198 result
= close(volume
->fd
);
202 TRACE(("fs_unmount - EXIT, result is %s\n", strerror(result
)));
208 fs_read_fs_stat(fs_volume
* _volume
, struct fs_info
* info
)
210 iso9660_volume
* volume
= (iso9660_volume
*)_volume
->private_volume
;
212 info
->flags
= B_FS_IS_PERSISTENT
| B_FS_IS_READONLY
;
213 info
->block_size
= volume
->logicalBlkSize
[FS_DATA_FORMAT
];
214 info
->io_size
= 65536;
215 info
->total_blocks
= volume
->volSpaceSize
[FS_DATA_FORMAT
];
216 info
->free_blocks
= 0;
218 strlcpy(info
->device_name
, volume
->devicePath
, sizeof(info
->device_name
));
219 strlcpy(info
->volume_name
, volume
->volIDString
, sizeof(info
->volume_name
));
221 // strip trailing spaces
223 for (i
= strlen(info
->volume_name
) - 1; i
>=0 ; i
--) {
224 if (info
->volume_name
[i
] != ' ')
229 strcpy(info
->volume_name
, "UNKNOWN");
231 info
->volume_name
[i
+ 1] = 0;
233 strcpy(info
->fsh_name
, "iso9660");
239 fs_get_vnode_name(fs_volume
* _volume
, fs_vnode
* _node
, char* buffer
,
242 iso9660_inode
* node
= (iso9660_inode
*)_node
->private_node
;
244 strlcpy(buffer
, node
->name
, bufferSize
);
250 fs_walk(fs_volume
* _volume
, fs_vnode
* _base
, const char* file
, ino_t
* _vnodeID
)
252 iso9660_volume
* volume
= (iso9660_volume
*)_volume
->private_volume
;
253 iso9660_inode
* baseNode
= (iso9660_inode
*)_base
->private_node
;
254 iso9660_inode
* newNode
= NULL
;
256 TRACE(("fs_walk - looking for %s in dir file of length %d\n", file
,
257 (int)baseNode
->dataLen
[FS_DATA_FORMAT
]));
259 if (strcmp(file
, ".") == 0) {
261 TRACE(("fs_walk - found \".\" file.\n"));
262 *_vnodeID
= baseNode
->id
;
263 return get_vnode(_volume
, *_vnodeID
, NULL
);
264 } else if (strcmp(file
, "..") == 0) {
266 TRACE(("fs_walk - found \"..\" file.\n"));
267 *_vnodeID
= baseNode
->parID
;
268 return get_vnode(_volume
, *_vnodeID
, NULL
);
271 // look up file in the directory
272 uint32 dataLength
= baseNode
->dataLen
[FS_DATA_FORMAT
];
273 status_t result
= ENOENT
;
274 size_t totalRead
= 0;
275 off_t block
= baseNode
->startLBN
[FS_DATA_FORMAT
];
278 while (totalRead
< dataLength
&& !done
) {
279 off_t cachedBlock
= block
;
280 char* blockData
= (char*)block_cache_get(volume
->fBlockCache
, block
);
281 if (blockData
== NULL
)
284 size_t bytesRead
= 0;
285 off_t blockBytesRead
= 0;
289 TRACE(("fs_walk - read buffer from disk at LBN %Ld into buffer "
290 "%p.\n", block
, blockData
));
292 // Move to the next block if necessary
293 // Don't go over end of buffer, if dir record sits on boundary.
295 while (blockBytesRead
< volume
->logicalBlkSize
[FS_DATA_FORMAT
]
296 && totalRead
+ blockBytesRead
< dataLength
299 initResult
= InitNode(volume
, &node
, blockData
, &bytesRead
);
300 TRACE(("fs_walk - InitNode returned %s, filename %s, %u bytes "
301 "read\n", strerror(initResult
), node
.name
, (unsigned)bytesRead
));
303 if (initResult
== B_OK
) {
304 if ((node
.flags
& ISO_IS_ASSOCIATED_FILE
) == 0
305 && !strcmp(node
.name
, file
)) {
306 TRACE(("fs_walk - success, found vnode at block %Ld, pos "
307 "%Ld\n", block
, blockBytesRead
));
309 *_vnodeID
= (block
<< 30) + (blockBytesRead
& 0xffffffff);
310 TRACE(("fs_walk - New vnode id is %Ld\n", *_vnodeID
));
312 result
= get_vnode(_volume
, *_vnodeID
, (void**)&newNode
);
313 if (result
== B_OK
) {
314 newNode
->parID
= baseNode
->id
;
319 free(node
.attr
.slName
);
326 blockData
+= bytesRead
;
327 blockBytesRead
+= bytesRead
;
329 TRACE(("fs_walk - Adding %u bytes to blockBytes read (total "
330 "%Ld/%u).\n", (unsigned)bytesRead
, blockBytesRead
,
331 (unsigned)baseNode
->dataLen
[FS_DATA_FORMAT
]));
333 totalRead
+= volume
->logicalBlkSize
[FS_DATA_FORMAT
];
336 TRACE(("fs_walk - moving to next block %Ld, total read %u\n",
337 block
, (unsigned)totalRead
));
338 block_cache_put(volume
->fBlockCache
, cachedBlock
);
341 TRACE(("fs_walk - EXIT, result is %s, vnid is %Lu\n",
342 strerror(result
), *_vnodeID
));
348 fs_read_vnode(fs_volume
* _volume
, ino_t vnodeID
, fs_vnode
* _node
,
349 int* _type
, uint32
* _flags
, bool reenter
)
351 iso9660_volume
* volume
= (iso9660_volume
*)_volume
->private_volume
;
353 iso9660_inode
* newNode
= (iso9660_inode
*)calloc(sizeof(iso9660_inode
), 1);
357 uint32 pos
= vnodeID
& 0x3fffffff;
358 uint32 block
= vnodeID
>> 30;
360 TRACE(("fs_read_vnode - block = %u, pos = %u, raw = %Lu node %p\n",
361 (unsigned)block
, (unsigned) pos
, vnodeID
, newNode
));
363 if (pos
> volume
->logicalBlkSize
[FS_DATA_FORMAT
]) {
368 char* data
= (char*)block_cache_get(volume
->fBlockCache
, block
);
374 status_t result
= InitNode(volume
, newNode
, data
+ pos
, NULL
);
375 block_cache_put(volume
->fBlockCache
, block
);
382 newNode
->volume
= volume
;
383 newNode
->id
= vnodeID
;
385 _node
->private_node
= newNode
;
386 _node
->ops
= &gISO9660VnodeOps
;
387 *_type
= newNode
->attr
.stat
[FS_DATA_FORMAT
].st_mode
388 & ~(S_IWUSR
| S_IWGRP
| S_IWOTH
);
391 if ((newNode
->flags
& ISO_IS_DIR
) == 0) {
392 newNode
->cache
= file_cache_create(volume
->id
, vnodeID
,
393 newNode
->dataLen
[FS_DATA_FORMAT
]);
401 fs_release_vnode(fs_volume
* /*_volume*/, fs_vnode
* _node
, bool /*reenter*/)
403 iso9660_inode
* node
= (iso9660_inode
*)_node
->private_node
;
405 TRACE(("fs_release_vnode - ENTER (%p)\n", node
));
407 if (node
->id
!= ISO_ROOTNODE_ID
) {
409 free(node
->attr
.slName
);
411 if (node
->cache
!= NULL
)
412 file_cache_delete(node
->cache
);
417 TRACE(("fs_release_vnode - EXIT\n"));
423 fs_read_pages(fs_volume
* _volume
, fs_vnode
* _node
, void* _cookie
, off_t pos
,
424 const iovec
* vecs
, size_t count
, size_t* _numBytes
)
426 iso9660_volume
* volume
= (iso9660_volume
*)_volume
->private_volume
;
427 iso9660_inode
* node
= (iso9660_inode
*)_node
->private_node
;
429 uint32 fileSize
= node
->dataLen
[FS_DATA_FORMAT
];
430 size_t bytesLeft
= *_numBytes
;
432 if (pos
>= fileSize
) {
436 if (pos
+ bytesLeft
> fileSize
) {
437 bytesLeft
= fileSize
- pos
;
438 *_numBytes
= bytesLeft
;
442 fileVec
.offset
= pos
+ ((off_t
)node
->startLBN
[FS_DATA_FORMAT
]
443 * (off_t
)volume
->logicalBlkSize
[FS_DATA_FORMAT
]);
444 fileVec
.length
= bytesLeft
;
447 size_t vecOffset
= 0;
448 return read_file_io_vec_pages(volume
->fd
, &fileVec
, 1, vecs
, count
,
449 &vecIndex
, &vecOffset
, &bytesLeft
);
454 fs_io(fs_volume
* _volume
, fs_vnode
* _node
, void* _cookie
, io_request
* request
)
456 iso9660_volume
* volume
= (iso9660_volume
*)_volume
->private_volume
;
457 iso9660_inode
* node
= (iso9660_inode
*)_node
->private_node
;
459 if (io_request_is_write(request
)) {
460 notify_io_request(request
, B_READ_ONLY_DEVICE
);
461 return B_READ_ONLY_DEVICE
;
464 if ((node
->flags
& ISO_IS_DIR
) != 0) {
465 notify_io_request(request
, B_IS_A_DIRECTORY
);
466 return B_IS_A_DIRECTORY
;
469 return do_iterative_fd_io(volume
->fd
, request
, iterative_io_get_vecs_hook
,
470 iterative_io_finished_hook
, node
);
475 fs_read_stat(fs_volume
* _volume
, fs_vnode
* _node
, struct stat
* st
)
477 iso9660_volume
* volume
= (iso9660_volume
*)_volume
->private_volume
;
478 iso9660_inode
* node
= (iso9660_inode
*)_node
->private_node
;
479 status_t result
= B_NO_ERROR
;
482 TRACE(("fs_read_stat - ENTER\n"));
484 st
->st_dev
= volume
->id
;
485 st
->st_ino
= node
->id
;
486 st
->st_nlink
= node
->attr
.stat
[FS_DATA_FORMAT
].st_nlink
;
487 st
->st_uid
= node
->attr
.stat
[FS_DATA_FORMAT
].st_uid
;
488 st
->st_gid
= node
->attr
.stat
[FS_DATA_FORMAT
].st_gid
;
489 st
->st_blksize
= 65536;
490 st
->st_mode
= node
->attr
.stat
[FS_DATA_FORMAT
].st_mode
;
492 // Same for file/dir in ISO9660
493 st
->st_size
= node
->dataLen
[FS_DATA_FORMAT
];
494 st
->st_blocks
= (st
->st_size
+ 511) / 512;
495 if (ConvertRecDate(&(node
->recordDate
), &time
) == B_NO_ERROR
) {
496 st
->st_ctim
.tv_sec
= st
->st_mtim
.tv_sec
= st
->st_atim
.tv_sec
= time
;
497 st
->st_ctim
.tv_nsec
= st
->st_mtim
.tv_nsec
= st
->st_atim
.tv_nsec
= 0;
500 TRACE(("fs_read_stat - EXIT, result is %s\n", strerror(result
)));
507 fs_open(fs_volume
* /*_volume*/, fs_vnode
* _node
, int openMode
, void** /*cookie*/)
509 // Do not allow any of the write-like open modes to get by
510 if ((openMode
& O_RWMASK
) == O_WRONLY
|| (openMode
& O_RWMASK
) == O_RDWR
511 || (openMode
& O_TRUNC
) != 0 || (openMode
& O_CREAT
) != 0)
519 fs_read(fs_volume
* _volume
, fs_vnode
* _node
, void* cookie
, off_t pos
,
520 void* buffer
, size_t* _length
)
522 iso9660_inode
* node
= (iso9660_inode
*)_node
->private_node
;
524 if ((node
->flags
& ISO_IS_DIR
) != 0)
527 uint32 fileSize
= node
->dataLen
[FS_DATA_FORMAT
];
529 // set/check boundaries for pos/length
532 if (pos
>= fileSize
) {
537 return file_cache_read(node
->cache
, NULL
, pos
, buffer
, _length
);
542 fs_close(fs_volume
* /*_volume*/, fs_vnode
* /*_node*/, void* /*cookie*/)
549 fs_free_cookie(fs_volume
* /*_volume*/, fs_vnode
* /*_node*/, void* /*cookie*/)
556 fs_access(fs_volume
* /*_volume*/, fs_vnode
* /*_node*/, int /*mode*/)
563 fs_read_link(fs_volume
* _volume
, fs_vnode
* _node
, char* buffer
,
566 iso9660_inode
* node
= (iso9660_inode
*)_node
->private_node
;
568 if (!S_ISLNK(node
->attr
.stat
[FS_DATA_FORMAT
].st_mode
))
571 size_t length
= strlen(node
->attr
.slName
);
572 if (length
> *_bufferSize
)
573 memcpy(buffer
, node
->attr
.slName
, *_bufferSize
);
575 memcpy(buffer
, node
->attr
.slName
, length
);
576 *_bufferSize
= length
;
584 fs_open_dir(fs_volume
* /*_volume*/, fs_vnode
* _node
, void** _cookie
)
586 iso9660_inode
* node
= (iso9660_inode
*)_node
->private_node
;
588 TRACE(("fs_open_dir - node is %p\n", node
));
590 if ((node
->flags
& ISO_IS_DIR
) == 0)
591 return B_NOT_A_DIRECTORY
;
593 dircookie
* dirCookie
= (dircookie
*)malloc(sizeof(dircookie
));
594 if (dirCookie
== NULL
)
597 dirCookie
->startBlock
= node
->startLBN
[FS_DATA_FORMAT
];
598 dirCookie
->block
= node
->startLBN
[FS_DATA_FORMAT
];
599 dirCookie
->totalSize
= node
->dataLen
[FS_DATA_FORMAT
];
601 dirCookie
->id
= node
->id
;
602 *_cookie
= (void*)dirCookie
;
609 fs_read_dir(fs_volume
* _volume
, fs_vnode
* _node
, void* _cookie
,
610 struct dirent
* buffer
, size_t bufferSize
, uint32
* num
)
612 iso9660_volume
* volume
= (iso9660_volume
*)_volume
->private_volume
;
613 dircookie
* dirCookie
= (dircookie
*)_cookie
;
615 TRACE(("fs_read_dir - ENTER\n"));
617 status_t result
= ISOReadDirEnt(volume
, dirCookie
, buffer
, bufferSize
);
619 // If we succeeded, return 1, the number of dirents we read.
625 // When you get to the end, don't return an error, just return
628 if (result
== B_ENTRY_NOT_FOUND
)
631 TRACE(("fs_read_dir - EXIT, result is %s\n", strerror(result
)));
637 fs_rewind_dir(fs_volume
* _volume
, fs_vnode
* _node
, void* _cookie
)
639 dircookie
* cookie
= (dircookie
*)_cookie
;
641 cookie
->block
= cookie
->startBlock
;
648 fs_close_dir(fs_volume
* _volume
, fs_vnode
* _node
, void* cookie
)
655 fs_free_dir_cookie(fs_volume
* _volume
, fs_vnode
* _node
, void* cookie
)
666 iso_std_ops(int32 op
, ...)
670 case B_MODULE_UNINIT
:
678 fs_volume_ops gISO9660VolumeOps
= {
685 /* index and index directory ops */
707 fs_vnode_ops gISO9660VnodeOps
= {
721 /* cache file access */
757 /* attribute directory ops */
776 /* node and FS layer support */
781 static file_system_module_info sISO660FileSystem
= {
783 "file_systems/iso9660" B_CURRENT_FS_API_VERSION
,
788 "iso9660", // short_name
789 "ISO9660 File System", // pretty_name
793 fs_identify_partition
,
795 fs_free_identify_partition_cookie
,
796 NULL
, // free_partition_content_cookie()
800 /* capability querying */
808 /* shadow partition modifications */
821 module_info
* modules
[] = {
822 (module_info
*)&sISO660FileSystem
,