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