📄 cdfs.c
字号:
//
// cdfs.c
//
// ISO-9660 CD-ROM Filesystem
//
// Copyright (C) 2002 Michael Ringgaard. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// 3. Neither the name of the project nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.
//
#include <os/krnl.h>
#include "iso9660.h"
#define CDFS_DEFAULT_CACHESIZE 128
#define CDFS_BLOCKSIZE 2048
struct cdfs
{
dev_t devno;
int blks;
int volblks;
int vdblk;
int joliet;
struct bufpool *cache;
unsigned char *path_table_buffer;
struct iso_pathtable_record **path_table;
int path_table_records;
};
struct cdfs_file
{
int extent;
int size;
time_t date;
};
static int cdfs_fnmatch(struct cdfs *cdfs, char *fn1, int len1, char *fn2, int len2)
{
if (cdfs->joliet)
{
wchar_t *wfn2 = (wchar_t *) fn2;
int wlen2 = len2 / 2;
if (wlen2 > 1 && ntohs(wfn2[wlen2 - 2]) == ';') wlen2 -= 2;
if (wlen2 > 0 && ntohs(wfn2[wlen2 - 1]) == '.') wlen2 -= 1;
if (len1 != wlen2) return 0;
while (len1--) if (*fn1++ != ntohs(*wfn2++)) return 0;
return 1;
}
else
{
if (len2 > 1 && fn2[len2 - 2] == ';') len2 -= 2;
if (len2 > 0 && fn2[len2 - 1] == '.') len2 -= 1;
if (len1 != len2) return 0;
while (len1--) if (*fn1++ != *fn2++) return 0;
return 1;
}
}
static int cdfs_read_path_table(struct cdfs *cdfs, struct iso_volume_descriptor *vd)
{
struct buf *buf;
unsigned char *pt;
int ptblk;
int ptlen;
int ptpos;
int n;
// Determine size and location of path table and allocate buffer
ptlen = isonum_733(vd->path_table_size);
ptblk = isonum_731(vd->type_l_path_table);
cdfs->path_table_buffer = malloc(ptlen);
if (!cdfs->path_table_buffer) return -ENOMEM;
// Read L path table into buffer
ptpos = 0;
while (ptpos < ptlen)
{
buf = get_buffer(cdfs->cache, ptblk++);
if (!buf) return -EIO;
if (ptlen - ptpos > CDFS_BLOCKSIZE)
{
memcpy(cdfs->path_table_buffer + ptpos, buf->data, CDFS_BLOCKSIZE);
ptpos += CDFS_BLOCKSIZE;
}
else
{
memcpy(cdfs->path_table_buffer + ptpos, buf->data, ptlen - ptpos);
ptpos = ptlen;
}
release_buffer(cdfs->cache, buf);
}
// Determine number of records in pathtable
// Path table records are indexed from 1 (first entry not used)
pt = cdfs->path_table_buffer;
n = 1;
while (pt < cdfs->path_table_buffer + ptlen)
{
struct iso_pathtable_record *pathrec = (struct iso_pathtable_record *) pt;
int namelen = pathrec->length;
int reclen = sizeof(struct iso_pathtable_record) + namelen + (namelen & 1);
n++;
pt += reclen;
}
cdfs->path_table_records = n;
// Allocate path table
cdfs->path_table = (struct iso_pathtable_record **) kmalloc(cdfs->path_table_records * sizeof(struct iso_pathtable_record **));
if (!cdfs->path_table) return -ENOMEM;
cdfs->path_table[0] = NULL;
// Setup pointers into path table buffer
pt = cdfs->path_table_buffer;
for (n = 1; n < cdfs->path_table_records; n++)
{
struct iso_pathtable_record *pathrec = (struct iso_pathtable_record *) pt;
int namelen = pathrec->length;
int reclen = sizeof(struct iso_pathtable_record) + namelen + (namelen & 1);
cdfs->path_table[n] = pathrec;
pt += reclen;
}
return 0;
}
static int cdfs_find_dir(struct cdfs *cdfs, char *name, int len)
{
char *p;
int l;
int dir = 2;
int parent = 1;
while (1)
{
// Skip path separator
if (*name == PS1 || *name == PS2)
{
name++;
len--;
}
if (len == 0) return parent;
// Find next part of name
p = name;
l = 0;
while (l < len && *p != PS1 && *p != PS2)
{
l++;
p++;
}
// Find directory for next name part
while (dir < cdfs->path_table_records)
{
if (cdfs->path_table[dir]->parent != parent) return -ENOENT;
if (cdfs_fnmatch(cdfs, name, l, cdfs->path_table[dir]->name, cdfs->path_table[dir]->length)) break;
dir++;
}
// If we reached the end of the path table the directory does not exist
if (dir == cdfs->path_table_records) return -ENOENT;
// If we have parsed the whole name return the directory number
if (l == len) return dir;
// Go forward in path table until first entry for directory found
parent = dir;
while (dir < cdfs->path_table_records)
{
if (cdfs->path_table[dir]->parent == parent) break;
dir++;
}
// Prepare for next name part
name = p;
len -= l;
}
}
static int cdfs_find_in_dir(struct cdfs *cdfs, int dir, char *name, int len, struct buf **dirbuf, struct iso_directory_record **dirrec)
{
struct buf *buf;
char *p;
struct iso_directory_record *rec;
int blk;
int left;
int reclen;
int namelen;
// The first two directory records are . (current) and .. (parent)
blk = cdfs->path_table[dir]->extent;
buf = get_buffer(cdfs->cache, blk++);
if (!buf) return -EIO;
// Get length of directory from the first record
p = buf->data;
rec = (struct iso_directory_record *) p;
left = isonum_733(rec->size);
// Find named entry in directory
while (left > 0)
{
// Read next block if all records in current block has been read
// Directory records never cross block boundaries
if (p >= buf->data + CDFS_BLOCKSIZE)
{
release_buffer(cdfs->cache, buf);
if (p > buf->data + CDFS_BLOCKSIZE) return -EIO;
buf = get_buffer(cdfs->cache, blk++);
if (!buf) return -EIO;
p = buf->data;
}
// Check for match
rec = (struct iso_directory_record *) p;
reclen = isonum_711(rec->length);
namelen = isonum_711(rec->name_len);
if (reclen > 0)
{
if (cdfs_fnmatch(cdfs, name, len, (char *) rec->name, namelen))
{
*dirrec = rec;
*dirbuf = buf;
return 0;
}
// Skip to next record
p += reclen;
left -= reclen;
}
else
{
// Skip to next block
left -= (buf->data + CDFS_BLOCKSIZE) - p;
p = buf->data + CDFS_BLOCKSIZE;
}
}
release_buffer(cdfs->cache, buf);
return -ENOENT;
}
static int cdfs_find_file(struct cdfs *cdfs, char *name, int len, struct buf **buf, struct iso_directory_record **rec)
{
int dir;
int split;
int n;
// If root get directory record from volume descriptor
if (len == 0)
{
struct iso_volume_descriptor *vd;
*buf = get_buffer(cdfs->cache, cdfs->vdblk);
if (!*buf) return -EIO;
vd = (struct iso_volume_descriptor *) (*buf)->data;
*rec = (struct iso_directory_record *) vd->root_directory_record;
return 0;
}
// Split path into directory part and file name part
split = -1;
for (n = 0; n < len; n++) if (name[n] == PS1 || name[n] == PS2) split = n;
// Find directly if file located in root directory
if (split == -1) return cdfs_find_in_dir(cdfs, 1, name, len, buf, rec);
// Locate directory
dir = cdfs_find_dir(cdfs, name, split + 1);
if (dir < 0) return dir;
// Find filename in directory
return cdfs_find_in_dir(cdfs, dir, name + split + 1, len - split - 1, buf, rec);
}
static time_t cdfs_isodate(unsigned char *date)
{
struct tm tm;
memset(&tm, 0, sizeof tm);
tm.tm_year = date[0];
tm.tm_mon = date[1] - 1;
tm.tm_mday = date[2];
tm.tm_hour = date[3];
tm.tm_min = date[4];
tm.tm_sec = date[5];
tm.tm_min += (*(char *) &date[6]) * 15;
return mktime(&tm);
}
int cdfs_mount(struct fs *fs, char *opts)
{
struct cdfs *cdfs;
dev_t devno;
int cachebufs;
int rc;
int blk;
struct buf *buf;
struct iso_volume_descriptor *vd;
// Check device
devno = dev_open(fs->mntfrom);
if (devno == NODEV) return -NODEV;
if (device(devno)->driver->type != DEV_TYPE_BLOCK) return -ENOTBLK;
// Revalidate device and check block size
if (get_option(opts, "revalidate", NULL, 0, NULL))
{
rc = dev_ioctl(devno, IOCTL_REVALIDATE, NULL, 0);
if (rc < 0) return rc;
}
if (dev_ioctl(devno, IOCTL_GETBLKSIZE, NULL, 0) != CDFS_BLOCKSIZE) return -ENXIO;
// Allocate file system
cdfs = (struct cdfs *) kmalloc(sizeof(struct cdfs));
memset(cdfs, 0, sizeof(struct cdfs));
cdfs->devno = devno;
cdfs->blks = dev_ioctl(devno, IOCTL_GETDEVSIZE, NULL, 0);
if (cdfs->blks < 0) return cdfs->blks;
// Allocate cache
cachebufs = get_num_option(opts, "cache", CDFS_DEFAULT_CACHESIZE);
cdfs->cache = init_buffer_pool(devno, cachebufs, CDFS_BLOCKSIZE, NULL, cdfs);
if (!cdfs->cache) return -ENOMEM;
// Read volume descriptors
cdfs->vdblk = 0;
blk = 16;
while (1)
{
int type;
unsigned char *esc;
buf = get_buffer(cdfs->cache, blk);
if (!buf) return -EIO;
vd = (struct iso_volume_descriptor *) buf->data;
type = isonum_711(vd->type);
esc = vd->escape_sequences;
if (memcmp(vd->id, "CD001", 5) != 0)
{
free_buffer_pool(cdfs->cache);
dev_close(cdfs->devno);
kfree(cdfs);
return -EIO;
}
if (cdfs->vdblk == 0 && type == ISO_VD_PRIMARY)
cdfs->vdblk = blk;
else if (type == ISO_VD_SUPPLEMENTAL &&
esc[0] == 0x25 && esc[1] == 0x2F &&
(esc[2] == 0x40 || esc[2] == 0x43 || esc[2] == 0x45))
{
cdfs->vdblk = blk;
cdfs->joliet = 1;
}
release_buffer(cdfs->cache, buf);
if (type == ISO_VD_END) break;
blk++;
}
if (cdfs->vdblk == 0) return -EIO;
// Initialize filesystem from selected volume descriptor and read path table
buf = get_buffer(cdfs->cache, cdfs->vdblk);
if (!buf) return -EIO;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -