176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <dprintf.h>
276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <stdio.h>
376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <ctype.h>
476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <string.h>
576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <sys/dirent.h>
676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <cache.h>
776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <core.h>
876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <disk.h>
976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <fs.h>
1076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <ilog2.h>
1176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <klibc/compiler.h>
1276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include "codepage.h"
1376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include "fat_fs.h"
1476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
1576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic struct inode * new_fat_inode(struct fs_info *fs)
1676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
1776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    struct inode *inode = alloc_inode(fs, 0, sizeof(struct fat_pvt_inode));
1876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    if (!inode)
1976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	malloc_error("inode structure");
2076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
2176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    return inode;
2276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
2376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
2476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*
2576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Check for a particular sector in the FAT cache
2676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
2776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic const void *get_fat_sector(struct fs_info *fs, sector_t sector)
2876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
2976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    return get_cache(fs->fs_dev, FAT_SB(fs)->fat + sector);
3076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
3176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
3276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic uint32_t get_next_cluster(struct fs_info *fs, uint32_t clust_num)
3376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
3476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    uint32_t next_cluster = 0;
3576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    sector_t fat_sector;
3676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    uint32_t offset;
3776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    uint32_t sector_mask = SECTOR_SIZE(fs) - 1;
3876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    const uint8_t *data;
3976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
4076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    switch(FAT_SB(fs)->fat_type) {
4176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    case FAT12:
4276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	offset = clust_num + (clust_num >> 1);
4376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	fat_sector = offset >> SECTOR_SHIFT(fs);
4476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	offset &= sector_mask;
4576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	data = get_fat_sector(fs, fat_sector);
4676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if (offset == sector_mask) {
4776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    /*
4876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	     * we got the end of the one fat sector,
4976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	     * but we have just one byte and we need two,
5076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	     * so store the low part, then read the next fat
5176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	     * sector, read the high part, then combine it.
5276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	     */
5376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    next_cluster = data[offset];
5476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    data = get_fat_sector(fs, fat_sector + 1);
5576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    next_cluster += data[0] << 8;
5676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	} else {
5776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    next_cluster = *(const uint16_t *)(data + offset);
5876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
5976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
6076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if (clust_num & 0x0001)
6176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    next_cluster >>= 4;         /* cluster number is ODD */
6276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	else
6376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    next_cluster &= 0x0fff;     /* cluster number is EVEN */
6476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	break;
6576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
6676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    case FAT16:
6776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	offset = clust_num << 1;
6876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	fat_sector = offset >> SECTOR_SHIFT(fs);
6976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	offset &= sector_mask;
7076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	data = get_fat_sector(fs, fat_sector);
7176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	next_cluster = *(const uint16_t *)(data + offset);
7276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	break;
7376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
7476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    case FAT32:
7576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	offset = clust_num << 2;
7676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	fat_sector = offset >> SECTOR_SHIFT(fs);
7776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	offset &= sector_mask;
7876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	data = get_fat_sector(fs, fat_sector);
7976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	next_cluster = *(const uint32_t *)(data + offset);
8076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	next_cluster &= 0x0fffffff;
8176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	break;
8276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    }
8376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
8476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    return next_cluster;
8576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
8676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
8776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int fat_next_extent(struct inode *inode, uint32_t lstart)
8876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
8976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    struct fs_info *fs = inode->fs;
9076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    struct fat_sb_info *sbi = FAT_SB(fs);
9176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    uint32_t mcluster = lstart >> sbi->clust_shift;
9276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    uint32_t lcluster;
9376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    uint32_t pcluster;
9476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    uint32_t tcluster;
9576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    uint32_t xcluster;
9676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    const uint32_t cluster_bytes = UINT32_C(1) << sbi->clust_byte_shift;
9776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    const uint32_t cluster_secs  = UINT32_C(1) << sbi->clust_shift;
9876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    sector_t data_area = sbi->data;
9976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
10076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    tcluster = (inode->size + cluster_bytes - 1) >> sbi->clust_byte_shift;
10176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    if (mcluster >= tcluster)
10276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	goto err;		/* Requested cluster beyond end of file */
10376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
10476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    lcluster = PVT(inode)->offset >> sbi->clust_shift;
10576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    pcluster = ((PVT(inode)->here - data_area) >> sbi->clust_shift) + 2;
10676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
10776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    if (lcluster > mcluster || PVT(inode)->here < data_area) {
10876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	lcluster = 0;
10976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	pcluster = PVT(inode)->start_cluster;
11076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    }
11176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
11276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    for (;;) {
11376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if (pcluster-2 >= sbi->clusters) {
11476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    inode->size = lcluster << sbi->clust_shift;
11576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    goto err;
11676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
11776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
11876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if (lcluster >= mcluster)
11976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    break;
12076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
12176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	lcluster++;
12276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	pcluster = get_next_cluster(fs, pcluster);
12376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    }
12476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
12576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    inode->next_extent.pstart =
12676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	((sector_t)(pcluster-2) << sbi->clust_shift) + data_area;
12776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    inode->next_extent.len = cluster_secs;
12876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    xcluster = 0;		/* Nonsense */
12976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
13076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    while (++lcluster < tcluster) {
13176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	xcluster = get_next_cluster(fs, pcluster);
13276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if (xcluster != ++pcluster)
13376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    break;		/* Not contiguous */
13476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	inode->next_extent.len += cluster_secs;
13576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    }
13676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
13776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    /* Note: ->here is bogus if ->offset >= EOF, but that's okay */
13876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    PVT(inode)->offset = lcluster << sbi->clust_shift;
13976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    PVT(inode)->here   = ((xcluster-2) << sbi->clust_shift) + data_area;
14076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
14176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    return 0;
14276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
14376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanerr:
14476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    dprintf("fat_next_extent: return error\n");
14576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    return -1;
14676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
14776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
14876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic sector_t get_next_sector(struct fs_info* fs, uint32_t sector)
14976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
15076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    struct fat_sb_info *sbi = FAT_SB(fs);
15176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    sector_t data_area = sbi->data;
15276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    sector_t data_sector;
15376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    uint32_t cluster;
15476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    int clust_shift = sbi->clust_shift;
15576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
15676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    if (sector < data_area) {
15776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Root directory sector... */
15876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	sector++;
15976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if (sector >= data_area)
16076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    sector = 0; /* Ran out of root directory, return EOF */
16176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return sector;
16276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    }
16376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
16476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    data_sector = sector - data_area;
16576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    if ((data_sector + 1) & sbi->clust_mask)  /* Still in the same cluster */
16676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return sector + 1;		      /* Next sector inside cluster */
16776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
16876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    /* get a new cluster */
16976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    cluster = data_sector >> clust_shift;
17076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    cluster = get_next_cluster(fs, cluster + 2) - 2;
17176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
17276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    if (cluster >= sbi->clusters)
17376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return 0;
17476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
17576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    /* return the start of the new cluster */
17676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    sector = (cluster << clust_shift) + data_area;
17776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    return sector;
17876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
17976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
18076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*
18176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * The FAT is a single-linked list.  We remember the last place we
18276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * were, so for a forward seek we can move forward from there, but
18376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * for a reverse seek we have to start over...
18476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
18576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic sector_t get_the_right_sector(struct file *file)
18676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
18776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    struct inode *inode = file->inode;
18876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    uint32_t sector_pos  = file->offset >> SECTOR_SHIFT(file->fs);
18976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    uint32_t where;
19076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    sector_t sector;
19176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
19276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    if (sector_pos < PVT(inode)->offset) {
19376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Reverse seek */
19476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	where = 0;
19576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	sector = PVT(inode)->start;
19676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    } else {
19776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	where = PVT(inode)->offset;
19876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	sector = PVT(inode)->here;
19976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    }
20076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
20176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    while (where < sector_pos) {
20276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	sector = get_next_sector(file->fs, sector);
20376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	where++;
20476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    }
20576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
20676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    PVT(inode)->offset = sector_pos;
20776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    PVT(inode)->here   = sector;
20876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
20976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    return sector;
21076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
21176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
21276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*
21376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Get the next sector in sequence
21476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
21576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic sector_t next_sector(struct file *file)
21676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
21776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    struct inode *inode = file->inode;
21876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    sector_t sector = get_next_sector(file->fs, PVT(inode)->here);
21976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    PVT(inode)->offset++;
22076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    PVT(inode)->here = sector;
22176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
22276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    return sector;
22376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
22476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
22576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
22676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * mangle_name:
22776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
22876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Mangle a filename pointed to by src into a buffer pointed
22976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * to by dst; ends on encountering any whitespace.
23076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * dst is preserved.
23176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
23276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * This verifies that a filename is < FILENAME_MAX characters,
23376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * doesn't contain whitespace, zero-pads the output buffer,
23476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * and removes redundant slashes.
23576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
23676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Unlike the generic version, this also converts backslashes to
23776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * forward slashes.
23876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
23976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
24076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic void vfat_mangle_name(char *dst, const char *src)
24176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
24276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    char *p = dst;
24376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    int i = FILENAME_MAX-1;
24476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    char c;
24576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
24676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    while (not_whitespace(c = *src)) {
24776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if (c == '\\')
24876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    c = '/';
24976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
25076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman        if (c == '/') {
25176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman            if (src[1] == '/' || src[1] == '\\') {
25276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman                src++;
25376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman                i--;
25476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman                continue;
25576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman            }
25676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman        }
25776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman        i--;
25876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman        *dst++ = *src++;
25976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    }
26076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
26176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    while (1) {
26276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman        if (dst == p)
26376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman            break;
26476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman        if (dst[-1] != '/')
26576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman            break;
26676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ((dst[-1] == '/') && ((dst - 1) == p))
26776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    break;
26876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
26976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman        dst--;
27076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman        i++;
27176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    }
27276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
27376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    i++;
27476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    for (; i > 0; i --)
27576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman        *dst++ = '\0';
27676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
27776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
27876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*
27976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Mangle a normal style string to DOS style string.
28076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
28176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic void mangle_dos_name(char *mangle_buf, const char *src)
28276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
28376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    int i;
28476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    unsigned char c;
28576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
28676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    if (src[0] == '.' && (!src[1] || (src[1] == '.' && !src[2]))) {
28776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* . and .. mangle to their respective zero-padded version */
28876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	i = stpcpy(mangle_buf, src) - mangle_buf;
28976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    } else {
29076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	i = 0;
29176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	while (i < 11) {
29276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    c = *src++;
29376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
29476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ((c <= ' ') || (c == '/'))
29576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    break;
29676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
29776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if (c == '.') {
29876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    while (i < 8)
29976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mangle_buf[i++] = ' ';
30076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    i = 8;
30176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    continue;
30276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
30376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
30476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	c = codepage.upper[c];
30576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if (i == 0 && c == 0xe5)
30676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    c = 0x05;		/* Special hack for the first byte only! */
30776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
30876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	mangle_buf[i++] = c;
30976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
31076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    }
31176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
31276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    while (i < 11)
31376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	mangle_buf[i++] = ' ';
31476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
31576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    mangle_buf[i] = '\0';
31676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
31776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
31876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*
31976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Match a string name against a longname.  "len" is the number of
32076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * codepoints in the input; including padding.
32176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
32276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Returns true on match.
32376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
32476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic bool vfat_match_longname(const char *str, const uint16_t *match,
32576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				int len)
32676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
32776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    unsigned char c = -1;	/* Nonzero: we have not yet seen NUL */
32876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    uint16_t cp;
32976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
33076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    dprintf("Matching: %s len %d\n", str, len);
33176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
33276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    while (len) {
33376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	cp = *match++;
33476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	len--;
33576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if (!cp)
33676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    break;
33776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	c = *str++;
33876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if (cp != codepage.uni[0][c] && cp != codepage.uni[1][c])
33976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    return false;	/* Also handles c == '\0' */
34076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    }
34176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
34276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    /* This should have been the end of the matching string */
34376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    if (*str)
34476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return false;
34576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
34676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    /* Any padding entries must be FFFF */
34776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    while (len--)
34876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if (*match++ != 0xffff)
34976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    return false;
35076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
35176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    return true;
35276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
35376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
35476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*
35576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Convert an UTF-16 longname to the system codepage; return
35676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * the length on success or -1 on failure.
35776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
35876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int vfat_cvt_longname(char *entry_name, const uint16_t *long_name)
35976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
36076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    struct unicache {
36176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	uint16_t utf16;
36276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	uint8_t cp;
36376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    };
36476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    static struct unicache unicache[256];
36576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    struct unicache *uc;
36676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    uint16_t cp;
36776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    unsigned int c;
36876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    char *p = entry_name;
36976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
37076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    do {
37176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	cp = *long_name++;
37276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	uc = &unicache[cp % 256];
37376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
37476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if (__likely(uc->utf16 == cp)) {
37576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    *p++ = uc->cp;
37676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	} else {
37776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    for (c = 0; c < 512; c++) {
37876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		/* This is a bit hacky... */
37976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		if (codepage.uni[0][c] == cp) {
38076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		    uc->utf16 = cp;
38176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		    *p++ = uc->cp = (uint8_t)c;
38276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		    goto found;
38376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		}
38476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    }
38576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    return -1;		/* Impossible character */
38676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	found:
38776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    ;
38876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
38976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    } while (cp);
39076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
39176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    return (p-entry_name)-1;
39276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
39376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
39476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic void copy_long_chunk(uint16_t *buf, const struct fat_dir_entry *de)
39576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
39676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    const struct fat_long_name_entry *le =
39776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	(const struct fat_long_name_entry *)de;
39876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
39976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    memcpy(buf,      le->name1, 5 * 2);
40076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    memcpy(buf + 5,  le->name2, 6 * 2);
40176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    memcpy(buf + 11, le->name3, 2 * 2);
40276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
40376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
40476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic uint8_t get_checksum(const char *dir_name)
40576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
40676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    int  i;
40776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    uint8_t sum = 0;
40876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
40976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    for (i = 11; i; i--)
41076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	sum = ((sum & 1) << 7) + (sum >> 1) + (uint8_t)*dir_name++;
41176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    return sum;
41276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
41376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
41476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
41576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/* compute the first sector number of one dir where the data stores */
41676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic inline sector_t first_sector(struct fs_info *fs,
41776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				    const struct fat_dir_entry *dir)
41876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
41976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    const struct fat_sb_info *sbi = FAT_SB(fs);
42076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    sector_t first_clust;
42176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    sector_t sector;
42276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
42376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    first_clust = (dir->first_cluster_high << 16) + dir->first_cluster_low;
42476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    if (first_clust == 0)
42576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	sector = sbi->root;	/* first_clust == 0 means root directory */
42676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    else
42776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	sector = ((first_clust - 2) << sbi->clust_shift) + sbi->data;
42876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
42976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    return sector;
43076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
43176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
43276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic inline enum dirent_type get_inode_mode(uint8_t attr)
43376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
43476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    return (attr & FAT_ATTR_DIRECTORY) ? DT_DIR : DT_REG;
43576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
43676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
43776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
43876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic struct inode *vfat_find_entry(const char *dname, struct inode *dir)
43976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
44076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    struct fs_info *fs = dir->fs;
44176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    struct inode *inode;
44276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    const struct fat_dir_entry *de;
44376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    struct fat_long_name_entry *long_de;
44476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
44576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    char mangled_name[12];
44676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    uint16_t long_name[260];	/* == 20*13 */
44776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    int long_len;
44876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
44976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    sector_t dir_sector = PVT(dir)->start;
45076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    uint8_t vfat_init, vfat_next, vfat_csum = 0;
45176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    uint8_t id;
45276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    int slots;
45376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    int entries;
45476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    int checksum;
45576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    int long_match = 0;
45676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
45776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    slots = (strlen(dname) + 12) / 13;
45876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    if (slots > 20)
45976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return NULL;		/* Name too long */
46076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
46176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    slots |= 0x40;
46276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    vfat_init = vfat_next = slots;
46376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    long_len = slots*13;
46476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
46576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    /* Produce the shortname version, in case we need it. */
46676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    mangle_dos_name(mangled_name, dname);
46776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
46876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    while (dir_sector) {
46976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	de = get_cache(fs->fs_dev, dir_sector);
47076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	entries = 1 << (fs->sector_shift - 5);
47176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
47276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	while (entries--) {
47376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    if (de->name[0] == 0)
47476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		return NULL;
47576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
47676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    if (de->attr == 0x0f) {
47776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		/*
47876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		 * It's a long name entry.
47976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		 */
48076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		long_de = (struct fat_long_name_entry *)de;
48176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		id = long_de->id;
48276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		if (id != vfat_next)
48376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		    goto not_match;
48476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
48576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		if (id & 0x40) {
48676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		    /* get the initial checksum value */
48776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		    vfat_csum = long_de->checksum;
48876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		    id &= 0x3f;
48976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		    long_len = id * 13;
49076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
49176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		    /* ZERO the long_name buffer */
49276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		    memset(long_name, 0, sizeof long_name);
49376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		} else {
49476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		    if (long_de->checksum != vfat_csum)
49576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			goto not_match;
49676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		}
49776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
49876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		vfat_next = --id;
49976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
50076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		/* got the long entry name */
50176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		copy_long_chunk(long_name + id*13, de);
50276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
50376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		/*
50476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		 * If we got the last entry, check it.
50576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		 * Or, go on with the next entry.
50676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		 */
50776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		if (id == 0) {
50876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		    if (!vfat_match_longname(dname, long_name, long_len))
50976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			goto not_match;
51076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		    long_match = 1;
51176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		}
51276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		de++;
51376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		continue;     /* Try the next entry */
51476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    } else {
51576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		/*
51676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		 * It's a short entry
51776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		 */
51876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		if (de->attr & 0x08) /* ignore volume labels */
51976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		    goto not_match;
52076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
52176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		if (long_match) {
52276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		    /*
52376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		     * We already have a VFAT long name match. However, the
52476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		     * match is only valid if the checksum matches.
52576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		     */
52676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		    checksum = get_checksum(de->name);
52776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		    if (checksum == vfat_csum)
52876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			goto found;  /* Got it */
52976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		} else {
53076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		    if (!memcmp(mangled_name, de->name, 11))
53176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			goto found;
53276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		}
53376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    }
53476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
53576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	not_match:
53676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    vfat_next = vfat_init;
53776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    long_match = 0;
53876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
53976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    de++;
54076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
54176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
54276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Try with the next sector */
54376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	dir_sector = get_next_sector(fs, dir_sector);
54476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    }
54576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    return NULL;		/* Nothing found... */
54676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
54776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanfound:
54876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    inode = new_fat_inode(fs);
54976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    inode->size = de->file_size;
55076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    PVT(inode)->start_cluster =
55176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	(de->first_cluster_high << 16) + de->first_cluster_low;
55276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    if (PVT(inode)->start_cluster == 0) {
55376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Root directory */
55476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	int root_size = FAT_SB(fs)->root_size;
55576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
55676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	PVT(inode)->start_cluster = FAT_SB(fs)->root_cluster;
55776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	inode->size = root_size ? root_size << fs->sector_shift : ~0;
55876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	PVT(inode)->start = PVT(inode)->here = FAT_SB(fs)->root;
55976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    } else {
56076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	PVT(inode)->start = PVT(inode)->here = first_sector(fs, de);
56176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    }
56276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    inode->mode = get_inode_mode(de->attr);
56376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
56476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    return inode;
56576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
56676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
56776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic struct inode *vfat_iget_root(struct fs_info *fs)
56876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
56976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    struct inode *inode = new_fat_inode(fs);
57076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    int root_size = FAT_SB(fs)->root_size;
57176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
57276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    /*
57376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman     * For FAT32, the only way to get the root directory size is to
57476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman     * follow the entire FAT chain to the end... which seems pointless.
57576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman     */
57676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    PVT(inode)->start_cluster = FAT_SB(fs)->root_cluster;
57776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    inode->size = root_size ? root_size << fs->sector_shift : ~0;
57876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    PVT(inode)->start = PVT(inode)->here = FAT_SB(fs)->root;
57976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    inode->mode = DT_DIR;
58076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
58176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    return inode;
58276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
58376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
58476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic struct inode *vfat_iget(const char *dname, struct inode *parent)
58576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
58676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    return vfat_find_entry(dname, parent);
58776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
58876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
58976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int vfat_readdir(struct file *file, struct dirent *dirent)
59076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
59176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    struct fs_info *fs = file->fs;
59276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    const struct fat_dir_entry *de;
59376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    const char *data;
59476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    const struct fat_long_name_entry *long_de;
59576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
59676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    sector_t sector = get_the_right_sector(file);
59776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
59876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    uint16_t long_name[261];	/* == 20*13 + 1 (to guarantee null) */
59976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    char filename[261];
60076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    int name_len = 0;
60176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
60276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    uint8_t vfat_next, vfat_csum;
60376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    uint8_t id;
60476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    int entries_left;
60576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    bool long_entry = false;
60676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    int sec_off = file->offset & ((1 << fs->sector_shift) - 1);
60776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
60876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    data = get_cache(fs->fs_dev, sector);
60976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    de = (const struct fat_dir_entry *)(data + sec_off);
61076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    entries_left = ((1 << fs->sector_shift) - sec_off) >> 5;
61176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
61276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    vfat_next = vfat_csum = 0xff;
61376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
61476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    while (1) {
61576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	while (entries_left--) {
61676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    if (de->name[0] == 0)
61776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		return -1;	/* End of directory */
61876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    if ((uint8_t)de->name[0] == 0xe5)
61976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		goto invalid;
62076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
62176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    if (de->attr == 0x0f) {
62276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		/*
62376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		 * It's a long name entry.
62476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		 */
62576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		long_de = (struct fat_long_name_entry *)de;
62676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		id = long_de->id;
62776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
62876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		if (id & 0x40) {
62976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		    /* init vfat_csum */
63076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		    vfat_csum = long_de->checksum;
63176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		    id &= 0x3f;
63276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		    if (id >= 20)
63376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			goto invalid; /* Too long! */
63476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
63576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		    /* ZERO the long_name buffer */
63676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		    memset(long_name, 0, sizeof long_name);
63776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		} else {
63876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		    if (long_de->checksum != vfat_csum || id != vfat_next)
63976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			goto invalid;
64076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		}
64176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
64276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		vfat_next = --id;
64376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
64476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		/* got the long entry name */
64576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		copy_long_chunk(long_name + id*13, de);
64676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
64776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		if (id == 0) {
64876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		    name_len = vfat_cvt_longname(filename, long_name);
64976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		    if (name_len > 0 && name_len < sizeof(dirent->d_name))
65076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			long_entry = true;
65176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		}
65276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
65376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		goto next;
65476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    } else {
65576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		/*
65676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		 * It's a short entry
65776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		 */
65876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		if (de->attr & 0x08) /* ignore volume labels */
65976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		    goto invalid;
66076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
66176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		if (long_entry && get_checksum(de->name) == vfat_csum) {
66276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		   /* Got a long entry */
66376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		} else {
66476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		    /* Use the shortname */
66576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		    int i;
66676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		    uint8_t c;
66776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		    char *p = filename;
66876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
66976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		    for (i = 0; i < 8; i++) {
67076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			c = de->name[i];
67176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			if (c == ' ')
67276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			    break;
67376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			if (de->lcase & LCASE_BASE)
67476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			    c = codepage.lower[c];
67576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			*p++ = c;
67676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		    }
67776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		    if (de->name[8] != ' ') {
67876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			*p++ = '.';
67976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			for (i = 8; i < 11; i++) {
68076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			    c = de->name[i];
68176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			    if (c == ' ')
68276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				break;
68376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			    if (de->lcase & LCASE_EXT)
68476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				c = codepage.lower[c];
68576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			    *p++ = c;
68676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			}
68776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		    }
68876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		    *p = '\0';
68976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		    name_len = p - filename;
69076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		}
69176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		goto got;	/* Got something one way or the other */
69276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    }
69376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
69476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	invalid:
69576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    long_entry = false;
69676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	next:
69776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    de++;
69876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    file->offset += sizeof(struct fat_dir_entry);
69976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
70076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
70176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Try with the next sector */
70276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	sector = next_sector(file);
70376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if (!sector)
70476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    return -1;
70576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	de = get_cache(fs->fs_dev, sector);
70676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	entries_left = 1 << (fs->sector_shift - 5);
70776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    }
70876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
70976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmangot:
71076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    name_len++;			/* Include final null */
71176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    dirent->d_ino = de->first_cluster_low | (de->first_cluster_high << 16);
71276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    dirent->d_off = file->offset;
71376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    dirent->d_reclen = offsetof(struct dirent, d_name) + name_len;
71476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    dirent->d_type = get_inode_mode(de->attr);
71576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    memcpy(dirent->d_name, filename, name_len);
71676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
71776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    file->offset += sizeof(*de);  /* Update for next reading */
71876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
71976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    return 0;
72076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
72176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
72276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/* init. the fs meta data, return the block size in bits */
72376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int vfat_fs_init(struct fs_info *fs)
72476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
72576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    struct fat_bpb fat;
72676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    struct fat_sb_info *sbi;
72776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    struct disk *disk = fs->fs_dev->disk;
72876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    int sectors_per_fat;
72976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    uint32_t clusters;
73076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    sector_t total_sectors;
73176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
73276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    fs->sector_shift = fs->block_shift = disk->sector_shift;
73376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    fs->sector_size  = 1 << fs->sector_shift;
73476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    fs->block_size   = 1 << fs->block_shift;
73576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
73676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    disk->rdwr_sectors(disk, &fat, 0, 1, 0);
73776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
73876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    /* XXX: Find better sanity checks... */
73976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    if (!fat.bxResSectors || !fat.bxFATs)
74076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return -1;
74176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    sbi = malloc(sizeof(*sbi));
74276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    if (!sbi)
74376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	malloc_error("fat_sb_info structure");
74476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    fs->fs_info = sbi;
74576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
74676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    sectors_per_fat = fat.bxFATsecs ? : fat.fat32.bxFATsecs_32;
74776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    total_sectors   = fat.bxSectors ? : fat.bsHugeSectors;
74876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
74976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    sbi->fat       = fat.bxResSectors;
75076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    sbi->root      = sbi->fat + sectors_per_fat * fat.bxFATs;
75176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    sbi->root_size = root_dir_size(fs, &fat);
75276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    sbi->data      = sbi->root + sbi->root_size;
75376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
75476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    sbi->clust_shift      = ilog2(fat.bxSecPerClust);
75576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    sbi->clust_byte_shift = sbi->clust_shift + fs->sector_shift;
75676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    sbi->clust_mask       = fat.bxSecPerClust - 1;
75776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    sbi->clust_size       = fat.bxSecPerClust << fs->sector_shift;
75876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
75976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    clusters = (total_sectors - sbi->data) >> sbi->clust_shift;
76076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    if (clusters <= 0xff4) {
76176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	sbi->fat_type = FAT12;
76276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    } else if (clusters <= 0xfff4) {
76376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	sbi->fat_type = FAT16;
76476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    } else {
76576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	sbi->fat_type = FAT32;
76676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
76776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if (clusters > 0x0ffffff4)
76876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    clusters = 0x0ffffff4; /* Maximum possible */
76976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
77076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if (fat.fat32.extended_flags & 0x80) {
77176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    /* Non-mirrored FATs, we need to read the active one */
77276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    sbi->fat += (fat.fat32.extended_flags & 0x0f) * sectors_per_fat;
77376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
77476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
77576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* FAT32: root directory is a cluster chain */
77676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	sbi->root = sbi->data
77776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    + ((fat.fat32.root_cluster-2) << sbi->clust_shift);
77876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    }
77976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    sbi->clusters = clusters;
78076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
78176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    /* fs UUID - serial number */
78276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    if (FAT32 == sbi->fat_type)
78376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	sbi->uuid = fat.fat32.num_serial;
78476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    else
78576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	sbi->uuid = fat.fat12_16.num_serial;
78676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
78776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    /* Initialize the cache */
78876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    cache_init(fs->fs_dev, fs->block_shift);
78976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
79076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    return fs->block_shift;
79176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
79276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
79376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int vfat_copy_superblock(void *buf)
79476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
79576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct fat_bpb fat;
79676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct disk *disk;
79776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	size_t sb_off;
79876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	void *dst;
79976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	int sb_len;
80076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
80176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	disk = this_fs->fs_dev->disk;
80276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	disk->rdwr_sectors(disk, &fat, 0, 1, 0);
80376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
80476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* XXX: Find better sanity checks... */
80576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if (!fat.bxResSectors || !fat.bxFATs)
80676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		return -1;
80776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
80876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	sb_off = offsetof(struct fat_bpb, sector_size);
80976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	sb_len = offsetof(struct fat_bpb, fat12_16) - sb_off \
81076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		+ sizeof(fat.fat12_16);
81176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
81276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/*
81376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	 * Only copy fields of the superblock we actually care about.
81476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	 */
81576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	dst = buf + sb_off;
81676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	memcpy(dst, (void *)&fat + sb_off, sb_len);
81776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
81876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return 0;
81976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
82076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
82176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#define FAT_UUID_LEN (4 + 1 + 4 + 1)
82276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic char *vfat_fs_uuid(struct fs_info *fs)
82376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
82476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    char *uuid = NULL;
82576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    char *ptr;
82676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
82776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    uuid = malloc(FAT_UUID_LEN);
82876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    if (!uuid)
82976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return NULL;
83076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
83176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    if (snprintf(uuid, FAT_UUID_LEN, "%04x-%04x",
83276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	          (uint16_t)(FAT_SB(fs)->uuid >> 16),
83376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	          (uint16_t)FAT_SB(fs)->uuid) < 0) {
83476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	free(uuid);
83576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return NULL;
83676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    }
83776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
83876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    for (ptr = uuid; ptr && *ptr; ptr++)
83976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	*ptr = toupper(*ptr);
84076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
84176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    return uuid;
84276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
84376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
84476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanconst struct fs_ops vfat_fs_ops = {
84576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    .fs_name       = "vfat",
84676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    .fs_flags      = FS_USEMEM | FS_THISIND,
84776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    .fs_init       = vfat_fs_init,
84876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    .searchdir     = NULL,
84976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    .getfssec      = generic_getfssec,
85076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    .close_file    = generic_close_file,
85176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    .mangle_name   = vfat_mangle_name,
85276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    .chdir_start   = generic_chdir_start,
85376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    .open_config   = generic_open_config,
85476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    .readdir       = vfat_readdir,
85576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    .iget_root     = vfat_iget_root,
85676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    .iget          = vfat_iget,
85776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    .next_extent   = fat_next_extent,
85876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    .copy_super    = vfat_copy_superblock,
85976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    .fs_uuid       = vfat_fs_uuid,
86076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman};
861