OpenCores
URL https://opencores.org/ocsvn/or1k_old/or1k_old/trunk

Subversion Repositories or1k_old

[/] [or1k_old/] [trunk/] [rc203soc/] [sw/] [uClinux/] [fs/] [hpfs/] [hpfs_fs.c] - Diff between revs 1765 and 1782

Only display areas with differences | Details | Blame | View Log

Rev 1765 Rev 1782
/*
/*
 *  linux/fs/hpfs/hpfs_fs.c
 *  linux/fs/hpfs/hpfs_fs.c
 *  read-only HPFS
 *  read-only HPFS
 *  version 1.0
 *  version 1.0
 *
 *
 *  Chris Smith 1993
 *  Chris Smith 1993
 *
 *
 *  Sources & references:
 *  Sources & references:
 *   Duncan, _Design ... of HPFS_, MSJ 4(5)   (C) 1989 Microsoft Corp
 *   Duncan, _Design ... of HPFS_, MSJ 4(5)   (C) 1989 Microsoft Corp
 *   linux/fs/minix  Copyright (C) 1991, 1992, 1993  Linus Torvalds
 *   linux/fs/minix  Copyright (C) 1991, 1992, 1993  Linus Torvalds
 *   linux/fs/msdos  Written 1992, 1993 by Werner Almesberger
 *   linux/fs/msdos  Written 1992, 1993 by Werner Almesberger
 *   linux/fs/isofs  Copyright (C) 1991  Eric Youngdale
 *   linux/fs/isofs  Copyright (C) 1991  Eric Youngdale
 */
 */
 
 
#include <linux/module.h>
#include <linux/module.h>
 
 
#include <linux/fs.h>
#include <linux/fs.h>
#include <linux/hpfs_fs.h>
#include <linux/hpfs_fs.h>
#include <linux/errno.h>
#include <linux/errno.h>
#include <linux/malloc.h>
#include <linux/malloc.h>
#include <linux/kernel.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/sched.h>
#include <linux/locks.h>
#include <linux/locks.h>
#include <linux/stat.h>
#include <linux/stat.h>
#include <linux/string.h>
#include <linux/string.h>
#include <asm/bitops.h>
#include <asm/bitops.h>
#include <asm/segment.h>
#include <asm/segment.h>
 
 
#include "hpfs.h"
#include "hpfs.h"
#include "hpfs_caps.h"
#include "hpfs_caps.h"
 
 
/*
/*
 * HPFS is a mixture of 512-byte blocks and 2048-byte blocks.  The 2k blocks
 * HPFS is a mixture of 512-byte blocks and 2048-byte blocks.  The 2k blocks
 * are used for directories and bitmaps.  For bmap to work, we must run the
 * are used for directories and bitmaps.  For bmap to work, we must run the
 * file system with 512-byte blocks.  The 2k blocks are assembled in buffers
 * file system with 512-byte blocks.  The 2k blocks are assembled in buffers
 * obtained from kmalloc.
 * obtained from kmalloc.
 *
 *
 * For a file's i-number we use the sector number of its fnode, coded.
 * For a file's i-number we use the sector number of its fnode, coded.
 * (Directory ino's are even, file ino's are odd, and ino >> 1 is the
 * (Directory ino's are even, file ino's are odd, and ino >> 1 is the
 * sector address of the fnode.  This is a hack to allow lookup() to
 * sector address of the fnode.  This is a hack to allow lookup() to
 * tell read_inode() whether it is necessary to read the fnode.)
 * tell read_inode() whether it is necessary to read the fnode.)
 *
 *
 * The map_xxx routines all read something into a buffer and return a
 * The map_xxx routines all read something into a buffer and return a
 * pointer somewhere in the buffer.  The caller must do the brelse.
 * pointer somewhere in the buffer.  The caller must do the brelse.
 * The other routines are balanced.
 * The other routines are balanced.
 *
 *
 * For details on the data structures see hpfs.h and the Duncan paper.
 * For details on the data structures see hpfs.h and the Duncan paper.
 *
 *
 * Overview
 * Overview
 *
 *
 * [ The names of these data structures, except fnode, are not Microsoft's
 * [ The names of these data structures, except fnode, are not Microsoft's
 * or IBM's.  I don't know what names they use.  The semantics described
 * or IBM's.  I don't know what names they use.  The semantics described
 * here are those of this implementation, and any coincidence between it
 * here are those of this implementation, and any coincidence between it
 * and real HPFS is to be hoped for but not guaranteed by me, and
 * and real HPFS is to be hoped for but not guaranteed by me, and
 * certainly not guaranteed by MS or IBM.  Who know nothing about this. ]
 * certainly not guaranteed by MS or IBM.  Who know nothing about this. ]
 *
 *
 * [ Also, the following will make little sense if you haven't read the
 * [ Also, the following will make little sense if you haven't read the
 * Duncan paper, which is excellent. ]
 * Duncan paper, which is excellent. ]
 *
 *
 * HPFS is a tree.  There are 3 kinds of nodes.  A directory is a tree
 * HPFS is a tree.  There are 3 kinds of nodes.  A directory is a tree
 * of dnodes, and a file's allocation info is a tree of sector runs
 * of dnodes, and a file's allocation info is a tree of sector runs
 * stored in fnodes and anodes.
 * stored in fnodes and anodes.
 *
 *
 * The top pointer is in the super block, it points to the fnode of the
 * The top pointer is in the super block, it points to the fnode of the
 * root directory.
 * root directory.
 *
 *
 * The root directory -- all directories -- gives file names, dates &c,
 * The root directory -- all directories -- gives file names, dates &c,
 * and fnode addresses.  If the directory fits in one dnode, that's it,
 * and fnode addresses.  If the directory fits in one dnode, that's it,
 * otherwise the top dnode points to other dnodes, forming a tree.  A
 * otherwise the top dnode points to other dnodes, forming a tree.  A
 * dnode tree (one directory) might look like
 * dnode tree (one directory) might look like
 *
 *
 *     ((a b c) d (e f g) h (i j) k l (m n o p))
 *     ((a b c) d (e f g) h (i j) k l (m n o p))
 *
 *
 * The subtrees appear between the files.  Each dir entry contains, along
 * The subtrees appear between the files.  Each dir entry contains, along
 * with the name and fnode, a dnode pointer to the subtree that precedes it
 * with the name and fnode, a dnode pointer to the subtree that precedes it
 * (if there is one; a flag tells that).  The first entry in every directory
 * (if there is one; a flag tells that).  The first entry in every directory
 * is ^A^A, the "." entry for the directory itself.  The last entry in every
 * is ^A^A, the "." entry for the directory itself.  The last entry in every
 * dnode is \377, a fake entry whose only valid fields are the bit marking
 * dnode is \377, a fake entry whose only valid fields are the bit marking
 * it last and the down pointer to the subtree preceding it, if any.
 * it last and the down pointer to the subtree preceding it, if any.
 *
 *
 * The "value" field of directory entries is an fnode address.  The fnode
 * The "value" field of directory entries is an fnode address.  The fnode
 * tells where the sectors of the file are.  The fnode for a subdirectory
 * tells where the sectors of the file are.  The fnode for a subdirectory
 * contains one pointer, to the root dnode of the subdirectory.  The fnode
 * contains one pointer, to the root dnode of the subdirectory.  The fnode
 * for a data file contains, in effect, a tiny anode.  (Most of the space
 * for a data file contains, in effect, a tiny anode.  (Most of the space
 * in fnodes is for extended attributes.)
 * in fnodes is for extended attributes.)
 *
 *
 * anodes and the anode part of fnodes are trees of extents.  An extent
 * anodes and the anode part of fnodes are trees of extents.  An extent
 * is a (length, disk address) pair, labeled with the file address being
 * is a (length, disk address) pair, labeled with the file address being
 * mapped.  E.g.,
 * mapped.  E.g.,
 *
 *
 *     (0: 3@1000  3: 1@2000  4: 2@10)
 *     (0: 3@1000  3: 1@2000  4: 2@10)
 *
 *
 * means the file:disk sector map (0:1000 1:1001 2:1002 3:2000 4:10 5:11).
 * means the file:disk sector map (0:1000 1:1001 2:1002 3:2000 4:10 5:11).
 *
 *
 * There is space for 8 file:len@disk triples in an fnode, or for 40 in an
 * There is space for 8 file:len@disk triples in an fnode, or for 40 in an
 * anode.  If this is insufficient, subtrees are used, as in
 * anode.  If this is insufficient, subtrees are used, as in
 *
 *
 *  (6: (0: 3@1000  3: 1@2000  4: 2@10)  12: (6: 3@8000  9: 1@9000  10: 2@20))
 *  (6: (0: 3@1000  3: 1@2000  4: 2@10)  12: (6: 3@8000  9: 1@9000  10: 2@20))
 *
 *
 * The label on a subtree is the first address *after* that tree.  The
 * The label on a subtree is the first address *after* that tree.  The
 * subtrees are always anodes.  The label:subtree pairs require only
 * subtrees are always anodes.  The label:subtree pairs require only
 * two words each, so non-leaf subtrees have a different format; there
 * two words each, so non-leaf subtrees have a different format; there
 * is room for 12 label:subtree pairs in an fnode, or 60 in an anode.
 * is room for 12 label:subtree pairs in an fnode, or 60 in an anode.
 *
 *
 * Within a directory, each dnode contains a pointer up to its parent
 * Within a directory, each dnode contains a pointer up to its parent
 * dnode.  The root dnode points up to the directory's fnode.
 * dnode.  The root dnode points up to the directory's fnode.
 *
 *
 * Each fnode contains a pointer to the directory that contains it
 * Each fnode contains a pointer to the directory that contains it
 * (to the fnode of the directory).  So this pointer in a directory
 * (to the fnode of the directory).  So this pointer in a directory
 * fnode is "..".
 * fnode is "..".
 *
 *
 * On the disk, dnodes are all together in the center of the partition,
 * On the disk, dnodes are all together in the center of the partition,
 * and HPFS even manages to put all the dnodes for a single directory
 * and HPFS even manages to put all the dnodes for a single directory
 * together, generally.  fnodes are out with the data.  anodes are seldom
 * together, generally.  fnodes are out with the data.  anodes are seldom
 * seen -- in fact noncontiguous files are seldom seen.  I think this is
 * seen -- in fact noncontiguous files are seldom seen.  I think this is
 * partly the open() call that lets programs specify the length of an
 * partly the open() call that lets programs specify the length of an
 * output file when they know it, and partly because HPFS.IFS really is
 * output file when they know it, and partly because HPFS.IFS really is
 * very good at resisting fragmentation.
 * very good at resisting fragmentation.
 */
 */


/* notation */
/* notation */
 
 
#define little_ushort(x) (*(unsigned short *) &(x))
#define little_ushort(x) (*(unsigned short *) &(x))
typedef void nonconst;
typedef void nonconst;
 
 
/* super block ops */
/* super block ops */
 
 
static void hpfs_read_inode(struct inode *);
static void hpfs_read_inode(struct inode *);
static void hpfs_put_super(struct super_block *);
static void hpfs_put_super(struct super_block *);
static void hpfs_statfs(struct super_block *, struct statfs *, int);
static void hpfs_statfs(struct super_block *, struct statfs *, int);
static int hpfs_remount_fs(struct super_block *, int *, char *);
static int hpfs_remount_fs(struct super_block *, int *, char *);
 
 
static const struct super_operations hpfs_sops =
static const struct super_operations hpfs_sops =
{
{
        hpfs_read_inode,                /* read_inode */
        hpfs_read_inode,                /* read_inode */
        NULL,                           /* notify_change */
        NULL,                           /* notify_change */
        NULL,                           /* write_inode */
        NULL,                           /* write_inode */
        NULL,                           /* put_inode */
        NULL,                           /* put_inode */
        hpfs_put_super,                 /* put_super */
        hpfs_put_super,                 /* put_super */
        NULL,                           /* write_super */
        NULL,                           /* write_super */
        hpfs_statfs,                    /* statfs */
        hpfs_statfs,                    /* statfs */
        hpfs_remount_fs,                /* remount_fs */
        hpfs_remount_fs,                /* remount_fs */
};
};
 
 
/* file ops */
/* file ops */
 
 
static int hpfs_file_read(struct inode *, struct file *, char *, int);
static int hpfs_file_read(struct inode *, struct file *, char *, int);
static secno hpfs_bmap(struct inode *, unsigned);
static secno hpfs_bmap(struct inode *, unsigned);
 
 
static const struct file_operations hpfs_file_ops =
static const struct file_operations hpfs_file_ops =
{
{
        NULL,                           /* lseek - default */
        NULL,                           /* lseek - default */
        hpfs_file_read,                 /* read */
        hpfs_file_read,                 /* read */
        NULL,                           /* write */
        NULL,                           /* write */
        NULL,                           /* readdir - bad */
        NULL,                           /* readdir - bad */
        NULL,                           /* select - default */
        NULL,                           /* select - default */
        NULL,                           /* ioctl - default */
        NULL,                           /* ioctl - default */
        generic_file_mmap,              /* mmap */
        generic_file_mmap,              /* mmap */
        NULL,                           /* no special open is needed */
        NULL,                           /* no special open is needed */
        NULL,                           /* release */
        NULL,                           /* release */
        file_fsync,                     /* fsync */
        file_fsync,                     /* fsync */
};
};
 
 
static const struct inode_operations hpfs_file_iops =
static const struct inode_operations hpfs_file_iops =
{
{
        (nonconst *) & hpfs_file_ops,   /* default file operations */
        (nonconst *) & hpfs_file_ops,   /* default file operations */
        NULL,                           /* create */
        NULL,                           /* create */
        NULL,                           /* lookup */
        NULL,                           /* lookup */
        NULL,                           /* link */
        NULL,                           /* link */
        NULL,                           /* unlink */
        NULL,                           /* unlink */
        NULL,                           /* symlink */
        NULL,                           /* symlink */
        NULL,                           /* mkdir */
        NULL,                           /* mkdir */
        NULL,                           /* rmdir */
        NULL,                           /* rmdir */
        NULL,                           /* mknod */
        NULL,                           /* mknod */
        NULL,                           /* rename */
        NULL,                           /* rename */
        NULL,                           /* readlink */
        NULL,                           /* readlink */
        NULL,                           /* follow_link */
        NULL,                           /* follow_link */
        generic_readpage,               /* readpage */
        generic_readpage,               /* readpage */
        NULL,                           /* writepage */
        NULL,                           /* writepage */
        (int (*)(struct inode *, int))
        (int (*)(struct inode *, int))
        &hpfs_bmap,                     /* bmap */
        &hpfs_bmap,                     /* bmap */
        NULL,                           /* truncate */
        NULL,                           /* truncate */
        NULL,                           /* permission */
        NULL,                           /* permission */
};
};
 
 
/* directory ops */
/* directory ops */
 
 
static int hpfs_dir_read(struct inode *inode, struct file *filp,
static int hpfs_dir_read(struct inode *inode, struct file *filp,
                         char *buf, int count);
                         char *buf, int count);
static int hpfs_readdir(struct inode *inode, struct file *filp,
static int hpfs_readdir(struct inode *inode, struct file *filp,
                        void *dirent, filldir_t filldir);
                        void *dirent, filldir_t filldir);
static int hpfs_lookup(struct inode *, const char *, int, struct inode **);
static int hpfs_lookup(struct inode *, const char *, int, struct inode **);
 
 
static const struct file_operations hpfs_dir_ops =
static const struct file_operations hpfs_dir_ops =
{
{
        NULL,                           /* lseek - default */
        NULL,                           /* lseek - default */
        hpfs_dir_read,                  /* read */
        hpfs_dir_read,                  /* read */
        NULL,                           /* write - bad */
        NULL,                           /* write - bad */
        hpfs_readdir,                   /* readdir */
        hpfs_readdir,                   /* readdir */
        NULL,                           /* select - default */
        NULL,                           /* select - default */
        NULL,                           /* ioctl - default */
        NULL,                           /* ioctl - default */
        NULL,                           /* mmap */
        NULL,                           /* mmap */
        NULL,                           /* no special open code */
        NULL,                           /* no special open code */
        NULL,                           /* no special release code */
        NULL,                           /* no special release code */
        file_fsync,                     /* fsync */
        file_fsync,                     /* fsync */
};
};
 
 
static const struct inode_operations hpfs_dir_iops =
static const struct inode_operations hpfs_dir_iops =
{
{
        (nonconst *) & hpfs_dir_ops,    /* default directory file ops */
        (nonconst *) & hpfs_dir_ops,    /* default directory file ops */
        NULL,                           /* create */
        NULL,                           /* create */
        hpfs_lookup,                    /* lookup */
        hpfs_lookup,                    /* lookup */
        NULL,                           /* link */
        NULL,                           /* link */
        NULL,                           /* unlink */
        NULL,                           /* unlink */
        NULL,                           /* symlink */
        NULL,                           /* symlink */
        NULL,                           /* mkdir */
        NULL,                           /* mkdir */
        NULL,                           /* rmdir */
        NULL,                           /* rmdir */
        NULL,                           /* mknod */
        NULL,                           /* mknod */
        NULL,                           /* rename */
        NULL,                           /* rename */
        NULL,                           /* readlink */
        NULL,                           /* readlink */
        NULL,                           /* follow_link */
        NULL,                           /* follow_link */
        NULL,                           /* readpage */
        NULL,                           /* readpage */
        NULL,                           /* writepage */
        NULL,                           /* writepage */
        NULL,                           /* bmap */
        NULL,                           /* bmap */
        NULL,                           /* truncate */
        NULL,                           /* truncate */
        NULL,                           /* permission */
        NULL,                           /* permission */
};
};
 
 
/* Four 512-byte buffers and the 2k block obtained by concatenating them */
/* Four 512-byte buffers and the 2k block obtained by concatenating them */
 
 
struct quad_buffer_head {
struct quad_buffer_head {
        struct buffer_head *bh[4];
        struct buffer_head *bh[4];
        void *data;
        void *data;
};
};
 
 
/* forwards */
/* forwards */
 
 
static int parse_opts(char *opts, uid_t *uid, gid_t *gid, umode_t *umask,
static int parse_opts(char *opts, uid_t *uid, gid_t *gid, umode_t *umask,
                      int *lowercase, int *conv, int *nocheck);
                      int *lowercase, int *conv, int *nocheck);
static int check_warn(int not_ok,
static int check_warn(int not_ok,
                      const char *p1, const char *p2, const char *p3);
                      const char *p1, const char *p2, const char *p3);
static int zerop(void *addr, unsigned len);
static int zerop(void *addr, unsigned len);
static void count_dnodes(struct inode *inode, dnode_secno dno,
static void count_dnodes(struct inode *inode, dnode_secno dno,
                         unsigned *n_dnodes, unsigned *n_subdirs);
                         unsigned *n_dnodes, unsigned *n_subdirs);
static unsigned count_bitmap(struct super_block *s);
static unsigned count_bitmap(struct super_block *s);
static unsigned count_one_bitmap(kdev_t dev, secno secno);
static unsigned count_one_bitmap(kdev_t dev, secno secno);
static secno bplus_lookup(struct inode *inode, struct bplus_header *b,
static secno bplus_lookup(struct inode *inode, struct bplus_header *b,
                          secno file_secno, struct buffer_head **bhp);
                          secno file_secno, struct buffer_head **bhp);
static struct hpfs_dirent *map_dirent(struct inode *inode, dnode_secno dno,
static struct hpfs_dirent *map_dirent(struct inode *inode, dnode_secno dno,
                                    const unsigned char *name, unsigned len,
                                    const unsigned char *name, unsigned len,
                                      struct quad_buffer_head *qbh);
                                      struct quad_buffer_head *qbh);
static struct hpfs_dirent *map_pos_dirent(struct inode *inode, loff_t *posp,
static struct hpfs_dirent *map_pos_dirent(struct inode *inode, loff_t *posp,
                                          struct quad_buffer_head *qbh);
                                          struct quad_buffer_head *qbh);
static dnode_secno dir_subdno(struct inode *inode, unsigned pos);
static dnode_secno dir_subdno(struct inode *inode, unsigned pos);
static struct hpfs_dirent *map_nth_dirent(kdev_t dev, dnode_secno dno,
static struct hpfs_dirent *map_nth_dirent(kdev_t dev, dnode_secno dno,
                                          int n,
                                          int n,
                                          struct quad_buffer_head *qbh);
                                          struct quad_buffer_head *qbh);
static unsigned choose_conv(unsigned char *p, unsigned len);
static unsigned choose_conv(unsigned char *p, unsigned len);
static unsigned convcpy_tofs(unsigned char *out, unsigned char *in,
static unsigned convcpy_tofs(unsigned char *out, unsigned char *in,
                             unsigned len);
                             unsigned len);
static dnode_secno fnode_dno(kdev_t dev, ino_t ino);
static dnode_secno fnode_dno(kdev_t dev, ino_t ino);
static struct fnode *map_fnode(kdev_t dev, ino_t ino,
static struct fnode *map_fnode(kdev_t dev, ino_t ino,
                               struct buffer_head **bhp);
                               struct buffer_head **bhp);
static struct anode *map_anode(kdev_t dev, unsigned secno,
static struct anode *map_anode(kdev_t dev, unsigned secno,
                               struct buffer_head **bhp);
                               struct buffer_head **bhp);
static struct dnode *map_dnode(kdev_t dev, unsigned secno,
static struct dnode *map_dnode(kdev_t dev, unsigned secno,
                               struct quad_buffer_head *qbh);
                               struct quad_buffer_head *qbh);
static void *map_sector(kdev_t dev, unsigned secno, struct buffer_head **bhp);
static void *map_sector(kdev_t dev, unsigned secno, struct buffer_head **bhp);
static void *map_4sectors(kdev_t dev, unsigned secno,
static void *map_4sectors(kdev_t dev, unsigned secno,
                          struct quad_buffer_head *qbh);
                          struct quad_buffer_head *qbh);
static void brelse4(struct quad_buffer_head *qbh);
static void brelse4(struct quad_buffer_head *qbh);
 
 
/*
/*
 * make inode number for a file
 * make inode number for a file
 */
 */
 
 
static inline ino_t file_ino(fnode_secno secno)
static inline ino_t file_ino(fnode_secno secno)
{
{
        return secno << 1 | 1;
        return secno << 1 | 1;
}
}
 
 
/*
/*
 * make inode number for a directory
 * make inode number for a directory
 */
 */
 
 
static inline ino_t dir_ino(fnode_secno secno)
static inline ino_t dir_ino(fnode_secno secno)
{
{
        return secno << 1;
        return secno << 1;
}
}
 
 
/*
/*
 * get fnode address from an inode number
 * get fnode address from an inode number
 */
 */
 
 
static inline fnode_secno ino_secno(ino_t ino)
static inline fnode_secno ino_secno(ino_t ino)
{
{
        return ino >> 1;
        return ino >> 1;
}
}
 
 
/*
/*
 * test for directory's inode number
 * test for directory's inode number
 */
 */
 
 
static inline int ino_is_dir(ino_t ino)
static inline int ino_is_dir(ino_t ino)
{
{
        return (ino & 1) == 0;
        return (ino & 1) == 0;
}
}
 
 
/*
/*
 * conv= options
 * conv= options
 */
 */
 
 
#define CONV_BINARY 0                   /* no conversion */
#define CONV_BINARY 0                   /* no conversion */
#define CONV_TEXT 1                     /* crlf->newline */
#define CONV_TEXT 1                     /* crlf->newline */
#define CONV_AUTO 2                     /* decide based on file contents */
#define CONV_AUTO 2                     /* decide based on file contents */
 
 
/*
/*
 * local time (HPFS) to GMT (Unix)
 * local time (HPFS) to GMT (Unix)
 */
 */
 
 
static inline time_t local_to_gmt(time_t t)
static inline time_t local_to_gmt(time_t t)
{
{
        extern struct timezone sys_tz;
        extern struct timezone sys_tz;
        return t + sys_tz.tz_minuteswest * 60;
        return t + sys_tz.tz_minuteswest * 60;
}
}


/* super block ops */
/* super block ops */
 
 
/*
/*
 * mount.  This gets one thing, the root directory inode.  It does a
 * mount.  This gets one thing, the root directory inode.  It does a
 * bunch of guessed-at consistency checks.
 * bunch of guessed-at consistency checks.
 */
 */
 
 
struct super_block *hpfs_read_super(struct super_block *s,
struct super_block *hpfs_read_super(struct super_block *s,
                                    void *options, int silent)
                                    void *options, int silent)
{
{
        struct hpfs_boot_block *bootblock;
        struct hpfs_boot_block *bootblock;
        struct hpfs_super_block *superblock;
        struct hpfs_super_block *superblock;
        struct hpfs_spare_block *spareblock;
        struct hpfs_spare_block *spareblock;
        struct hpfs_dirent *de;
        struct hpfs_dirent *de;
        struct buffer_head *bh0, *bh1, *bh2;
        struct buffer_head *bh0, *bh1, *bh2;
        struct quad_buffer_head qbh;
        struct quad_buffer_head qbh;
        dnode_secno root_dno;
        dnode_secno root_dno;
        kdev_t dev;
        kdev_t dev;
        uid_t uid;
        uid_t uid;
        gid_t gid;
        gid_t gid;
        umode_t umask;
        umode_t umask;
        int lowercase;
        int lowercase;
        int conv;
        int conv;
        int dubious;
        int dubious;
        int nocheck;
        int nocheck;
 
 
        MOD_INC_USE_COUNT;
        MOD_INC_USE_COUNT;
 
 
        /*
        /*
         * Get the mount options
         * Get the mount options
         */
         */
 
 
        if (!parse_opts(options, &uid, &gid, &umask, &lowercase, &conv,
        if (!parse_opts(options, &uid, &gid, &umask, &lowercase, &conv,
                                 &nocheck)) {
                                 &nocheck)) {
                printk("HPFS: syntax error in mount options.  Not mounted.\n");
                printk("HPFS: syntax error in mount options.  Not mounted.\n");
                s->s_dev = 0;
                s->s_dev = 0;
                MOD_DEC_USE_COUNT;
                MOD_DEC_USE_COUNT;
                return 0;
                return 0;
        }
        }
 
 
        /*
        /*
         * Fill in the super block struct
         * Fill in the super block struct
         */
         */
 
 
        lock_super(s);
        lock_super(s);
        dev = s->s_dev;
        dev = s->s_dev;
        set_blocksize(dev, 512);
        set_blocksize(dev, 512);
 
 
        /*
        /*
         * fetch sectors 0, 16, 17
         * fetch sectors 0, 16, 17
         */
         */
 
 
        bootblock = map_sector(dev, 0, &bh0);
        bootblock = map_sector(dev, 0, &bh0);
        if (!bootblock)
        if (!bootblock)
                goto bail;
                goto bail;
 
 
        superblock = map_sector(dev, 16, &bh1);
        superblock = map_sector(dev, 16, &bh1);
        if (!superblock)
        if (!superblock)
                goto bail0;
                goto bail0;
 
 
        spareblock = map_sector(dev, 17, &bh2);
        spareblock = map_sector(dev, 17, &bh2);
        if (!spareblock)
        if (!spareblock)
                goto bail1;
                goto bail1;
 
 
        /*
        /*
         * Check that this fs looks enough like a known one that we can find
         * Check that this fs looks enough like a known one that we can find
         * and read the root directory.
         * and read the root directory.
         */
         */
 
 
        if (bootblock->magic != 0xaa55
        if (bootblock->magic != 0xaa55
            || superblock->magic != SB_MAGIC
            || superblock->magic != SB_MAGIC
            || spareblock->magic != SP_MAGIC
            || spareblock->magic != SP_MAGIC
            || bootblock->sig_28h != 0x28
            || bootblock->sig_28h != 0x28
            || memcmp(&bootblock->sig_hpfs, "HPFS    ", 8)
            || memcmp(&bootblock->sig_hpfs, "HPFS    ", 8)
            || little_ushort(bootblock->bytes_per_sector) != 512) {
            || little_ushort(bootblock->bytes_per_sector) != 512) {
                printk("HPFS: hpfs_read_super: Not HPFS\n");
                printk("HPFS: hpfs_read_super: Not HPFS\n");
                goto bail2;
                goto bail2;
        }
        }
 
 
        /*
        /*
         * Check for inconsistencies -- possibly wrong guesses here, possibly
         * Check for inconsistencies -- possibly wrong guesses here, possibly
         * filesystem problems.
         * filesystem problems.
         */
         */
 
 
        dubious = 0;
        dubious = 0;
 
 
        dubious |= check_warn(spareblock->dirty != 0,
        dubious |= check_warn(spareblock->dirty != 0,
                       "`Improperly stopped'", "flag is set", "run CHKDSK");
                       "`Improperly stopped'", "flag is set", "run CHKDSK");
        dubious |= check_warn(spareblock->n_spares_used != 0,
        dubious |= check_warn(spareblock->n_spares_used != 0,
                              "Spare blocks", "may be in use", "run CHKDSK");
                              "Spare blocks", "may be in use", "run CHKDSK");
 
 
        /*
        /*
         * Above errors mean we could get wrong answers if we proceed,
         * Above errors mean we could get wrong answers if we proceed,
         * so don't
         * so don't
         */
         */
 
 
        if (dubious && !nocheck)
        if (dubious && !nocheck)
                goto bail2;
                goto bail2;
 
 
        dubious |= check_warn((spareblock->n_dnode_spares !=
        dubious |= check_warn((spareblock->n_dnode_spares !=
                               spareblock->n_dnode_spares_free),
                               spareblock->n_dnode_spares_free),
                              "Spare dnodes", "may be in use", "run CHKDSK");
                              "Spare dnodes", "may be in use", "run CHKDSK");
        dubious |= check_warn(superblock->zero1 != 0,
        dubious |= check_warn(superblock->zero1 != 0,
                              "#1", "unknown word nonzero", "investigate");
                              "#1", "unknown word nonzero", "investigate");
        dubious |= check_warn(superblock->zero3 != 0,
        dubious |= check_warn(superblock->zero3 != 0,
                              "#3", "unknown word nonzero", "investigate");
                              "#3", "unknown word nonzero", "investigate");
        dubious |= check_warn(superblock->zero4 != 0,
        dubious |= check_warn(superblock->zero4 != 0,
                              "#4", "unknown word nonzero", "investigate");
                              "#4", "unknown word nonzero", "investigate");
        dubious |= check_warn(!zerop(superblock->zero5,
        dubious |= check_warn(!zerop(superblock->zero5,
                                     sizeof superblock->zero5),
                                     sizeof superblock->zero5),
                              "#5", "unknown word nonzero", "investigate");
                              "#5", "unknown word nonzero", "investigate");
        dubious |= check_warn(!zerop(superblock->zero6,
        dubious |= check_warn(!zerop(superblock->zero6,
                                     sizeof superblock->zero6),
                                     sizeof superblock->zero6),
                              "#6", "unknown word nonzero", "investigate");
                              "#6", "unknown word nonzero", "investigate");
 
 
        if (dubious)
        if (dubious)
                printk("HPFS: Proceeding, but operation may be unreliable\n");
                printk("HPFS: Proceeding, but operation may be unreliable\n");
 
 
        /*
        /*
         * set fs read only
         * set fs read only
         */
         */
 
 
        s->s_flags |= MS_RDONLY;
        s->s_flags |= MS_RDONLY;
 
 
        /*
        /*
         * fill in standard stuff
         * fill in standard stuff
         */
         */
 
 
        s->s_magic = HPFS_SUPER_MAGIC;
        s->s_magic = HPFS_SUPER_MAGIC;
        s->s_blocksize = 512;
        s->s_blocksize = 512;
        s->s_blocksize_bits = 9;
        s->s_blocksize_bits = 9;
        s->s_op = (struct super_operations *) &hpfs_sops;
        s->s_op = (struct super_operations *) &hpfs_sops;
 
 
        /*
        /*
         * fill in hpfs stuff
         * fill in hpfs stuff
         */
         */
 
 
        s->s_hpfs_root = dir_ino(superblock->root);
        s->s_hpfs_root = dir_ino(superblock->root);
        s->s_hpfs_fs_size = superblock->n_sectors;
        s->s_hpfs_fs_size = superblock->n_sectors;
        s->s_hpfs_dirband_size = superblock->n_dir_band / 4;
        s->s_hpfs_dirband_size = superblock->n_dir_band / 4;
        s->s_hpfs_dmap = superblock->dir_band_bitmap;
        s->s_hpfs_dmap = superblock->dir_band_bitmap;
        s->s_hpfs_bitmaps = superblock->bitmaps;
        s->s_hpfs_bitmaps = superblock->bitmaps;
        s->s_hpfs_uid = uid;
        s->s_hpfs_uid = uid;
        s->s_hpfs_gid = gid;
        s->s_hpfs_gid = gid;
        s->s_hpfs_mode = 0777 & ~umask;
        s->s_hpfs_mode = 0777 & ~umask;
        s->s_hpfs_n_free = -1;
        s->s_hpfs_n_free = -1;
        s->s_hpfs_n_free_dnodes = -1;
        s->s_hpfs_n_free_dnodes = -1;
        s->s_hpfs_lowercase = lowercase;
        s->s_hpfs_lowercase = lowercase;
        s->s_hpfs_conv = conv;
        s->s_hpfs_conv = conv;
 
 
        /*
        /*
         * done with the low blocks
         * done with the low blocks
         */
         */
 
 
        brelse(bh2);
        brelse(bh2);
        brelse(bh1);
        brelse(bh1);
        brelse(bh0);
        brelse(bh0);
 
 
        /*
        /*
         * all set.  try it out.
         * all set.  try it out.
         */
         */
 
 
        s->s_mounted = iget(s, s->s_hpfs_root);
        s->s_mounted = iget(s, s->s_hpfs_root);
        unlock_super(s);
        unlock_super(s);
 
 
        if (!s->s_mounted) {
        if (!s->s_mounted) {
                printk("HPFS: hpfs_read_super: inode get failed\n");
                printk("HPFS: hpfs_read_super: inode get failed\n");
                s->s_dev = 0;
                s->s_dev = 0;
                MOD_DEC_USE_COUNT;
                MOD_DEC_USE_COUNT;
                return 0;
                return 0;
        }
        }
 
 
        /*
        /*
         * find the root directory's . pointer & finish filling in the inode
         * find the root directory's . pointer & finish filling in the inode
         */
         */
 
 
        root_dno = fnode_dno(dev, s->s_hpfs_root);
        root_dno = fnode_dno(dev, s->s_hpfs_root);
        if (root_dno)
        if (root_dno)
                de = map_dirent(s->s_mounted, root_dno, "\001\001", 2, &qbh);
                de = map_dirent(s->s_mounted, root_dno, "\001\001", 2, &qbh);
        if (!root_dno || !de) {
        if (!root_dno || !de) {
                printk("HPFS: "
                printk("HPFS: "
                       "hpfs_read_super: root dir isn't in the root dir\n");
                       "hpfs_read_super: root dir isn't in the root dir\n");
                s->s_dev = 0;
                s->s_dev = 0;
                MOD_DEC_USE_COUNT;
                MOD_DEC_USE_COUNT;
                return 0;
                return 0;
        }
        }
 
 
        s->s_mounted->i_atime = local_to_gmt(de->read_date);
        s->s_mounted->i_atime = local_to_gmt(de->read_date);
        s->s_mounted->i_mtime = local_to_gmt(de->write_date);
        s->s_mounted->i_mtime = local_to_gmt(de->write_date);
        s->s_mounted->i_ctime = local_to_gmt(de->creation_date);
        s->s_mounted->i_ctime = local_to_gmt(de->creation_date);
 
 
        brelse4(&qbh);
        brelse4(&qbh);
        return s;
        return s;
 
 
 bail2:
 bail2:
        brelse(bh2);
        brelse(bh2);
 bail1:
 bail1:
        brelse(bh1);
        brelse(bh1);
 bail0:
 bail0:
        brelse(bh0);
        brelse(bh0);
 bail:
 bail:
        s->s_dev = 0;
        s->s_dev = 0;
        unlock_super(s);
        unlock_super(s);
        MOD_DEC_USE_COUNT;
        MOD_DEC_USE_COUNT;
        return 0;
        return 0;
}
}
 
 
static int check_warn(int not_ok,
static int check_warn(int not_ok,
                      const char *p1, const char *p2, const char *p3)
                      const char *p1, const char *p2, const char *p3)
{
{
        if (not_ok)
        if (not_ok)
                printk("HPFS: %s %s. Please %s\n", p1, p2, p3);
                printk("HPFS: %s %s. Please %s\n", p1, p2, p3);
        return not_ok;
        return not_ok;
}
}
 
 
static int zerop(void *addr, unsigned len)
static int zerop(void *addr, unsigned len)
{
{
        unsigned char *p = addr;
        unsigned char *p = addr;
        return p[0] == 0 && memcmp(p, p + 1, len - 1) == 0;
        return p[0] == 0 && memcmp(p, p + 1, len - 1) == 0;
}
}
 
 
/*
/*
 * A tiny parser for option strings, stolen from dosfs.
 * A tiny parser for option strings, stolen from dosfs.
 */
 */
 
 
static int parse_opts(char *opts, uid_t *uid, gid_t *gid, umode_t *umask,
static int parse_opts(char *opts, uid_t *uid, gid_t *gid, umode_t *umask,
                      int *lowercase, int *conv, int *nocheck)
                      int *lowercase, int *conv, int *nocheck)
{
{
        char *p, *rhs;
        char *p, *rhs;
 
 
        *uid = current->uid;
        *uid = current->uid;
        *gid = current->gid;
        *gid = current->gid;
        *umask = current->fs->umask;
        *umask = current->fs->umask;
        *lowercase = 1;
        *lowercase = 1;
        *conv = CONV_BINARY;
        *conv = CONV_BINARY;
        *nocheck = 0;
        *nocheck = 0;
 
 
        if (!opts)
        if (!opts)
                return 1;
                return 1;
 
 
        for (p = strtok(opts, ","); p != 0; p = strtok(0, ",")) {
        for (p = strtok(opts, ","); p != 0; p = strtok(0, ",")) {
                if ((rhs = strchr(p, '=')) != 0)
                if ((rhs = strchr(p, '=')) != 0)
                        *rhs++ = '\0';
                        *rhs++ = '\0';
                if (!strcmp(p, "uid")) {
                if (!strcmp(p, "uid")) {
                        if (!rhs || !*rhs)
                        if (!rhs || !*rhs)
                                return 0;
                                return 0;
                        *uid = simple_strtoul(rhs, &rhs, 0);
                        *uid = simple_strtoul(rhs, &rhs, 0);
                        if (*rhs)
                        if (*rhs)
                                return 0;
                                return 0;
                }
                }
                else if (!strcmp(p, "gid")) {
                else if (!strcmp(p, "gid")) {
                        if (!rhs || !*rhs)
                        if (!rhs || !*rhs)
                                return 0;
                                return 0;
                        *gid = simple_strtoul(rhs, &rhs, 0);
                        *gid = simple_strtoul(rhs, &rhs, 0);
                        if (*rhs)
                        if (*rhs)
                                return 0;
                                return 0;
                }
                }
                else if (!strcmp(p, "umask")) {
                else if (!strcmp(p, "umask")) {
                        if (!rhs || !*rhs)
                        if (!rhs || !*rhs)
                                return 0;
                                return 0;
                        *umask = simple_strtoul(rhs, &rhs, 8);
                        *umask = simple_strtoul(rhs, &rhs, 8);
                        if (*rhs)
                        if (*rhs)
                                return 0;
                                return 0;
                }
                }
                else if (!strcmp(p, "case")) {
                else if (!strcmp(p, "case")) {
                        if (!strcmp(rhs, "lower"))
                        if (!strcmp(rhs, "lower"))
                                *lowercase = 1;
                                *lowercase = 1;
                        else if (!strcmp(rhs, "asis"))
                        else if (!strcmp(rhs, "asis"))
                                *lowercase = 0;
                                *lowercase = 0;
                        else
                        else
                                return 0;
                                return 0;
                }
                }
                else if (!strcmp(p, "conv")) {
                else if (!strcmp(p, "conv")) {
                        if (!strcmp(rhs, "binary"))
                        if (!strcmp(rhs, "binary"))
                                *conv = CONV_BINARY;
                                *conv = CONV_BINARY;
                        else if (!strcmp(rhs, "text"))
                        else if (!strcmp(rhs, "text"))
                                *conv = CONV_TEXT;
                                *conv = CONV_TEXT;
                        else if (!strcmp(rhs, "auto"))
                        else if (!strcmp(rhs, "auto"))
                                *conv = CONV_AUTO;
                                *conv = CONV_AUTO;
                        else
                        else
                                return 0;
                                return 0;
                }
                }
                else if (!strcmp(p,"nocheck"))
                else if (!strcmp(p,"nocheck"))
                        *nocheck=1;
                        *nocheck=1;
                else
                else
                        return 1;
                        return 1;
        }
        }
 
 
        return 1;
        return 1;
}
}
 
 
/*
/*
 * read_inode.  This is called with exclusive access to a new inode that
 * read_inode.  This is called with exclusive access to a new inode that
 * has only (i_dev,i_ino) set.  It is responsible for filling in the rest.
 * has only (i_dev,i_ino) set.  It is responsible for filling in the rest.
 * We leave the dates blank, to be filled in from the dir entry.
 * We leave the dates blank, to be filled in from the dir entry.
 *
 *
 * NOTE that there must be no sleeping from the return in this routine
 * NOTE that there must be no sleeping from the return in this routine
 * until lookup() finishes filling in the inode, otherwise the partly
 * until lookup() finishes filling in the inode, otherwise the partly
 * completed inode would be visible during the sleep.
 * completed inode would be visible during the sleep.
 *
 *
 * It is done in this strange and sinful way because the alternative
 * It is done in this strange and sinful way because the alternative
 * is to read the fnode, find the dir pointer in it, read that fnode
 * is to read the fnode, find the dir pointer in it, read that fnode
 * to get the dnode pointer, search through that whole directory for
 * to get the dnode pointer, search through that whole directory for
 * the ino we're reading, and get the dates.  It works that way, but
 * the ino we're reading, and get the dates.  It works that way, but
 * ls sounds like fsck.
 * ls sounds like fsck.
 */
 */
 
 
static void hpfs_read_inode(struct inode *inode)
static void hpfs_read_inode(struct inode *inode)
{
{
        struct super_block *s = inode->i_sb;
        struct super_block *s = inode->i_sb;
 
 
        /* be ready to bail out */
        /* be ready to bail out */
 
 
        inode->i_op = 0;
        inode->i_op = 0;
        inode->i_mode = 0;
        inode->i_mode = 0;
 
 
        if (inode->i_ino == 0
        if (inode->i_ino == 0
            || ino_secno(inode->i_ino) >= inode->i_sb->s_hpfs_fs_size) {
            || ino_secno(inode->i_ino) >= inode->i_sb->s_hpfs_fs_size) {
                printk("HPFS: read_inode: bad ino\n");
                printk("HPFS: read_inode: bad ino\n");
                return;
                return;
        }
        }
 
 
        /*
        /*
         * canned stuff
         * canned stuff
         */
         */
 
 
        inode->i_uid = s->s_hpfs_uid;
        inode->i_uid = s->s_hpfs_uid;
        inode->i_gid = s->s_hpfs_gid;
        inode->i_gid = s->s_hpfs_gid;
        inode->i_mode = s->s_hpfs_mode;
        inode->i_mode = s->s_hpfs_mode;
        inode->i_hpfs_conv = s->s_hpfs_conv;
        inode->i_hpfs_conv = s->s_hpfs_conv;
 
 
        inode->i_hpfs_dno = 0;
        inode->i_hpfs_dno = 0;
        inode->i_hpfs_n_secs = 0;
        inode->i_hpfs_n_secs = 0;
        inode->i_hpfs_file_sec = 0;
        inode->i_hpfs_file_sec = 0;
        inode->i_hpfs_disk_sec = 0;
        inode->i_hpfs_disk_sec = 0;
        inode->i_hpfs_dpos = 0;
        inode->i_hpfs_dpos = 0;
        inode->i_hpfs_dsubdno = 0;
        inode->i_hpfs_dsubdno = 0;
 
 
        /*
        /*
         * figure out whether we are looking at a directory or a file
         * figure out whether we are looking at a directory or a file
         */
         */
 
 
        if (ino_is_dir(inode->i_ino))
        if (ino_is_dir(inode->i_ino))
                inode->i_mode |= S_IFDIR;
                inode->i_mode |= S_IFDIR;
        else {
        else {
                inode->i_mode |= S_IFREG;
                inode->i_mode |= S_IFREG;
                inode->i_mode &= ~0111;
                inode->i_mode &= ~0111;
        }
        }
 
 
        /*
        /*
         * these fields must be filled in from the dir entry, which we don't
         * these fields must be filled in from the dir entry, which we don't
         * have but lookup does.  It will fill them in before letting the
         * have but lookup does.  It will fill them in before letting the
         * inode out of its grasp.
         * inode out of its grasp.
         */
         */
 
 
        inode->i_atime = 0;
        inode->i_atime = 0;
        inode->i_mtime = 0;
        inode->i_mtime = 0;
        inode->i_ctime = 0;
        inode->i_ctime = 0;
        inode->i_size = 0;
        inode->i_size = 0;
 
 
        /*
        /*
         * fill in the rest
         * fill in the rest
         */
         */
 
 
        if (S_ISREG(inode->i_mode)) {
        if (S_ISREG(inode->i_mode)) {
 
 
                inode->i_op = (struct inode_operations *) &hpfs_file_iops;
                inode->i_op = (struct inode_operations *) &hpfs_file_iops;
                inode->i_nlink = 1;
                inode->i_nlink = 1;
                inode->i_blksize = 512;
                inode->i_blksize = 512;
 
 
        }
        }
        else {
        else {
                unsigned n_dnodes, n_subdirs;
                unsigned n_dnodes, n_subdirs;
                struct buffer_head *bh0;
                struct buffer_head *bh0;
                struct fnode *fnode = map_fnode(inode->i_dev,
                struct fnode *fnode = map_fnode(inode->i_dev,
                                                inode->i_ino, &bh0);
                                                inode->i_ino, &bh0);
 
 
                if (!fnode) {
                if (!fnode) {
                        printk("HPFS: read_inode: no fnode\n");
                        printk("HPFS: read_inode: no fnode\n");
                        inode->i_mode = 0;
                        inode->i_mode = 0;
                        return;
                        return;
                }
                }
 
 
                inode->i_hpfs_parent_dir = dir_ino(fnode->up);
                inode->i_hpfs_parent_dir = dir_ino(fnode->up);
                inode->i_hpfs_dno = fnode->u.external[0].disk_secno;
                inode->i_hpfs_dno = fnode->u.external[0].disk_secno;
 
 
                brelse(bh0);
                brelse(bh0);
 
 
                n_dnodes = n_subdirs = 0;
                n_dnodes = n_subdirs = 0;
                count_dnodes(inode, inode->i_hpfs_dno, &n_dnodes, &n_subdirs);
                count_dnodes(inode, inode->i_hpfs_dno, &n_dnodes, &n_subdirs);
 
 
                inode->i_op = (struct inode_operations *) &hpfs_dir_iops;
                inode->i_op = (struct inode_operations *) &hpfs_dir_iops;
                inode->i_blksize = 512; /* 2048 here confuses ls & du & ... */
                inode->i_blksize = 512; /* 2048 here confuses ls & du & ... */
                inode->i_blocks = 4 * n_dnodes;
                inode->i_blocks = 4 * n_dnodes;
                inode->i_size = 512 * inode->i_blocks;
                inode->i_size = 512 * inode->i_blocks;
                inode->i_nlink = 2 + n_subdirs;
                inode->i_nlink = 2 + n_subdirs;
        }
        }
}
}
 
 
/*
/*
 * unmount.
 * unmount.
 */
 */
 
 
static void hpfs_put_super(struct super_block *s)
static void hpfs_put_super(struct super_block *s)
{
{
        lock_super(s);
        lock_super(s);
        s->s_dev = 0;
        s->s_dev = 0;
        unlock_super(s);
        unlock_super(s);
        MOD_DEC_USE_COUNT;
        MOD_DEC_USE_COUNT;
}
}
 
 
/*
/*
 * statfs.  For free inode counts we report the count of dnodes in the
 * statfs.  For free inode counts we report the count of dnodes in the
 * directory band -- not exactly right but pretty analogous.
 * directory band -- not exactly right but pretty analogous.
 */
 */
 
 
static void hpfs_statfs(struct super_block *s, struct statfs *buf, int bufsiz)
static void hpfs_statfs(struct super_block *s, struct statfs *buf, int bufsiz)
{
{
        struct statfs tmp;
        struct statfs tmp;
 
 
        /*
        /*
         * count the bits in the bitmaps, unless we already have
         * count the bits in the bitmaps, unless we already have
         */
         */
        if (s->s_hpfs_n_free == -1) {
        if (s->s_hpfs_n_free == -1) {
                s->s_hpfs_n_free = count_bitmap(s);
                s->s_hpfs_n_free = count_bitmap(s);
                s->s_hpfs_n_free_dnodes =
                s->s_hpfs_n_free_dnodes =
                    count_one_bitmap(s->s_dev, s->s_hpfs_dmap);
                    count_one_bitmap(s->s_dev, s->s_hpfs_dmap);
        }
        }
 
 
        /*
        /*
         * fill in the user statfs struct
         * fill in the user statfs struct
         */
         */
        tmp.f_type = s->s_magic;
        tmp.f_type = s->s_magic;
        tmp.f_bsize = 512;
        tmp.f_bsize = 512;
        tmp.f_blocks = s->s_hpfs_fs_size;
        tmp.f_blocks = s->s_hpfs_fs_size;
        tmp.f_bfree = s->s_hpfs_n_free;
        tmp.f_bfree = s->s_hpfs_n_free;
        tmp.f_bavail = s->s_hpfs_n_free;
        tmp.f_bavail = s->s_hpfs_n_free;
        tmp.f_files = s->s_hpfs_dirband_size;
        tmp.f_files = s->s_hpfs_dirband_size;
        tmp.f_ffree = s->s_hpfs_n_free_dnodes;
        tmp.f_ffree = s->s_hpfs_n_free_dnodes;
        tmp.f_namelen = 254;
        tmp.f_namelen = 254;
        memcpy_tofs(buf, &tmp, bufsiz);
        memcpy_tofs(buf, &tmp, bufsiz);
}
}
 
 
/*
/*
 * remount.  Don't let read only be turned off.
 * remount.  Don't let read only be turned off.
 */
 */
 
 
static int hpfs_remount_fs(struct super_block *s, int *flags, char *data)
static int hpfs_remount_fs(struct super_block *s, int *flags, char *data)
{
{
        if (!(*flags & MS_RDONLY))
        if (!(*flags & MS_RDONLY))
                return -EINVAL;
                return -EINVAL;
        return 0;
        return 0;
}
}
 
 
/*
/*
 * count the dnodes in a directory, and the subdirs.
 * count the dnodes in a directory, and the subdirs.
 */
 */
 
 
static void count_dnodes(struct inode *inode, dnode_secno dno,
static void count_dnodes(struct inode *inode, dnode_secno dno,
                         unsigned *n_dnodes, unsigned *n_subdirs)
                         unsigned *n_dnodes, unsigned *n_subdirs)
{
{
        struct quad_buffer_head qbh;
        struct quad_buffer_head qbh;
        struct dnode *dnode;
        struct dnode *dnode;
        struct hpfs_dirent *de;
        struct hpfs_dirent *de;
        struct hpfs_dirent *de_end;
        struct hpfs_dirent *de_end;
 
 
        dnode = map_dnode(inode->i_dev, dno, &qbh);
        dnode = map_dnode(inode->i_dev, dno, &qbh);
        if (!dnode)
        if (!dnode)
                return;
                return;
        de = dnode_first_de(dnode);
        de = dnode_first_de(dnode);
        de_end = dnode_end_de(dnode);
        de_end = dnode_end_de(dnode);
 
 
        (*n_dnodes)++;
        (*n_dnodes)++;
 
 
        for (; de < de_end; de = de_next_de(de)) {
        for (; de < de_end; de = de_next_de(de)) {
                if (de->down)
                if (de->down)
                        count_dnodes(inode, de_down_pointer(de),
                        count_dnodes(inode, de_down_pointer(de),
                                     n_dnodes, n_subdirs);
                                     n_dnodes, n_subdirs);
                if (de->directory && !de->first)
                if (de->directory && !de->first)
                        (*n_subdirs)++;
                        (*n_subdirs)++;
                if (de->last || de->length == 0)
                if (de->last || de->length == 0)
                        break;
                        break;
        }
        }
 
 
        brelse4(&qbh);
        brelse4(&qbh);
}
}
 
 
/*
/*
 * count the bits in the free space bit maps
 * count the bits in the free space bit maps
 */
 */
 
 
static unsigned count_bitmap(struct super_block *s)
static unsigned count_bitmap(struct super_block *s)
{
{
        unsigned n, count, n_bands;
        unsigned n, count, n_bands;
        secno *bitmaps;
        secno *bitmaps;
        struct quad_buffer_head qbh;
        struct quad_buffer_head qbh;
 
 
        /*
        /*
         * there is one bit map for each 16384 sectors
         * there is one bit map for each 16384 sectors
         */
         */
        n_bands = (s->s_hpfs_fs_size + 0x3fff) >> 14;
        n_bands = (s->s_hpfs_fs_size + 0x3fff) >> 14;
 
 
        /*
        /*
         * their locations are given in an array pointed to by the super
         * their locations are given in an array pointed to by the super
         * block
         * block
         */
         */
        bitmaps = map_4sectors(s->s_dev, s->s_hpfs_bitmaps, &qbh);
        bitmaps = map_4sectors(s->s_dev, s->s_hpfs_bitmaps, &qbh);
        if (!bitmaps)
        if (!bitmaps)
                return 0;
                return 0;
 
 
        count = 0;
        count = 0;
 
 
        /*
        /*
         * map each one and count the free sectors
         * map each one and count the free sectors
         */
         */
        for (n = 0; n < n_bands; n++)
        for (n = 0; n < n_bands; n++)
                if (bitmaps[n] == 0)
                if (bitmaps[n] == 0)
                        printk("HPFS: bit map pointer missing\n");
                        printk("HPFS: bit map pointer missing\n");
                else
                else
                        count += count_one_bitmap(s->s_dev, bitmaps[n]);
                        count += count_one_bitmap(s->s_dev, bitmaps[n]);
 
 
        brelse4(&qbh);
        brelse4(&qbh);
        return count;
        return count;
}
}
 
 
/*
/*
 * Read in one bit map, count the bits, return the count.
 * Read in one bit map, count the bits, return the count.
 */
 */
 
 
static unsigned count_one_bitmap(kdev_t dev, secno secno)
static unsigned count_one_bitmap(kdev_t dev, secno secno)
{
{
        struct quad_buffer_head qbh;
        struct quad_buffer_head qbh;
        char *bits;
        char *bits;
        unsigned i, count;
        unsigned i, count;
 
 
        bits = map_4sectors(dev, secno, &qbh);
        bits = map_4sectors(dev, secno, &qbh);
        if (!bits)
        if (!bits)
                return 0;
                return 0;
 
 
        count = 0;
        count = 0;
 
 
        for (i = 0; i < 8 * 2048; i++)
        for (i = 0; i < 8 * 2048; i++)
                count += (test_bit(i, bits) != 0);
                count += (test_bit(i, bits) != 0);
        brelse4(&qbh);
        brelse4(&qbh);
 
 
        return count;
        return count;
}
}


/* file ops */
/* file ops */
 
 
/*
/*
 * read.  Read the bytes, put them in buf, return the count.
 * read.  Read the bytes, put them in buf, return the count.
 */
 */
 
 
static int hpfs_file_read(struct inode *inode, struct file *filp,
static int hpfs_file_read(struct inode *inode, struct file *filp,
                          char *buf, int count)
                          char *buf, int count)
{
{
        unsigned q, r, n, n0;
        unsigned q, r, n, n0;
        struct buffer_head *bh;
        struct buffer_head *bh;
        char *block;
        char *block;
        char *start;
        char *start;
 
 
        if (inode == 0 || !S_ISREG(inode->i_mode))
        if (inode == 0 || !S_ISREG(inode->i_mode))
                return -EINVAL;
                return -EINVAL;
 
 
        /*
        /*
         * truncate count at EOF
         * truncate count at EOF
         */
         */
        if (count > inode->i_size - (off_t) filp->f_pos)
        if (count > inode->i_size - (off_t) filp->f_pos)
                count = inode->i_size - filp->f_pos;
                count = inode->i_size - filp->f_pos;
 
 
        start = buf;
        start = buf;
        while (count > 0) {
        while (count > 0) {
                /*
                /*
                 * get file sector number, offset in sector, length to end of
                 * get file sector number, offset in sector, length to end of
                 * sector
                 * sector
                 */
                 */
                q = filp->f_pos >> 9;
                q = filp->f_pos >> 9;
                r = filp->f_pos & 511;
                r = filp->f_pos & 511;
                n = 512 - r;
                n = 512 - r;
 
 
                /*
                /*
                 * get length to copy to user buffer
                 * get length to copy to user buffer
                 */
                 */
                if (n > count)
                if (n > count)
                        n = count;
                        n = count;
 
 
                /*
                /*
                 * read the sector, copy to user
                 * read the sector, copy to user
                 */
                 */
                block = map_sector(inode->i_dev, hpfs_bmap(inode, q), &bh);
                block = map_sector(inode->i_dev, hpfs_bmap(inode, q), &bh);
                if (!block)
                if (!block)
                        return -EIO;
                        return -EIO;
 
 
                /*
                /*
                 * but first decide if it has \r\n, if the mount option said
                 * but first decide if it has \r\n, if the mount option said
                 * to do that
                 * to do that
                 */
                 */
                if (inode->i_hpfs_conv == CONV_AUTO)
                if (inode->i_hpfs_conv == CONV_AUTO)
                        inode->i_hpfs_conv = choose_conv(block + r, n);
                        inode->i_hpfs_conv = choose_conv(block + r, n);
 
 
                if (inode->i_hpfs_conv == CONV_BINARY) {
                if (inode->i_hpfs_conv == CONV_BINARY) {
                        /*
                        /*
                         * regular copy, output length is same as input
                         * regular copy, output length is same as input
                         * length
                         * length
                         */
                         */
                        memcpy_tofs(buf, block + r, n);
                        memcpy_tofs(buf, block + r, n);
                        n0 = n;
                        n0 = n;
                }
                }
                else {
                else {
                        /*
                        /*
                         * squeeze out \r, output length varies
                         * squeeze out \r, output length varies
                         */
                         */
                        n0 = convcpy_tofs(buf, block + r, n);
                        n0 = convcpy_tofs(buf, block + r, n);
                        if (count > inode->i_size - (off_t) filp->f_pos - n + n0)
                        if (count > inode->i_size - (off_t) filp->f_pos - n + n0)
                                count = inode->i_size - filp->f_pos - n + n0;
                                count = inode->i_size - filp->f_pos - n + n0;
                }
                }
 
 
                brelse(bh);
                brelse(bh);
 
 
                /*
                /*
                 * advance input n bytes, output n0 bytes
                 * advance input n bytes, output n0 bytes
                 */
                 */
                filp->f_pos += n;
                filp->f_pos += n;
                buf += n0;
                buf += n0;
                count -= n0;
                count -= n0;
        }
        }
 
 
        return buf - start;
        return buf - start;
}
}
 
 
/*
/*
 * This routine implements conv=auto.  Return CONV_BINARY or CONV_TEXT.
 * This routine implements conv=auto.  Return CONV_BINARY or CONV_TEXT.
 */
 */
 
 
static unsigned choose_conv(unsigned char *p, unsigned len)
static unsigned choose_conv(unsigned char *p, unsigned len)
{
{
        unsigned tvote, bvote;
        unsigned tvote, bvote;
        unsigned c;
        unsigned c;
 
 
        tvote = bvote = 0;
        tvote = bvote = 0;
 
 
        while (len--) {
        while (len--) {
                c = *p++;
                c = *p++;
                if (c < ' ')
                if (c < ' ')
                        if (c == '\r' && len && *p == '\n')
                        if (c == '\r' && len && *p == '\n')
                                tvote += 10;
                                tvote += 10;
                        else if (c == '\t' || c == '\n');
                        else if (c == '\t' || c == '\n');
                        else
                        else
                                bvote += 5;
                                bvote += 5;
                else if (c < '\177')
                else if (c < '\177')
                        tvote++;
                        tvote++;
                else
                else
                        bvote += 5;
                        bvote += 5;
        }
        }
 
 
        if (tvote > bvote)
        if (tvote > bvote)
                return CONV_TEXT;
                return CONV_TEXT;
        else
        else
                return CONV_BINARY;
                return CONV_BINARY;
}
}
 
 
/*
/*
 * This routine implements conv=text.  :s/crlf/nl/
 * This routine implements conv=text.  :s/crlf/nl/
 */
 */
 
 
static unsigned convcpy_tofs(unsigned char *out, unsigned char *in,
static unsigned convcpy_tofs(unsigned char *out, unsigned char *in,
                             unsigned len)
                             unsigned len)
{
{
        unsigned char *start = out;
        unsigned char *start = out;
 
 
        while (len--) {
        while (len--) {
                unsigned c = *in++;
                unsigned c = *in++;
                if (c == '\r' && (len == 0 || *in == '\n'));
                if (c == '\r' && (len == 0 || *in == '\n'));
                else
                else
                        put_user(c, out++);
                        put_user(c, out++);
        }
        }
 
 
        return out - start;
        return out - start;
}
}
 
 
/*
/*
 * Return the disk sector number containing a file sector.
 * Return the disk sector number containing a file sector.
 */
 */
 
 
static secno hpfs_bmap(struct inode *inode, unsigned file_secno)
static secno hpfs_bmap(struct inode *inode, unsigned file_secno)
{
{
        unsigned n, disk_secno;
        unsigned n, disk_secno;
        struct fnode *fnode;
        struct fnode *fnode;
        struct buffer_head *bh;
        struct buffer_head *bh;
 
 
        /*
        /*
         * There is one sector run cached in the inode. See if the sector is
         * There is one sector run cached in the inode. See if the sector is
         * in it.
         * in it.
         */
         */
 
 
        n = file_secno - inode->i_hpfs_file_sec;
        n = file_secno - inode->i_hpfs_file_sec;
        if (n < inode->i_hpfs_n_secs)
        if (n < inode->i_hpfs_n_secs)
                return inode->i_hpfs_disk_sec + n;
                return inode->i_hpfs_disk_sec + n;
 
 
        /*
        /*
         * No, read the fnode and go find the sector.
         * No, read the fnode and go find the sector.
         */
         */
 
 
        else {
        else {
                fnode = map_fnode(inode->i_dev, inode->i_ino, &bh);
                fnode = map_fnode(inode->i_dev, inode->i_ino, &bh);
                if (!fnode)
                if (!fnode)
                        return 0;
                        return 0;
                disk_secno = bplus_lookup(inode, &fnode->btree,
                disk_secno = bplus_lookup(inode, &fnode->btree,
                                          file_secno, &bh);
                                          file_secno, &bh);
                brelse(bh);
                brelse(bh);
                return disk_secno;
                return disk_secno;
        }
        }
}
}
 
 
/*
/*
 * Search allocation tree *b for the given file sector number and return
 * Search allocation tree *b for the given file sector number and return
 * the disk sector number.  Buffer *bhp has the tree in it, and can be
 * the disk sector number.  Buffer *bhp has the tree in it, and can be
 * reused for subtrees when access to *b is no longer needed.
 * reused for subtrees when access to *b is no longer needed.
 * *bhp is busy on entry and exit.
 * *bhp is busy on entry and exit.
 */
 */
 
 
static secno bplus_lookup(struct inode *inode, struct bplus_header *b,
static secno bplus_lookup(struct inode *inode, struct bplus_header *b,
                          secno file_secno, struct buffer_head **bhp)
                          secno file_secno, struct buffer_head **bhp)
{
{
        int i;
        int i;
 
 
        /*
        /*
         * A leaf-level tree gives a list of sector runs.  Find the one
         * A leaf-level tree gives a list of sector runs.  Find the one
         * containing the file sector we want, cache the map info in the
         * containing the file sector we want, cache the map info in the
         * inode for later, and return the corresponding disk sector.
         * inode for later, and return the corresponding disk sector.
         */
         */
 
 
        if (!b->internal) {
        if (!b->internal) {
                struct bplus_leaf_node *n = b->u.external;
                struct bplus_leaf_node *n = b->u.external;
                for (i = 0; i < b->n_used_nodes; i++) {
                for (i = 0; i < b->n_used_nodes; i++) {
                        unsigned t = file_secno - n[i].file_secno;
                        unsigned t = file_secno - n[i].file_secno;
                        if (t < n[i].length) {
                        if (t < n[i].length) {
                                inode->i_hpfs_file_sec = n[i].file_secno;
                                inode->i_hpfs_file_sec = n[i].file_secno;
                                inode->i_hpfs_disk_sec = n[i].disk_secno;
                                inode->i_hpfs_disk_sec = n[i].disk_secno;
                                inode->i_hpfs_n_secs = n[i].length;
                                inode->i_hpfs_n_secs = n[i].length;
                                return n[i].disk_secno + t;
                                return n[i].disk_secno + t;
                        }
                        }
                }
                }
        }
        }
 
 
        /*
        /*
         * A non-leaf tree gives a list of subtrees.  Find the one containing
         * A non-leaf tree gives a list of subtrees.  Find the one containing
         * the file sector we want, read it in, and recurse to search it.
         * the file sector we want, read it in, and recurse to search it.
         */
         */
 
 
        else {
        else {
                struct bplus_internal_node *n = b->u.internal;
                struct bplus_internal_node *n = b->u.internal;
                for (i = 0; i < b->n_used_nodes; i++) {
                for (i = 0; i < b->n_used_nodes; i++) {
                        if (file_secno < n[i].file_secno) {
                        if (file_secno < n[i].file_secno) {
                                struct anode *anode;
                                struct anode *anode;
                                anode_secno ano = n[i].down;
                                anode_secno ano = n[i].down;
                                brelse(*bhp);
                                brelse(*bhp);
                                anode = map_anode(inode->i_dev, ano, bhp);
                                anode = map_anode(inode->i_dev, ano, bhp);
                                if (!anode)
                                if (!anode)
                                        break;
                                        break;
                                return bplus_lookup(inode, &anode->btree,
                                return bplus_lookup(inode, &anode->btree,
                                                    file_secno, bhp);
                                                    file_secno, bhp);
                        }
                        }
                }
                }
        }
        }
 
 
        /*
        /*
         * If we get here there was a hole in the file.  As far as I know we
         * If we get here there was a hole in the file.  As far as I know we
         * never do get here, but falling off the end would be indelicate. So
         * never do get here, but falling off the end would be indelicate. So
         * return a pointer to a handy all-zero sector.  This is not a
         * return a pointer to a handy all-zero sector.  This is not a
         * reasonable way to handle files with holes if they really do
         * reasonable way to handle files with holes if they really do
         * happen.
         * happen.
         */
         */
 
 
        printk("HPFS: bplus_lookup: sector not found\n");
        printk("HPFS: bplus_lookup: sector not found\n");
        return 15;
        return 15;
}
}


/* directory ops */
/* directory ops */
 
 
/*
/*
 * lookup.  Search the specified directory for the specified name, set
 * lookup.  Search the specified directory for the specified name, set
 * *result to the corresponding inode.
 * *result to the corresponding inode.
 *
 *
 * lookup uses the inode number to tell read_inode whether it is reading
 * lookup uses the inode number to tell read_inode whether it is reading
 * the inode of a directory or a file -- file ino's are odd, directory
 * the inode of a directory or a file -- file ino's are odd, directory
 * ino's are even.  read_inode avoids i/o for file inodes; everything
 * ino's are even.  read_inode avoids i/o for file inodes; everything
 * needed is up here in the directory.  (And file fnodes are out in
 * needed is up here in the directory.  (And file fnodes are out in
 * the boondocks.)
 * the boondocks.)
 */
 */
 
 
static int hpfs_lookup(struct inode *dir, const char *name, int len,
static int hpfs_lookup(struct inode *dir, const char *name, int len,
                       struct inode **result)
                       struct inode **result)
{
{
        struct quad_buffer_head qbh;
        struct quad_buffer_head qbh;
        struct hpfs_dirent *de;
        struct hpfs_dirent *de;
        struct inode *inode;
        struct inode *inode;
        ino_t ino;
        ino_t ino;
 
 
        /* In case of madness */
        /* In case of madness */
 
 
        *result = 0;
        *result = 0;
        if (dir == 0)
        if (dir == 0)
                return -ENOENT;
                return -ENOENT;
        if (!S_ISDIR(dir->i_mode))
        if (!S_ISDIR(dir->i_mode))
                goto bail;
                goto bail;
 
 
        /*
        /*
         * Read in the directory entry. "." is there under the name ^A^A .
         * Read in the directory entry. "." is there under the name ^A^A .
         * Always read the dir even for . and .. in case we need the dates.
         * Always read the dir even for . and .. in case we need the dates.
         */
         */
 
 
        if (name[0] == '.' && len == 1)
        if (name[0] == '.' && len == 1)
                de = map_dirent(dir, dir->i_hpfs_dno, "\001\001", 2, &qbh);
                de = map_dirent(dir, dir->i_hpfs_dno, "\001\001", 2, &qbh);
        else if (name[0] == '.' && name[1] == '.' && len == 2)
        else if (name[0] == '.' && name[1] == '.' && len == 2)
                de = map_dirent(dir,
                de = map_dirent(dir,
                                fnode_dno(dir->i_dev, dir->i_hpfs_parent_dir),
                                fnode_dno(dir->i_dev, dir->i_hpfs_parent_dir),
                                "\001\001", 2, &qbh);
                                "\001\001", 2, &qbh);
        else
        else
                de = map_dirent(dir, dir->i_hpfs_dno, name, len, &qbh);
                de = map_dirent(dir, dir->i_hpfs_dno, name, len, &qbh);
 
 
        /*
        /*
         * This is not really a bailout, just means file not found.
         * This is not really a bailout, just means file not found.
         */
         */
 
 
        if (!de)
        if (!de)
                goto bail;
                goto bail;
 
 
        /*
        /*
         * Get inode number, what we're after.
         * Get inode number, what we're after.
         */
         */
 
 
        if (de->directory)
        if (de->directory)
                ino = dir_ino(de->fnode);
                ino = dir_ino(de->fnode);
        else
        else
                ino = file_ino(de->fnode);
                ino = file_ino(de->fnode);
 
 
        /*
        /*
         * Go find or make an inode.
         * Go find or make an inode.
         */
         */
 
 
        if (!(inode = iget(dir->i_sb, ino)))
        if (!(inode = iget(dir->i_sb, ino)))
                goto bail1;
                goto bail1;
 
 
        /*
        /*
         * Fill in the info from the directory if this is a newly created
         * Fill in the info from the directory if this is a newly created
         * inode.
         * inode.
         */
         */
 
 
        if (!inode->i_atime) {
        if (!inode->i_atime) {
                inode->i_atime = local_to_gmt(de->read_date);
                inode->i_atime = local_to_gmt(de->read_date);
                inode->i_mtime = local_to_gmt(de->write_date);
                inode->i_mtime = local_to_gmt(de->write_date);
                inode->i_ctime = local_to_gmt(de->creation_date);
                inode->i_ctime = local_to_gmt(de->creation_date);
                if (de->read_only)
                if (de->read_only)
                        inode->i_mode &= ~0222;
                        inode->i_mode &= ~0222;
                if (!de->directory) {
                if (!de->directory) {
                        inode->i_size = de->file_size;
                        inode->i_size = de->file_size;
                        /*
                        /*
                         * i_blocks should count the fnode and any anodes.
                         * i_blocks should count the fnode and any anodes.
                         * We count 1 for the fnode and don't bother about
                         * We count 1 for the fnode and don't bother about
                         * anodes -- the disk heads are on the directory band
                         * anodes -- the disk heads are on the directory band
                         * and we want them to stay there.
                         * and we want them to stay there.
                         */
                         */
                        inode->i_blocks = 1 + ((inode->i_size + 511) >> 9);
                        inode->i_blocks = 1 + ((inode->i_size + 511) >> 9);
                }
                }
        }
        }
 
 
        brelse4(&qbh);
        brelse4(&qbh);
 
 
        /*
        /*
         * Made it.
         * Made it.
         */
         */
 
 
        *result = inode;
        *result = inode;
        iput(dir);
        iput(dir);
        return 0;
        return 0;
 
 
        /*
        /*
         * Didn't.
         * Didn't.
         */
         */
 bail1:
 bail1:
        brelse4(&qbh);
        brelse4(&qbh);
 bail:
 bail:
        iput(dir);
        iput(dir);
        return -ENOENT;
        return -ENOENT;
}
}
 
 
/*
/*
 * Compare two counted strings ignoring case.
 * Compare two counted strings ignoring case.
 * HPFS directory order sorts letters as if they're upper case.
 * HPFS directory order sorts letters as if they're upper case.
 */
 */
 
 
static inline int memcasecmp(const unsigned char *s1, const unsigned char *s2,
static inline int memcasecmp(const unsigned char *s1, const unsigned char *s2,
                             unsigned n)
                             unsigned n)
{
{
        int t;
        int t;
 
 
        if (n != 0)
        if (n != 0)
                do {
                do {
                        unsigned c1 = linux_char_to_upper_linux (*s1++);
                        unsigned c1 = linux_char_to_upper_linux (*s1++);
                        unsigned c2 = hpfs_char_to_upper_linux (*s2++);
                        unsigned c2 = hpfs_char_to_upper_linux (*s2++);
                        if ((t = c1 - c2) != 0)
                        if ((t = c1 - c2) != 0)
                                return t;
                                return t;
                } while (--n != 0);
                } while (--n != 0);
 
 
        return 0;
        return 0;
}
}
 
 
/*
/*
 * Search a directory for the given name, return a pointer to its dir entry
 * Search a directory for the given name, return a pointer to its dir entry
 * and a pointer to the buffer containing it.
 * and a pointer to the buffer containing it.
 */
 */
 
 
static struct hpfs_dirent *map_dirent(struct inode *inode, dnode_secno dno,
static struct hpfs_dirent *map_dirent(struct inode *inode, dnode_secno dno,
                                      const unsigned char *name, unsigned len,
                                      const unsigned char *name, unsigned len,
                                      struct quad_buffer_head *qbh)
                                      struct quad_buffer_head *qbh)
{
{
        struct dnode *dnode;
        struct dnode *dnode;
        struct hpfs_dirent *de;
        struct hpfs_dirent *de;
        struct hpfs_dirent *de_end;
        struct hpfs_dirent *de_end;
        int t, l;
        int t, l;
 
 
        /*
        /*
         * read the dnode at the root of our subtree
         * read the dnode at the root of our subtree
         */
         */
        dnode = map_dnode(inode->i_dev, dno, qbh);
        dnode = map_dnode(inode->i_dev, dno, qbh);
        if (!dnode)
        if (!dnode)
                return 0;
                return 0;
 
 
        /*
        /*
         * get pointers to start and end+1 of dir entries
         * get pointers to start and end+1 of dir entries
         */
         */
        de = dnode_first_de(dnode);
        de = dnode_first_de(dnode);
        de_end = dnode_end_de(dnode);
        de_end = dnode_end_de(dnode);
 
 
        /*
        /*
         * look through the entries for the name we're after
         * look through the entries for the name we're after
         */
         */
        for ( ; de < de_end; de = de_next_de(de)) {
        for ( ; de < de_end; de = de_next_de(de)) {
 
 
                /*
                /*
                 * compare names
                 * compare names
                 */
                 */
                l = len < de->namelen ? len : de->namelen;
                l = len < de->namelen ? len : de->namelen;
                t = memcasecmp(name, de->name, l);
                t = memcasecmp(name, de->name, l);
 
 
                /*
                /*
                 * initial substring matches, compare lengths
                 * initial substring matches, compare lengths
                 */
                 */
                if (t == 0) {
                if (t == 0) {
                        t = len - de->namelen;
                        t = len - de->namelen;
                        /* bingo */
                        /* bingo */
                        if (t == 0)
                        if (t == 0)
                                return de;
                                return de;
                }
                }
 
 
                /*
                /*
                 * wanted name .lt. dir name => not present.
                 * wanted name .lt. dir name => not present.
                 */
                 */
                if (t < 0) {
                if (t < 0) {
                        /*
                        /*
                         * if there is a subtree, search it.
                         * if there is a subtree, search it.
                         */
                         */
                        if (de->down) {
                        if (de->down) {
                                dnode_secno sub_dno = de_down_pointer(de);
                                dnode_secno sub_dno = de_down_pointer(de);
                                brelse4(qbh);
                                brelse4(qbh);
                                return map_dirent(inode, sub_dno,
                                return map_dirent(inode, sub_dno,
                                                  name, len, qbh);
                                                  name, len, qbh);
                        }
                        }
                        else
                        else
                                break;
                                break;
                }
                }
 
 
                /*
                /*
                 * de->last is set on the last name in the dnode (it's always
                 * 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
                 * a "\377" pseudo entry).  de->length == 0 means we're about
                 * to infinite loop. This test does nothing in a well-formed
                 * to infinite loop. This test does nothing in a well-formed
                 * dnode.
                 * dnode.
                 */
                 */
                if (de->last || de->length == 0)
                if (de->last || de->length == 0)
                        break;
                        break;
        }
        }
 
 
        /*
        /*
         * name not found.
         * name not found.
         */
         */
        brelse4(qbh);
        brelse4(qbh);
        return 0;
        return 0;
}
}
 
 
/*
/*
 * readdir.  Return exactly 1 dirent.  (I tried and tried, but currently
 * 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
 * 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
 * fixed, throw this out and just walk the tree and write records into
 * the user buffer.)
 * the user buffer.)
 *
 *
 * [ we now can handle multiple dirents, although the current libc doesn't
 * [ we now can handle multiple dirents, although the current libc doesn't
 *   use that. The way hpfs does this is pretty strange, as we need to do
 *   use that. The way hpfs does this is pretty strange, as we need to do
 *   the name translation etc before calling "filldir()". This is untested,
 *   the name translation etc before calling "filldir()". This is untested,
 *   as I don't have any hpfs partitions to test against.   Linus ]
 *   as I don't have any hpfs partitions to test against.   Linus ]
 *
 *
 * We keep track of our position in the dnode tree with a sort of
 * We keep track of our position in the dnode tree with a sort of
 * dewey-decimal record of subtree locations.  Like so:
 * 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)
 *   (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,
 * Subtrees appear after their file, out of lexical order,
 * which would be before their file.  It's easier.
 * which would be before their file.  It's easier.
 *
 *
 * A directory can't hold more than 56 files, so 6 bits are used for
 * 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
 * position numbers.  If the tree is so deep that the position encoding
 * doesn't fit, I'm sure something absolutely fascinating happens.
 * doesn't fit, I'm sure something absolutely fascinating happens.
 *
 *
 * The actual sequence of f_pos values is
 * The actual sequence of f_pos values is
 *     0 => .   -1 => ..   1 1.1 ... 8.9 9 => files  -2 => eof
 *     0 => .   -1 => ..   1 1.1 ... 8.9 9 => files  -2 => eof
 *
 *
 * The directory inode caches one position-to-dnode correspondence so
 * The directory inode caches one position-to-dnode correspondence so
 * we won't have to repeatedly scan the top levels of the tree.
 * we won't have to repeatedly scan the top levels of the tree.
 */
 */
 
 
/*
/*
 * Translate the given name: Blam it to lowercase if the mount option said to.
 * Translate the given name: Blam it to lowercase if the mount option said to.
 */
 */
 
 
static void translate_hpfs_name(const unsigned char * from, int len, char * to, int lowercase)
static void translate_hpfs_name(const unsigned char * from, int len, char * to, int lowercase)
{
{
        while (len > 0) {
        while (len > 0) {
                unsigned t = *from;
                unsigned t = *from;
                len--;
                len--;
                if (lowercase)
                if (lowercase)
                        t = hpfs_char_to_lower_linux (t);
                        t = hpfs_char_to_lower_linux (t);
                else
                else
                        t = hpfs_char_to_linux (t);
                        t = hpfs_char_to_linux (t);
                *to = t;
                *to = t;
                from++;
                from++;
                to++;
                to++;
        }
        }
}
}
 
 
static int hpfs_readdir(struct inode *inode, struct file *filp, void * dirent,
static int hpfs_readdir(struct inode *inode, struct file *filp, void * dirent,
        filldir_t filldir)
        filldir_t filldir)
{
{
        struct quad_buffer_head qbh;
        struct quad_buffer_head qbh;
        struct hpfs_dirent *de;
        struct hpfs_dirent *de;
        int namelen, lc;
        int namelen, lc;
        ino_t ino;
        ino_t ino;
        char * tempname;
        char * tempname;
        long old_pos;
        long old_pos;
 
 
        if (inode == 0
        if (inode == 0
            || inode->i_sb == 0
            || inode->i_sb == 0
            || !S_ISDIR(inode->i_mode))
            || !S_ISDIR(inode->i_mode))
                return -EBADF;
                return -EBADF;
 
 
        tempname = (char *) __get_free_page(GFP_KERNEL);
        tempname = (char *) __get_free_page(GFP_KERNEL);
        if (!tempname)
        if (!tempname)
                return -ENOMEM;
                return -ENOMEM;
 
 
        lc = inode->i_sb->s_hpfs_lowercase;
        lc = inode->i_sb->s_hpfs_lowercase;
        switch ((long) filp->f_pos) {
        switch ((long) filp->f_pos) {
        case -2:
        case -2:
                break;
                break;
 
 
        case 0:
        case 0:
                if (filldir(dirent, ".", 1, filp->f_pos, inode->i_ino) < 0)
                if (filldir(dirent, ".", 1, filp->f_pos, inode->i_ino) < 0)
                        break;
                        break;
                filp->f_pos = -1;
                filp->f_pos = -1;
                /* fall through */
                /* fall through */
 
 
        case -1:
        case -1:
                if (filldir(dirent, "..", 2, filp->f_pos, inode->i_hpfs_parent_dir) < 0)
                if (filldir(dirent, "..", 2, filp->f_pos, inode->i_hpfs_parent_dir) < 0)
                        break;
                        break;
                filp->f_pos = 1;
                filp->f_pos = 1;
                /* fall through */
                /* fall through */
 
 
        default:
        default:
                for (;;) {
                for (;;) {
                        old_pos = filp->f_pos;
                        old_pos = filp->f_pos;
                        de = map_pos_dirent(inode, &filp->f_pos, &qbh);
                        de = map_pos_dirent(inode, &filp->f_pos, &qbh);
                        if (!de) {
                        if (!de) {
                                filp->f_pos = -2;
                                filp->f_pos = -2;
                                break;
                                break;
                        }
                        }
                        namelen = de->namelen;
                        namelen = de->namelen;
                        translate_hpfs_name(de->name, namelen, tempname, lc);
                        translate_hpfs_name(de->name, namelen, tempname, lc);
                        if (de->directory)
                        if (de->directory)
                                ino = dir_ino(de->fnode);
                                ino = dir_ino(de->fnode);
                        else
                        else
                                ino = file_ino(de->fnode);
                                ino = file_ino(de->fnode);
                        brelse4(&qbh);
                        brelse4(&qbh);
                        if (filldir(dirent, tempname, namelen, old_pos, ino) < 0) {
                        if (filldir(dirent, tempname, namelen, old_pos, ino) < 0) {
                                filp->f_pos = old_pos;
                                filp->f_pos = old_pos;
                                break;
                                break;
                        }
                        }
                }
                }
        }
        }
        free_page((unsigned long) tempname);
        free_page((unsigned long) tempname);
        return 0;
        return 0;
}
}
 
 
/*
/*
 * Map the dir entry at subtree coordinates given by *posp, and
 * Map the dir entry at subtree coordinates given by *posp, and
 * increment *posp to point to the following dir entry.
 * increment *posp to point to the following dir entry.
 */
 */
 
 
static struct hpfs_dirent *map_pos_dirent(struct inode *inode, loff_t *posp,
static struct hpfs_dirent *map_pos_dirent(struct inode *inode, loff_t *posp,
                                          struct quad_buffer_head *qbh)
                                          struct quad_buffer_head *qbh)
{
{
        unsigned pos, q, r;
        unsigned pos, q, r;
        dnode_secno dno;
        dnode_secno dno;
        struct hpfs_dirent *de;
        struct hpfs_dirent *de;
 
 
        /*
        /*
         * Get the position code and split off the rightmost index r
         * Get the position code and split off the rightmost index r
         */
         */
 
 
        pos = *posp;
        pos = *posp;
        q = pos >> 6;
        q = pos >> 6;
        r = pos & 077;
        r = pos & 077;
 
 
        /*
        /*
         * Get the sector address of the dnode
         * Get the sector address of the dnode
         * pointed to by the leading part q
         * pointed to by the leading part q
         */
         */
 
 
        dno = dir_subdno(inode, q);
        dno = dir_subdno(inode, q);
        if (!dno)
        if (!dno)
                return 0;
                return 0;
 
 
        /*
        /*
         * Get the entry at index r in dnode q
         * Get the entry at index r in dnode q
         */
         */
 
 
        de = map_nth_dirent(inode->i_dev, dno, r, qbh);
        de = map_nth_dirent(inode->i_dev, dno, r, qbh);
 
 
        /*
        /*
         * If none, we're out of files in this dnode.  Ascend.
         * If none, we're out of files in this dnode.  Ascend.
         */
         */
 
 
        if (!de) {
        if (!de) {
                if (q == 0)
                if (q == 0)
                        return 0;
                        return 0;
                *posp = q + 1;
                *posp = q + 1;
                return map_pos_dirent(inode, posp, qbh);
                return map_pos_dirent(inode, posp, qbh);
        }
        }
 
 
        /*
        /*
         * If a subtree is here, descend.
         * If a subtree is here, descend.
         */
         */
 
 
        if (de->down)
        if (de->down)
                *posp = pos << 6 | 1;
                *posp = pos << 6 | 1;
        else
        else
                *posp = pos + 1;
                *posp = pos + 1;
 
 
        /*
        /*
         * Don't return the ^A^A and \377 entries.
         * Don't return the ^A^A and \377 entries.
         */
         */
 
 
        if (de->first || de->last) {
        if (de->first || de->last) {
                brelse4(qbh);
                brelse4(qbh);
                return map_pos_dirent(inode, posp, qbh);
                return map_pos_dirent(inode, posp, qbh);
        }
        }
        else
        else
                return de;
                return de;
}
}
 
 
/*
/*
 * Return the address of the dnode with subtree coordinates given by pos.
 * Return the address of the dnode with subtree coordinates given by pos.
 */
 */
 
 
static dnode_secno dir_subdno(struct inode *inode, unsigned pos)
static dnode_secno dir_subdno(struct inode *inode, unsigned pos)
{
{
        struct hpfs_dirent *de;
        struct hpfs_dirent *de;
        struct quad_buffer_head qbh;
        struct quad_buffer_head qbh;
 
 
        /*
        /*
         * 0 is the root dnode
         * 0 is the root dnode
         */
         */
 
 
        if (pos == 0)
        if (pos == 0)
                return inode->i_hpfs_dno;
                return inode->i_hpfs_dno;
 
 
        /*
        /*
         * we have one pos->dnode translation cached in the inode
         * we have one pos->dnode translation cached in the inode
         */
         */
 
 
        else if (pos == inode->i_hpfs_dpos)
        else if (pos == inode->i_hpfs_dpos)
                return inode->i_hpfs_dsubdno;
                return inode->i_hpfs_dsubdno;
 
 
        /*
        /*
         * otherwise go look
         * otherwise go look
         */
         */
 
 
        else {
        else {
                unsigned q = pos >> 6;
                unsigned q = pos >> 6;
                unsigned r = pos & 077;
                unsigned r = pos & 077;
                dnode_secno dno;
                dnode_secno dno;
 
 
                /*
                /*
                 * dnode at position q
                 * dnode at position q
                 */
                 */
                dno = dir_subdno(inode, q);
                dno = dir_subdno(inode, q);
                if (dno == 0)
                if (dno == 0)
                        return 0;
                        return 0;
 
 
                /*
                /*
                 * entry at index r
                 * entry at index r
                 */
                 */
                de = map_nth_dirent(inode->i_dev, dno, r, &qbh);
                de = map_nth_dirent(inode->i_dev, dno, r, &qbh);
                if (!de || !de->down)
                if (!de || !de->down)
                        return 0;
                        return 0;
 
 
                /*
                /*
                 * get the dnode down pointer
                 * get the dnode down pointer
                 */
                 */
                dno = de_down_pointer(de);
                dno = de_down_pointer(de);
                brelse4(&qbh);
                brelse4(&qbh);
 
 
                /*
                /*
                 * cache it for next time
                 * cache it for next time
                 */
                 */
                inode->i_hpfs_dpos = pos;
                inode->i_hpfs_dpos = pos;
                inode->i_hpfs_dsubdno = dno;
                inode->i_hpfs_dsubdno = dno;
                return dno;
                return dno;
        }
        }
}
}
 
 
/*
/*
 * Return the dir entry at index n in dnode dno, or 0 if there isn't one
 * Return the dir entry at index n in dnode dno, or 0 if there isn't one
 */
 */
 
 
static struct hpfs_dirent *map_nth_dirent(kdev_t dev, dnode_secno dno,
static struct hpfs_dirent *map_nth_dirent(kdev_t dev, dnode_secno dno,
                                          int n,
                                          int n,
                                          struct quad_buffer_head *qbh)
                                          struct quad_buffer_head *qbh)
{
{
        int i;
        int i;
        struct hpfs_dirent *de, *de_end;
        struct hpfs_dirent *de, *de_end;
        struct dnode *dnode = map_dnode(dev, dno, qbh);
        struct dnode *dnode = map_dnode(dev, dno, qbh);
 
 
        de = dnode_first_de(dnode);
        de = dnode_first_de(dnode);
        de_end = dnode_end_de(dnode);
        de_end = dnode_end_de(dnode);
 
 
        for (i = 1; de < de_end; i++, de = de_next_de(de)) {
        for (i = 1; de < de_end; i++, de = de_next_de(de)) {
                if (i == n)
                if (i == n)
                        return de;
                        return de;
                if (de->last || de->length == 0)
                if (de->last || de->length == 0)
                        break;
                        break;
        }
        }
 
 
        brelse4(qbh);
        brelse4(qbh);
        return 0;
        return 0;
}
}
 
 
static int hpfs_dir_read(struct inode *inode, struct file *filp,
static int hpfs_dir_read(struct inode *inode, struct file *filp,
                         char *buf, int count)
                         char *buf, int count)
{
{
        return -EISDIR;
        return -EISDIR;
}
}


/* Return the dnode pointer in a directory fnode */
/* Return the dnode pointer in a directory fnode */
 
 
static dnode_secno fnode_dno(kdev_t dev, ino_t ino)
static dnode_secno fnode_dno(kdev_t dev, ino_t ino)
{
{
        struct buffer_head *bh;
        struct buffer_head *bh;
        struct fnode *fnode;
        struct fnode *fnode;
        dnode_secno dno;
        dnode_secno dno;
 
 
        fnode = map_fnode(dev, ino, &bh);
        fnode = map_fnode(dev, ino, &bh);
        if (!fnode)
        if (!fnode)
                return 0;
                return 0;
 
 
        dno = fnode->u.external[0].disk_secno;
        dno = fnode->u.external[0].disk_secno;
        brelse(bh);
        brelse(bh);
        return dno;
        return dno;
}
}
 
 
/* Map an fnode into a buffer and return pointers to it and to the buffer. */
/* Map an fnode into a buffer and return pointers to it and to the buffer. */
 
 
static struct fnode *map_fnode(kdev_t dev, ino_t ino, struct buffer_head **bhp)
static struct fnode *map_fnode(kdev_t dev, ino_t ino, struct buffer_head **bhp)
{
{
        struct fnode *fnode;
        struct fnode *fnode;
 
 
        if (ino == 0) {
        if (ino == 0) {
                printk("HPFS: missing fnode\n");
                printk("HPFS: missing fnode\n");
                return 0;
                return 0;
        }
        }
 
 
        fnode = map_sector(dev, ino_secno(ino), bhp);
        fnode = map_sector(dev, ino_secno(ino), bhp);
        if (fnode)
        if (fnode)
                if (fnode->magic != FNODE_MAGIC) {
                if (fnode->magic != FNODE_MAGIC) {
                        printk("HPFS: map_fnode: bad fnode pointer\n");
                        printk("HPFS: map_fnode: bad fnode pointer\n");
                        brelse(*bhp);
                        brelse(*bhp);
                        return 0;
                        return 0;
                }
                }
        return fnode;
        return fnode;
}
}
 
 
/* Map an anode into a buffer and return pointers to it and to the buffer. */
/* Map an anode into a buffer and return pointers to it and to the buffer. */
 
 
static struct anode *map_anode(kdev_t dev, unsigned secno,
static struct anode *map_anode(kdev_t dev, unsigned secno,
                               struct buffer_head **bhp)
                               struct buffer_head **bhp)
{
{
        struct anode *anode;
        struct anode *anode;
 
 
        if (secno == 0) {
        if (secno == 0) {
                printk("HPFS: missing anode\n");
                printk("HPFS: missing anode\n");
                return 0;
                return 0;
        }
        }
 
 
        anode = map_sector(dev, secno, bhp);
        anode = map_sector(dev, secno, bhp);
        if (anode)
        if (anode)
                if (anode->magic != ANODE_MAGIC || anode->self != secno) {
                if (anode->magic != ANODE_MAGIC || anode->self != secno) {
                        printk("HPFS: map_anode: bad anode pointer\n");
                        printk("HPFS: map_anode: bad anode pointer\n");
                        brelse(*bhp);
                        brelse(*bhp);
                        return 0;
                        return 0;
                }
                }
        return anode;
        return anode;
}
}
 
 
/* Map a dnode into a buffer and return pointers to it and to the buffer. */
/* Map a dnode into a buffer and return pointers to it and to the buffer. */
 
 
static struct dnode *map_dnode(kdev_t dev, unsigned secno,
static struct dnode *map_dnode(kdev_t dev, unsigned secno,
                               struct quad_buffer_head *qbh)
                               struct quad_buffer_head *qbh)
{
{
        struct dnode *dnode;
        struct dnode *dnode;
 
 
        if (secno == 0) {
        if (secno == 0) {
                printk("HPFS: missing dnode\n");
                printk("HPFS: missing dnode\n");
                return 0;
                return 0;
        }
        }
 
 
        dnode = map_4sectors(dev, secno, qbh);
        dnode = map_4sectors(dev, secno, qbh);
        if (dnode)
        if (dnode)
                if (dnode->magic != DNODE_MAGIC || dnode->self != secno) {
                if (dnode->magic != DNODE_MAGIC || dnode->self != secno) {
                        printk("HPFS: map_dnode: bad dnode pointer\n");
                        printk("HPFS: map_dnode: bad dnode pointer\n");
                        brelse4(qbh);
                        brelse4(qbh);
                        return 0;
                        return 0;
                }
                }
        return dnode;
        return dnode;
}
}
 
 
/* Map a sector into a buffer and return pointers to it and to the buffer. */
/* Map a sector into a buffer and return pointers to it and to the buffer. */
 
 
static void *map_sector(kdev_t dev, unsigned secno, struct buffer_head **bhp)
static void *map_sector(kdev_t dev, unsigned secno, struct buffer_head **bhp)
{
{
        struct buffer_head *bh;
        struct buffer_head *bh;
 
 
        if ((*bhp = bh = bread(dev, secno, 512)) != 0)
        if ((*bhp = bh = bread(dev, secno, 512)) != 0)
                return bh->b_data;
                return bh->b_data;
        else {
        else {
                printk("HPFS: map_sector: read error\n");
                printk("HPFS: map_sector: read error\n");
                return 0;
                return 0;
        }
        }
}
}
 
 
/* Map 4 sectors into a 4buffer and return pointers to it and to the buffer. */
/* Map 4 sectors into a 4buffer and return pointers to it and to the buffer. */
 
 
static void *map_4sectors(kdev_t dev, unsigned secno,
static void *map_4sectors(kdev_t dev, unsigned secno,
                          struct quad_buffer_head *qbh)
                          struct quad_buffer_head *qbh)
{
{
        struct buffer_head *bh;
        struct buffer_head *bh;
        char *data;
        char *data;
 
 
        if (secno & 3) {
        if (secno & 3) {
                printk("HPFS: map_4sectors: unaligned read\n");
                printk("HPFS: map_4sectors: unaligned read\n");
                return 0;
                return 0;
        }
        }
 
 
        qbh->data = data = kmalloc(2048, GFP_KERNEL);
        qbh->data = data = kmalloc(2048, GFP_KERNEL);
        if (!data)
        if (!data)
                goto bail;
                goto bail;
 
 
        qbh->bh[0] = bh = breada(dev, secno, 512, 0, UINT_MAX);
        qbh->bh[0] = bh = breada(dev, secno, 512, 0, UINT_MAX);
        if (!bh)
        if (!bh)
                goto bail0;
                goto bail0;
        memcpy(data, bh->b_data, 512);
        memcpy(data, bh->b_data, 512);
 
 
        qbh->bh[1] = bh = bread(dev, secno + 1, 512);
        qbh->bh[1] = bh = bread(dev, secno + 1, 512);
        if (!bh)
        if (!bh)
                goto bail1;
                goto bail1;
        memcpy(data + 512, bh->b_data, 512);
        memcpy(data + 512, bh->b_data, 512);
 
 
        qbh->bh[2] = bh = bread(dev, secno + 2, 512);
        qbh->bh[2] = bh = bread(dev, secno + 2, 512);
        if (!bh)
        if (!bh)
                goto bail2;
                goto bail2;
        memcpy(data + 2 * 512, bh->b_data, 512);
        memcpy(data + 2 * 512, bh->b_data, 512);
 
 
        qbh->bh[3] = bh = bread(dev, secno + 3, 512);
        qbh->bh[3] = bh = bread(dev, secno + 3, 512);
        if (!bh)
        if (!bh)
                goto bail3;
                goto bail3;
        memcpy(data + 3 * 512, bh->b_data, 512);
        memcpy(data + 3 * 512, bh->b_data, 512);
 
 
        return data;
        return data;
 
 
 bail3:
 bail3:
        brelse(qbh->bh[2]);
        brelse(qbh->bh[2]);
 bail2:
 bail2:
        brelse(qbh->bh[1]);
        brelse(qbh->bh[1]);
 bail1:
 bail1:
        brelse(qbh->bh[0]);
        brelse(qbh->bh[0]);
 bail0:
 bail0:
        kfree_s(data, 2048);
        kfree_s(data, 2048);
 bail:
 bail:
        printk("HPFS: map_4sectors: read error\n");
        printk("HPFS: map_4sectors: read error\n");
        return 0;
        return 0;
}
}
 
 
/* Deallocate a 4-buffer block */
/* Deallocate a 4-buffer block */
 
 
static void brelse4(struct quad_buffer_head *qbh)
static void brelse4(struct quad_buffer_head *qbh)
{
{
        brelse(qbh->bh[3]);
        brelse(qbh->bh[3]);
        brelse(qbh->bh[2]);
        brelse(qbh->bh[2]);
        brelse(qbh->bh[1]);
        brelse(qbh->bh[1]);
        brelse(qbh->bh[0]);
        brelse(qbh->bh[0]);
        kfree_s(qbh->data, 2048);
        kfree_s(qbh->data, 2048);
}
}
 
 
static struct file_system_type hpfs_fs_type = {
static struct file_system_type hpfs_fs_type = {
        hpfs_read_super, "hpfs", 1, NULL
        hpfs_read_super, "hpfs", 1, NULL
};
};
 
 
int init_hpfs_fs(void)
int init_hpfs_fs(void)
{
{
        return register_filesystem(&hpfs_fs_type);
        return register_filesystem(&hpfs_fs_type);
}
}
 
 
#ifdef MODULE
#ifdef MODULE
int init_module(void)
int init_module(void)
{
{
        int status;
        int status;
 
 
        if ((status = init_hpfs_fs()) == 0)
        if ((status = init_hpfs_fs()) == 0)
                register_symtab(0);
                register_symtab(0);
        return status;
        return status;
}
}
 
 
void cleanup_module(void)
void cleanup_module(void)
{
{
        unregister_filesystem(&hpfs_fs_type);
        unregister_filesystem(&hpfs_fs_type);
}
}
 
 
#endif
#endif
 
 
 
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.