Fix more class/struct mixups.
[haiku.git] / src / add-ons / kernel / file_systems / iso9660 / kernel_interface.cpp
blobfc3405cb435f91472e57106b6edd5ccaa2f3fb41
1 /*
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
8 */
11 #include <ctype.h>
13 #ifndef FS_SHELL
14 # include <dirent.h>
15 # include <errno.h>
16 # include <fcntl.h>
17 # include <stdio.h>
18 # include <stdlib.h>
19 # include <string.h>
20 # include <sys/stat.h>
21 # include <time.h>
22 # include <unistd.h>
24 # include <KernelExport.h>
25 # include <NodeMonitor.h>
26 # include <fs_interface.h>
27 # include <fs_cache.h>
29 # include <fs_attr.h>
30 # include <fs_info.h>
31 # include <fs_index.h>
32 # include <fs_query.h>
33 # include <fs_volume.h>
35 # include <util/kernel_cpp.h>
36 #endif
38 #include "iso9660.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
46 #ifdef TRACE_ISO9660
47 # define TRACE(x) dprintf x
48 #else
49 # define TRACE(x) ;
50 #endif
53 struct identify_cookie {
54 iso9660_info info;
57 extern fs_volume_ops gISO9660VolumeOps;
58 extern fs_vnode_ops gISO9660VnodeOps;
61 //! fs_io() callback hook
62 static status_t
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]);
70 vecs->length = size;
72 *_count = 1;
74 return B_OK;
78 //! fs_io() callback hook
79 static status_t
80 iterative_io_finished_hook(void* cookie, io_request* request, status_t status,
81 bool partialTransfer, size_t bytesTransferred)
83 // nothing to do here...
84 return B_OK;
88 // #pragma mark - Scanning
91 static float
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);
97 if (status != B_OK) {
98 delete info;
99 return -1;
102 *_cookie = info;
103 return 0.6f;
107 static status_t
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)
119 return B_NO_MEMORY;
121 return B_OK;
125 static void
126 fs_free_identify_partition_cookie(partition_data* partition, void* _cookie)
128 delete (iso9660_info*)_cookie;
132 // #pragma mark - FS hooks
135 static status_t
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.
144 if (args != NULL) {
145 uint32 i;
146 char* spot;
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]);
154 // look for nojoliet
155 spot = strstr(buf, "nojoliet");
156 if (spot != NULL)
157 allowJoliet = false;
159 free(buf);
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,
173 &gISO9660VnodeOps,
174 volume->rootDirRec.attr.stat[FS_DATA_FORMAT].st_mode, 0);
175 if (result != B_OK) {
176 block_cache_delete(volume->fBlockCache, false);
177 free(volume);
178 result = B_ERROR;
181 return result;
185 static status_t
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);
200 free(volume);
202 TRACE(("fs_unmount - EXIT, result is %s\n", strerror(result)));
203 return result;
207 static status_t
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
222 int i;
223 for (i = strlen(info->volume_name) - 1; i >=0 ; i--) {
224 if (info->volume_name[i] != ' ')
225 break;
228 if (i < 0)
229 strcpy(info->volume_name, "UNKNOWN");
230 else
231 info->volume_name[i + 1] = 0;
233 strcpy(info->fsh_name, "iso9660");
234 return B_OK;
238 static status_t
239 fs_get_vnode_name(fs_volume* _volume, fs_vnode* _node, char* buffer,
240 size_t bufferSize)
242 iso9660_inode* node = (iso9660_inode*)_node->private_node;
244 strlcpy(buffer, node->name, bufferSize);
245 return B_OK;
249 static status_t
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) {
260 // base directory
261 TRACE(("fs_walk - found \".\" file.\n"));
262 *_vnodeID = baseNode->id;
263 return get_vnode(_volume, *_vnodeID, NULL);
264 } else if (strcmp(file, "..") == 0) {
265 // parent directory
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];
276 bool done = false;
278 while (totalRead < dataLength && !done) {
279 off_t cachedBlock = block;
280 char* blockData = (char*)block_cache_get(volume->fBlockCache, block);
281 if (blockData == NULL)
282 break;
284 size_t bytesRead = 0;
285 off_t blockBytesRead = 0;
286 iso9660_inode node;
287 int initResult;
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
297 && blockData[0] != 0
298 && !done) {
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;
315 done = true;
317 } else {
318 free(node.name);
319 free(node.attr.slName);
321 } else {
322 result = initResult;
323 if (bytesRead == 0)
324 done = true;
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];
334 block++;
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));
343 return result;
347 static status_t
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);
354 if (newNode == NULL)
355 return B_NO_MEMORY;
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]) {
364 free(newNode);
365 return B_BAD_VALUE;
368 char* data = (char*)block_cache_get(volume->fBlockCache, block);
369 if (data == NULL) {
370 free(newNode);
371 return B_IO_ERROR;
374 status_t result = InitNode(volume, newNode, data + pos, NULL);
375 block_cache_put(volume->fBlockCache, block);
377 if (result < B_OK) {
378 free(newNode);
379 return result;
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);
389 *_flags = 0;
391 if ((newNode->flags & ISO_IS_DIR) == 0) {
392 newNode->cache = file_cache_create(volume->id, vnodeID,
393 newNode->dataLen[FS_DATA_FORMAT]);
396 return B_OK;
400 static status_t
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) {
408 free(node->name);
409 free(node->attr.slName);
411 if (node->cache != NULL)
412 file_cache_delete(node->cache);
414 free(node);
417 TRACE(("fs_release_vnode - EXIT\n"));
418 return B_OK;
422 static status_t
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) {
433 *_numBytes = 0;
434 return B_OK;
436 if (pos + bytesLeft > fileSize) {
437 bytesLeft = fileSize - pos;
438 *_numBytes = bytesLeft;
441 file_io_vec fileVec;
442 fileVec.offset = pos + ((off_t)node->startLBN[FS_DATA_FORMAT]
443 * (off_t)volume->logicalBlkSize[FS_DATA_FORMAT]);
444 fileVec.length = bytesLeft;
446 uint32 vecIndex = 0;
447 size_t vecOffset = 0;
448 return read_file_io_vec_pages(volume->fd, &fileVec, 1, vecs, count,
449 &vecIndex, &vecOffset, &bytesLeft);
453 static status_t
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);
474 static status_t
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;
480 time_t time;
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)));
502 return result;
506 static status_t
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)
512 return EROFS;
514 return B_OK;
518 static status_t
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)
525 return EISDIR;
527 uint32 fileSize = node->dataLen[FS_DATA_FORMAT];
529 // set/check boundaries for pos/length
530 if (pos < 0)
531 return B_BAD_VALUE;
532 if (pos >= fileSize) {
533 *_length = 0;
534 return B_OK;
537 return file_cache_read(node->cache, NULL, pos, buffer, _length);
541 static status_t
542 fs_close(fs_volume* /*_volume*/, fs_vnode* /*_node*/, void* /*cookie*/)
544 return B_OK;
548 static status_t
549 fs_free_cookie(fs_volume* /*_volume*/, fs_vnode* /*_node*/, void* /*cookie*/)
551 return B_OK;
555 static status_t
556 fs_access(fs_volume* /*_volume*/, fs_vnode* /*_node*/, int /*mode*/)
558 return B_OK;
562 static status_t
563 fs_read_link(fs_volume* _volume, fs_vnode* _node, char* buffer,
564 size_t* _bufferSize)
566 iso9660_inode* node = (iso9660_inode*)_node->private_node;
568 if (!S_ISLNK(node->attr.stat[FS_DATA_FORMAT].st_mode))
569 return B_BAD_VALUE;
571 size_t length = strlen(node->attr.slName);
572 if (length > *_bufferSize)
573 memcpy(buffer, node->attr.slName, *_bufferSize);
574 else {
575 memcpy(buffer, node->attr.slName, length);
576 *_bufferSize = length;
579 return B_OK;
583 static status_t
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)
595 return B_NO_MEMORY;
597 dirCookie->startBlock = node->startLBN[FS_DATA_FORMAT];
598 dirCookie->block = node->startLBN[FS_DATA_FORMAT];
599 dirCookie->totalSize = node->dataLen[FS_DATA_FORMAT];
600 dirCookie->pos = 0;
601 dirCookie->id = node->id;
602 *_cookie = (void*)dirCookie;
604 return B_OK;
608 static status_t
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.
620 if (result == B_OK)
621 *num = 1;
622 else
623 *num = 0;
625 // When you get to the end, don't return an error, just return
626 // a zero in *num.
628 if (result == B_ENTRY_NOT_FOUND)
629 result = B_OK;
631 TRACE(("fs_read_dir - EXIT, result is %s\n", strerror(result)));
632 return result;
636 static status_t
637 fs_rewind_dir(fs_volume* _volume, fs_vnode* _node, void* _cookie)
639 dircookie* cookie = (dircookie*)_cookie;
641 cookie->block = cookie->startBlock;
642 cookie->pos = 0;
643 return B_OK;
647 static status_t
648 fs_close_dir(fs_volume* _volume, fs_vnode* _node, void* cookie)
650 return B_OK;
654 static status_t
655 fs_free_dir_cookie(fs_volume* _volume, fs_vnode* _node, void* cookie)
657 free(cookie);
658 return B_OK;
662 // #pragma mark -
665 static status_t
666 iso_std_ops(int32 op, ...)
668 switch (op) {
669 case B_MODULE_INIT:
670 case B_MODULE_UNINIT:
671 return B_OK;
672 default:
673 return B_ERROR;
678 fs_volume_ops gISO9660VolumeOps = {
679 &fs_unmount,
680 &fs_read_fs_stat,
681 NULL,
682 NULL,
683 &fs_read_vnode,
685 /* index and index directory ops */
686 NULL,
687 NULL,
688 NULL,
689 NULL,
690 NULL,
691 NULL,
692 NULL,
693 NULL,
695 /* query ops */
696 NULL,
697 NULL,
698 NULL,
699 NULL,
700 NULL,
702 /* FS layer ops */
703 NULL,
704 NULL,
707 fs_vnode_ops gISO9660VnodeOps = {
708 &fs_walk,
709 &fs_get_vnode_name,
710 &fs_release_vnode,
711 NULL,
713 /* vm-related ops */
714 NULL,
715 &fs_read_pages,
716 NULL,
718 &fs_io,
719 NULL, // cancel_io()
721 /* cache file access */
722 NULL,
724 /* common */
725 NULL,
726 NULL,
727 NULL,
728 NULL,
729 NULL,
730 &fs_read_link,
731 NULL,
732 NULL,
733 NULL,
734 NULL,
735 &fs_access,
736 &fs_read_stat,
737 NULL,
738 NULL,
740 /* file */
741 NULL,
742 &fs_open,
743 &fs_close,
744 &fs_free_cookie,
745 &fs_read,
746 NULL,
748 /* dir */
749 NULL,
750 NULL,
751 &fs_open_dir,
752 &fs_close_dir,
753 &fs_free_dir_cookie,
754 &fs_read_dir,
755 &fs_rewind_dir,
757 /* attribute directory ops */
758 NULL,
759 NULL,
760 NULL,
761 NULL,
762 NULL,
764 /* attribute ops */
765 NULL,
766 NULL,
767 NULL,
768 NULL,
769 NULL,
770 NULL,
771 NULL,
772 NULL,
773 NULL,
774 NULL,
776 /* node and FS layer support */
777 NULL,
778 NULL,
781 static file_system_module_info sISO660FileSystem = {
783 "file_systems/iso9660" B_CURRENT_FS_API_VERSION,
785 iso_std_ops,
788 "iso9660", // short_name
789 "ISO9660 File System", // pretty_name
790 0, // DDM flags
792 // scanning
793 fs_identify_partition,
794 fs_scan_partition,
795 fs_free_identify_partition_cookie,
796 NULL, // free_partition_content_cookie()
798 &fs_mount,
800 /* capability querying */
801 NULL,
802 NULL,
803 NULL,
804 NULL,
805 NULL,
806 NULL,
808 /* shadow partition modifications */
809 NULL,
811 /* writing */
812 NULL,
813 NULL,
814 NULL,
815 NULL,
816 NULL,
817 NULL,
818 NULL,
821 module_info* modules[] = {
822 (module_info*)&sISO660FileSystem,
823 NULL,