📄 hpfs_fs.c
字号:
goto bail1;
/*
* Fill in the info from the directory if this is a newly created
* inode.
*/
if (!inode->i_atime) {
inode->i_atime = local_to_gmt(de->read_date);
inode->i_mtime = local_to_gmt(de->write_date);
inode->i_ctime = local_to_gmt(de->creation_date);
if (de->read_only)
inode->i_mode &= ~0222;
if (!de->directory) {
inode->i_size = de->file_size;
/*
* i_blocks should count the fnode and any anodes.
* We count 1 for the fnode and don't bother about
* anodes -- the disk heads are on the directory band
* and we want them to stay there.
*/
inode->i_blocks = 1 + ((inode->i_size + 511) >> 9);
}
}
brelse4(&qbh);
/*
* Made it.
*/
*result = inode;
iput(dir);
return 0;
/*
* Didn't.
*/
bail1:
brelse4(&qbh);
bail:
iput(dir);
return -ENOENT;
}
/*
* Compare two counted strings ignoring case.
* HPFS directory order sorts letters as if they're upper case.
*/
static inline int memcasecmp(const unsigned char *s1, const unsigned char *s2,
unsigned n)
{
int t;
if (n != 0)
do {
unsigned c1 = *s1++;
unsigned c2 = *s2++;
if (c1 - 'a' < 26)
c1 -= 040;
if (c2 - 'a' < 26)
c2 -= 040;
if ((t = c1 - c2) != 0)
return t;
} while (--n != 0);
return 0;
}
/*
* Search a directory for the given name, return a pointer to its dir entry
* and a pointer to the buffer containing it.
*/
static struct hpfs_dirent *map_dirent(struct inode *inode, dnode_secno dno,
const unsigned char *name, unsigned len,
struct quad_buffer_head *qbh)
{
struct dnode *dnode;
struct hpfs_dirent *de;
struct hpfs_dirent *de_end;
int t, l;
/*
* read the dnode at the root of our subtree
*/
dnode = map_dnode(inode->i_dev, dno, qbh);
if (!dnode)
return 0;
/*
* get pointers to start and end+1 of dir entries
*/
de = dnode_first_de(dnode);
de_end = dnode_end_de(dnode);
/*
* look through the entries for the name we're after
*/
for ( ; de < de_end; de = de_next_de(de)) {
/*
* compare names
*/
l = len < de->namelen ? len : de->namelen;
t = memcasecmp(name, de->name, l);
/*
* initial substring matches, compare lengths
*/
if (t == 0) {
t = len - de->namelen;
/* bingo */
if (t == 0)
return de;
}
/*
* wanted name .lt. dir name => not present.
*/
if (t < 0) {
/*
* if there is a subtree, search it.
*/
if (de->down) {
dnode_secno sub_dno = de_down_pointer(de);
brelse4(qbh);
return map_dirent(inode, sub_dno,
name, len, qbh);
}
else
break;
}
/*
* de->last is set on the last name in the dnode (it's always
* a "\377" pseudo entry). de->length == 0 means we're about
* to infinite loop. This test does nothing in a well-formed
* dnode.
*/
if (de->last || de->length == 0)
break;
}
/*
* name not found.
*/
return 0;
}
/*
* readdir. Return exactly 1 dirent. (I tried and tried, but currently
* the interface with libc just does not permit more than 1. If it gets
* fixed, throw this out and just walk the tree and write records into
* the user buffer.)
*
* We keep track of our position in the dnode tree with a sort of
* dewey-decimal record of subtree locations. Like so:
*
* (1 (1.1 1.2 1.3) 2 3 (3.1 (3.1.1 3.1.2) 3.2 3.3 (3.3.1)) 4)
*
* Subtrees appear after their file, out of lexical order,
* which would be before their file. It's easier.
*
* A directory can't hold more than 56 files, so 6 bits are used for
* position numbers. If the tree is so deep that the position encoding
* doesn't fit, I'm sure something absolutely fascinating happens.
*
* The actual sequence of f_pos values is
* 0 => . -1 => .. 1 1.1 ... 8.9 9 => files -2 => eof
*
* The directory inode caches one position-to-dnode correspondence so
* we won't have to repeatedly scan the top levels of the tree.
*/
static int hpfs_readdir(struct inode *inode, struct file *filp,
struct dirent *dirent, int likely_story)
{
struct quad_buffer_head qbh;
struct hpfs_dirent *de;
int namelen, lc;
ino_t ino;
if (inode == 0
|| inode->i_sb == 0
|| !S_ISDIR(inode->i_mode))
return -EBADF;
lc = inode->i_sb->s_hpfs_lowercase;
switch (filp->f_pos) {
case 0:
write_one_dirent(dirent, ".", 1, inode->i_ino, lc);
filp->f_pos = -1;
return 1;
case -1:
write_one_dirent(dirent, "..", 2,
inode->i_hpfs_parent_dir, lc);
filp->f_pos = 1;
return 2;
case -2:
return 0;
default:
de = map_pos_dirent(inode, &filp->f_pos, &qbh);
if (!de) {
filp->f_pos = -2;
return 0;
}
namelen = de->namelen;
if (de->directory)
ino = dir_ino(de->fnode);
else
ino = file_ino(de->fnode);
write_one_dirent(dirent, de->name, namelen, ino, lc);
brelse4(&qbh);
return namelen;
}
}
/*
* Send the given name and ino off to the user dirent struct at *dirent.
* Blam it to lowercase if the mount option said to.
*
* Note that Linux d_reclen is the length of the file name, and has nothing
* to do with the length of the dirent record.
*/
static void write_one_dirent(struct dirent *dirent, const unsigned char *name,
unsigned namelen, ino_t ino, int lowercase)
{
unsigned n;
put_fs_long(ino, &dirent->d_ino);
put_fs_word(namelen, &dirent->d_reclen);
if (lowercase)
for (n = namelen; n != 0;) {
unsigned t = name[--n];
if (t - 'A' < 26)
t += 040;
put_fs_byte(t, &dirent->d_name[n]);
}
else
memcpy_tofs(dirent->d_name, name, namelen);
put_fs_byte(0, &dirent->d_name[namelen]);
}
/*
* Map the dir entry at subtree coordinates given by *posp, and
* increment *posp to point to the following dir entry.
*/
static struct hpfs_dirent *map_pos_dirent(struct inode *inode, off_t *posp,
struct quad_buffer_head *qbh)
{
unsigned pos, q, r;
dnode_secno dno;
struct hpfs_dirent *de;
/*
* Get the position code and split off the rightmost index r
*/
pos = *posp;
q = pos >> 6;
r = pos & 077;
/*
* Get the sector address of the dnode
* pointed to by the leading part q
*/
dno = dir_subdno(inode, q);
if (!dno)
return 0;
/*
* Get the entry at index r in dnode q
*/
de = map_nth_dirent(inode->i_dev, dno, r, qbh);
/*
* If none, we're out of files in this dnode. Ascend.
*/
if (!de) {
if (q == 0)
return 0;
*posp = q + 1;
return map_pos_dirent(inode, posp, qbh);
}
/*
* If a subtree is here, descend.
*/
if (de->down)
*posp = pos << 6 | 1;
else
*posp = pos + 1;
/*
* Don't return the ^A^A and \377 entries.
*/
if (de->first || de->last) {
brelse4(qbh);
return map_pos_dirent(inode, posp, qbh);
}
else
return de;
}
/*
* Return the address of the dnode with subtree coordinates given by pos.
*/
static dnode_secno dir_subdno(struct inode *inode, unsigned pos)
{
struct hpfs_dirent *de;
struct quad_buffer_head qbh;
/*
* 0 is the root dnode
*/
if (pos == 0)
return inode->i_hpfs_dno;
/*
* we have one pos->dnode translation cached in the inode
*/
else if (pos == inode->i_hpfs_dpos)
return inode->i_hpfs_dsubdno;
/*
* otherwise go look
*/
else {
unsigned q = pos >> 6;
unsigned r = pos & 077;
dnode_secno dno;
/*
* dnode at position q
*/
dno = dir_subdno(inode, q);
if (dno == 0)
return 0;
/*
* entry at index r
*/
de = map_nth_dirent(inode->i_dev, dno, r, &qbh);
if (!de || !de->down)
return 0;
/*
* get the dnode down pointer
*/
dno = de_down_pointer(de);
brelse4(&qbh);
/*
* cache it for next time
*/
inode->i_hpfs_dpos = pos;
inode->i_hpfs_dsubdno = dno;
return dno;
}
}
/*
* Return the dir entry at index n in dnode dno, or 0 if there isn't one
*/
static struct hpfs_dirent *map_nth_dirent(dev_t dev, dnode_secno dno,
int n,
struct quad_buffer_head *qbh)
{
int i;
struct hpfs_dirent *de, *de_end;
struct dnode *dnode = map_dnode(dev, dno, qbh);
de = dnode_first_de(dnode);
de_end = dnode_end_de(dnode);
for (i = 1; de < de_end; i++, de = de_next_de(de)) {
if (i == n)
return de;
if (de->last || de->length == 0)
break;
}
brelse4(qbh);
return 0;
}
static int hpfs_dir_read(struct inode *inode, struct file *filp,
char *buf, int count)
{
return -EISDIR;
}
/* Return the dnode pointer in a directory fnode */
static dnode_secno fnode_dno(dev_t dev, ino_t ino)
{
struct buffer_head *bh;
struct fnode *fnode;
dnode_secno dno;
fnode = map_fnode(dev, ino, &bh);
if (!fnode)
return 0;
dno = fnode->u.external[0].disk_secno;
brelse(bh);
return dno;
}
/* Map an fnode into a buffer and return pointers to it and to the buffer. */
static struct fnode *map_fnode(dev_t dev, ino_t ino, struct buffer_head **bhp)
{
struct fnode *fnode;
if (ino == 0) {
printk("HPFS: missing fnode\n");
return 0;
}
fnode = map_sector(dev, ino_secno(ino), bhp);
if (fnode)
if (fnode->magic != FNODE_MAGIC) {
printk("HPFS: map_fnode: bad fnode pointer\n");
brelse(*bhp);
return 0;
}
return fnode;
}
/* Map an anode into a buffer and return pointers to it and to the buffer. */
static struct anode *map_anode(dev_t dev, unsigned secno,
struct buffer_head **bhp)
{
struct anode *anode;
if (secno == 0) {
printk("HPFS: missing anode\n");
return 0;
}
anode = map_sector(dev, secno, bhp);
if (anode)
if (anode->magic != ANODE_MAGIC || anode->self != secno) {
printk("HPFS: map_anode: bad anode pointer\n");
brelse(*bhp);
return 0;
}
return anode;
}
/* Map a dnode into a buffer and return pointers to it and to the buffer. */
static struct dnode *map_dnode(dev_t dev, unsigned secno,
struct quad_buffer_head *qbh)
{
struct dnode *dnode;
if (secno == 0) {
printk("HPFS: missing dnode\n");
return 0;
}
dnode = map_4sectors(dev, secno, qbh);
if (dnode)
if (dnode->magic != DNODE_MAGIC || dnode->self != secno) {
printk("HPFS: map_dnode: bad dnode pointer\n");
brelse4(qbh);
return 0;
}
return dnode;
}
/* Map a sector into a buffer and return pointers to it and to the buffer. */
static void *map_sector(dev_t dev, unsigned secno, struct buffer_head **bhp)
{
struct buffer_head *bh;
if ((*bhp = bh = bread(dev, secno, 512)) != 0)
return bh->b_data;
else {
printk("HPFS: map_sector: read error\n");
return 0;
}
}
/* Map 4 sectors into a 4buffer and return pointers to it and to the buffer. */
static void *map_4sectors(dev_t dev, unsigned secno,
struct quad_buffer_head *qbh)
{
struct buffer_head *bh;
char *data;
if (secno & 3) {
printk("HPFS: map_4sectors: unaligned read\n");
return 0;
}
qbh->data = data = kmalloc(2048, GFP_KERNEL);
if (!data)
goto bail;
qbh->bh[0] = bh = breada(dev,
secno, secno + 1, secno + 2, secno + 3, -1);
if (!bh)
goto bail0;
memcpy(data, bh->b_data, 512);
qbh->bh[1] = bh = bread(dev, secno + 1, 512);
if (!bh)
goto bail1;
memcpy(data + 512, bh->b_data, 512);
qbh->bh[2] = bh = bread(dev, secno + 2, 512);
if (!bh)
goto bail2;
memcpy(data + 2 * 512, bh->b_data, 512);
qbh->bh[3] = bh = bread(dev, secno + 3, 512);
if (!bh)
goto bail3;
memcpy(data + 3 * 512, bh->b_data, 512);
return data;
bail3:
brelse(qbh->bh[2]);
bail2:
brelse(qbh->bh[1]);
bail1:
brelse(qbh->bh[0]);
bail0:
kfree_s(data, 2048);
bail:
printk("HPFS: map_4sectors: read error\n");
return 0;
}
/* Deallocate a 4-buffer block */
static void brelse4(struct quad_buffer_head *qbh)
{
brelse(qbh->bh[3]);
brelse(qbh->bh[2]);
brelse(qbh->bh[1]);
brelse(qbh->bh[0]);
kfree_s(qbh->data, 2048);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -