diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/ext2/dev.c | 2 | ||||
-rw-r--r-- | fs/fat/Makefile | 1 | ||||
-rw-r--r-- | fs/fat/fat.c | 6 | ||||
-rw-r--r-- | fs/fat/fat_write.c | 1090 | ||||
-rw-r--r-- | fs/yaffs2/yaffs_guts.c | 161 |
5 files changed, 1101 insertions, 159 deletions
diff --git a/fs/ext2/dev.c b/fs/ext2/dev.c index 78851d0a5d..874e21161e 100644 --- a/fs/ext2/dev.c +++ b/fs/ext2/dev.c @@ -52,7 +52,7 @@ int ext2fs_set_blk_dev(block_dev_desc_t *rbdd, int part) int ext2fs_devread(int sector, int byte_offset, int byte_len, char *buf) { - char sec_buf[SECTOR_SIZE]; + ALLOC_CACHE_ALIGN_BUFFER(char, sec_buf, SECTOR_SIZE); unsigned sectors; /* diff --git a/fs/fat/Makefile b/fs/fat/Makefile index 93b6f07389..9635d36fcf 100644 --- a/fs/fat/Makefile +++ b/fs/fat/Makefile @@ -25,6 +25,7 @@ LIB = $(obj)libfat.o AOBJS = COBJS-$(CONFIG_CMD_FAT) := fat.o +COBJS-$(CONFIG_FAT_WRITE):= fat_write.o ifndef CONFIG_SPL_BUILD COBJS-$(CONFIG_CMD_FAT) += file.o diff --git a/fs/fat/fat.c b/fs/fat/fat.c index 756ac64a8b..28baa546ab 100644 --- a/fs/fat/fat.c +++ b/fs/fat/fat.c @@ -572,8 +572,8 @@ static dir_entry *get_dentfromdir (fsdata *mydata, int startsect, } if ((dentptr->attr & ATTR_VOLUME)) { #ifdef CONFIG_SUPPORT_VFAT - if ((dentptr->attr & ATTR_VFAT) && - (dentptr-> name[0] & LAST_LONG_ENTRY_MASK)) { + if ((dentptr->attr & ATTR_VFAT) == ATTR_VFAT && + (dentptr->name[0] & LAST_LONG_ENTRY_MASK)) { prevcksum = ((dir_slot *)dentptr)->alias_checksum; get_vfatname(mydata, curclust, get_dentfromdir_block, @@ -897,7 +897,7 @@ do_fat_read (const char *filename, void *buffer, unsigned long maxsize, } if ((dentptr->attr & ATTR_VOLUME)) { #ifdef CONFIG_SUPPORT_VFAT - if ((dentptr->attr & ATTR_VFAT) && + if ((dentptr->attr & ATTR_VFAT) == ATTR_VFAT && (dentptr->name[0] & LAST_LONG_ENTRY_MASK)) { prevcksum = ((dir_slot *)dentptr)->alias_checksum; diff --git a/fs/fat/fat_write.c b/fs/fat/fat_write.c new file mode 100644 index 0000000000..4f1772f29a --- /dev/null +++ b/fs/fat/fat_write.c @@ -0,0 +1,1090 @@ +/* + * fat_write.c + * + * R/W (V)FAT 12/16/32 filesystem implementation by Donggeun Kim + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <command.h> +#include <config.h> +#include <fat.h> +#include <asm/byteorder.h> +#include <part.h> +#include "fat.c" + +static void uppercase(char *str, int len) +{ + int i; + + for (i = 0; i < len; i++) { + TOUPPER(*str); + str++; + } +} + +static int total_sector; +static int disk_write(__u32 startblock, __u32 getsize, __u8 *bufptr) +{ + if (cur_dev == NULL) + return -1; + + if (startblock + getsize > total_sector) { + printf("error: overflow occurs\n"); + return -1; + } + + startblock += part_offset; + + if (cur_dev->block_read) { + return cur_dev->block_write(cur_dev->dev, startblock, getsize, + (unsigned long *) bufptr); + } + return -1; +} + +/* + * Set short name in directory entry + */ +static void set_name(dir_entry *dirent, const char *filename) +{ + char s_name[VFAT_MAXLEN_BYTES]; + char *period; + int period_location, len, i, ext_num; + + if (filename == NULL) + return; + + len = strlen(filename); + if (len == 0) + return; + + memcpy(s_name, filename, len); + uppercase(s_name, len); + + period = strchr(s_name, '.'); + if (period == NULL) { + period_location = len; + ext_num = 0; + } else { + period_location = period - s_name; + ext_num = len - period_location - 1; + } + + /* Pad spaces when the length of file name is shorter than eight */ + if (period_location < 8) { + memcpy(dirent->name, s_name, period_location); + for (i = period_location; i < 8; i++) + dirent->name[i] = ' '; + } else if (period_location == 8) { + memcpy(dirent->name, s_name, period_location); + } else { + memcpy(dirent->name, s_name, 6); + dirent->name[6] = '~'; + dirent->name[7] = '1'; + } + + if (ext_num < 3) { + memcpy(dirent->ext, s_name + period_location + 1, ext_num); + for (i = ext_num; i < 3; i++) + dirent->ext[i] = ' '; + } else + memcpy(dirent->ext, s_name + period_location + 1, 3); + + debug("name : %s\n", dirent->name); + debug("ext : %s\n", dirent->ext); +} + +/* + * Write fat buffer into block device + */ +static int flush_fat_buffer(fsdata *mydata) +{ + int getsize = FATBUFBLOCKS; + __u32 fatlength = mydata->fatlength; + __u8 *bufptr = mydata->fatbuf; + __u32 startblock = mydata->fatbufnum * FATBUFBLOCKS; + + fatlength *= mydata->sect_size; + startblock += mydata->fat_sect; + + if (getsize > fatlength) + getsize = fatlength; + + /* Write FAT buf */ + if (disk_write(startblock, getsize, bufptr) < 0) { + debug("error: writing FAT blocks\n"); + return -1; + } + + return 0; +} + +/* + * Get the entry at index 'entry' in a FAT (12/16/32) table. + * On failure 0x00 is returned. + * When bufnum is changed, write back the previous fatbuf to the disk. + */ +static __u32 get_fatent_value(fsdata *mydata, __u32 entry) +{ + __u32 bufnum; + __u32 off16, offset; + __u32 ret = 0x00; + __u16 val1, val2; + + switch (mydata->fatsize) { + case 32: + bufnum = entry / FAT32BUFSIZE; + offset = entry - bufnum * FAT32BUFSIZE; + break; + case 16: + bufnum = entry / FAT16BUFSIZE; + offset = entry - bufnum * FAT16BUFSIZE; + break; + case 12: + bufnum = entry / FAT12BUFSIZE; + offset = entry - bufnum * FAT12BUFSIZE; + break; + + default: + /* Unsupported FAT size */ + return ret; + } + + debug("FAT%d: entry: 0x%04x = %d, offset: 0x%04x = %d\n", + mydata->fatsize, entry, entry, offset, offset); + + /* Read a new block of FAT entries into the cache. */ + if (bufnum != mydata->fatbufnum) { + int getsize = FATBUFBLOCKS; + __u8 *bufptr = mydata->fatbuf; + __u32 fatlength = mydata->fatlength; + __u32 startblock = bufnum * FATBUFBLOCKS; + + if (getsize > fatlength) + getsize = fatlength; + + fatlength *= mydata->sect_size; /* We want it in bytes now */ + startblock += mydata->fat_sect; /* Offset from start of disk */ + + /* Write back the fatbuf to the disk */ + if (mydata->fatbufnum != -1) { + if (flush_fat_buffer(mydata) < 0) + return -1; + } + + if (disk_read(startblock, getsize, bufptr) < 0) { + debug("Error reading FAT blocks\n"); + return ret; + } + mydata->fatbufnum = bufnum; + } + + /* Get the actual entry from the table */ + switch (mydata->fatsize) { + case 32: + ret = FAT2CPU32(((__u32 *) mydata->fatbuf)[offset]); + break; + case 16: + ret = FAT2CPU16(((__u16 *) mydata->fatbuf)[offset]); + break; + case 12: + off16 = (offset * 3) / 4; + + switch (offset & 0x3) { + case 0: + ret = FAT2CPU16(((__u16 *) mydata->fatbuf)[off16]); + ret &= 0xfff; + break; + case 1: + val1 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]); + val1 &= 0xf000; + val2 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16 + 1]); + val2 &= 0x00ff; + ret = (val2 << 4) | (val1 >> 12); + break; + case 2: + val1 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]); + val1 &= 0xff00; + val2 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16 + 1]); + val2 &= 0x000f; + ret = (val2 << 8) | (val1 >> 8); + break; + case 3: + ret = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]); + ret = (ret & 0xfff0) >> 4; + break; + default: + break; + } + break; + } + debug("FAT%d: ret: %08x, entry: %08x, offset: %04x\n", + mydata->fatsize, ret, entry, offset); + + return ret; +} + +#ifdef CONFIG_SUPPORT_VFAT +/* + * Set the file name information from 'name' into 'slotptr', + */ +static int str2slot(dir_slot *slotptr, const char *name, int *idx) +{ + int j, end_idx = 0; + + for (j = 0; j <= 8; j += 2) { + if (name[*idx] == 0x00) { + slotptr->name0_4[j] = 0; + slotptr->name0_4[j + 1] = 0; + end_idx++; + goto name0_4; + } + slotptr->name0_4[j] = name[*idx]; + (*idx)++; + end_idx++; + } + for (j = 0; j <= 10; j += 2) { + if (name[*idx] == 0x00) { + slotptr->name5_10[j] = 0; + slotptr->name5_10[j + 1] = 0; + end_idx++; + goto name5_10; + } + slotptr->name5_10[j] = name[*idx]; + (*idx)++; + end_idx++; + } + for (j = 0; j <= 2; j += 2) { + if (name[*idx] == 0x00) { + slotptr->name11_12[j] = 0; + slotptr->name11_12[j + 1] = 0; + end_idx++; + goto name11_12; + } + slotptr->name11_12[j] = name[*idx]; + (*idx)++; + end_idx++; + } + + if (name[*idx] == 0x00) + return 1; + + return 0; +/* Not used characters are filled with 0xff 0xff */ +name0_4: + for (; end_idx < 5; end_idx++) { + slotptr->name0_4[end_idx * 2] = 0xff; + slotptr->name0_4[end_idx * 2 + 1] = 0xff; + } + end_idx = 5; +name5_10: + end_idx -= 5; + for (; end_idx < 6; end_idx++) { + slotptr->name5_10[end_idx * 2] = 0xff; + slotptr->name5_10[end_idx * 2 + 1] = 0xff; + } + end_idx = 11; +name11_12: + end_idx -= 11; + for (; end_idx < 2; end_idx++) { + slotptr->name11_12[end_idx * 2] = 0xff; + slotptr->name11_12[end_idx * 2 + 1] = 0xff; + } + + return 1; +} + +static int is_next_clust(fsdata *mydata, dir_entry *dentptr); +static void flush_dir_table(fsdata *mydata, dir_entry **dentptr); + +/* + * Fill dir_slot entries with appropriate name, id, and attr + * The real directory entry is returned by 'dentptr' + */ +static void +fill_dir_slot(fsdata *mydata, dir_entry **dentptr, const char *l_name) +{ + dir_slot *slotptr = (dir_slot *)get_vfatname_block; + __u8 counter, checksum; + int idx = 0, ret; + char s_name[16]; + + /* Get short file name and checksum value */ + strncpy(s_name, (*dentptr)->name, 16); + checksum = mkcksum(s_name); + + do { + memset(slotptr, 0x00, sizeof(dir_slot)); + ret = str2slot(slotptr, l_name, &idx); + slotptr->id = ++counter; + slotptr->attr = ATTR_VFAT; + slotptr->alias_checksum = checksum; + slotptr++; + } while (ret == 0); + + slotptr--; + slotptr->id |= LAST_LONG_ENTRY_MASK; + + while (counter >= 1) { + if (is_next_clust(mydata, *dentptr)) { + /* A new cluster is allocated for directory table */ + flush_dir_table(mydata, dentptr); + } + memcpy(*dentptr, slotptr, sizeof(dir_slot)); + (*dentptr)++; + slotptr--; + counter--; + } + + if (is_next_clust(mydata, *dentptr)) { + /* A new cluster is allocated for directory table */ + flush_dir_table(mydata, dentptr); + } +} + +static __u32 dir_curclust; + +/* + * Extract the full long filename starting at 'retdent' (which is really + * a slot) into 'l_name'. If successful also copy the real directory entry + * into 'retdent' + * If additional adjacent cluster for directory entries is read into memory, + * then 'get_vfatname_block' is copied into 'get_dentfromdir_block' and + * the location of the real directory entry is returned by 'retdent' + * Return 0 on success, -1 otherwise. + */ +static int +get_long_file_name(fsdata *mydata, int curclust, __u8 *cluster, + dir_entry **retdent, char *l_name) +{ + dir_entry *realdent; + dir_slot *slotptr = (dir_slot *)(*retdent); + dir_slot *slotptr2 = NULL; + __u8 *buflimit = cluster + mydata->sect_size * ((curclust == 0) ? + PREFETCH_BLOCKS : + mydata->clust_size); + __u8 counter = (slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff; + int idx = 0, cur_position = 0; + + if (counter > VFAT_MAXSEQ) { + debug("Error: VFAT name is too long\n"); + return -1; + } + + while ((__u8 *)slotptr < buflimit) { + if (counter == 0) + break; + if (((slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff) != counter) + return -1; + slotptr++; + counter--; + } + + if ((__u8 *)slotptr >= buflimit) { + if (curclust == 0) + return -1; + curclust = get_fatent_value(mydata, dir_curclust); + if (CHECK_CLUST(curclust, mydata->fatsize)) { + debug("curclust: 0x%x\n", curclust); + printf("Invalid FAT entry\n"); + return -1; + } + + dir_curclust = curclust; + + if (get_cluster(mydata, curclust, get_vfatname_block, + mydata->clust_size * mydata->sect_size) != 0) { + debug("Error: reading directory block\n"); + return -1; + } + + slotptr2 = (dir_slot *)get_vfatname_block; + while (counter > 0) { + if (((slotptr2->id & ~LAST_LONG_ENTRY_MASK) + & 0xff) != counter) + return -1; + slotptr2++; + counter--; + } + + /* Save the real directory entry */ + realdent = (dir_entry *)slotptr2; + while ((__u8 *)slotptr2 > get_vfatname_block) { + slotptr2--; + slot2str(slotptr2, l_name, &idx); + } + } else { + /* Save the real directory entry */ + realdent = (dir_entry *)slotptr; + } + + do { + slotptr--; + if (slot2str(slotptr, l_name, &idx)) + break; + } while (!(slotptr->id & LAST_LONG_ENTRY_MASK)); + + l_name[idx] = '\0'; + if (*l_name == DELETED_FLAG) + *l_name = '\0'; + else if (*l_name == aRING) + *l_name = DELETED_FLAG; + downcase(l_name); + + /* Return the real directory entry */ + *retdent = realdent; + + if (slotptr2) { + memcpy(get_dentfromdir_block, get_vfatname_block, + mydata->clust_size * mydata->sect_size); + cur_position = (__u8 *)realdent - get_vfatname_block; + *retdent = (dir_entry *) &get_dentfromdir_block[cur_position]; + } + + return 0; +} + +#endif + +/* + * Set the entry at index 'entry' in a FAT (16/32) table. + */ +static int set_fatent_value(fsdata *mydata, __u32 entry, __u32 entry_value) +{ + __u32 bufnum, offset; + + switch (mydata->fatsize) { + case 32: + bufnum = entry / FAT32BUFSIZE; + offset = entry - bufnum * FAT32BUFSIZE; + break; + case 16: + bufnum = entry / FAT16BUFSIZE; + offset = entry - bufnum * FAT16BUFSIZE; + break; + default: + /* Unsupported FAT size */ + return -1; + } + + /* Read a new block of FAT entries into the cache. */ + if (bufnum != mydata->fatbufnum) { + int getsize = FATBUFBLOCKS; + __u8 *bufptr = mydata->fatbuf; + __u32 fatlength = mydata->fatlength; + __u32 startblock = bufnum * FATBUFBLOCKS; + + fatlength *= mydata->sect_size; + startblock += mydata->fat_sect; + + if (getsize > fatlength) + getsize = fatlength; + + if (mydata->fatbufnum != -1) { + if (flush_fat_buffer(mydata) < 0) + return -1; + } + + if (disk_read(startblock, getsize, bufptr) < 0) { + debug("Error reading FAT blocks\n"); + return -1; + } + mydata->fatbufnum = bufnum; + } + + /* Set the actual entry */ + switch (mydata->fatsize) { + case 32: + ((__u32 *) mydata->fatbuf)[offset] = cpu_to_le32(entry_value); + break; + case 16: + ((__u16 *) mydata->fatbuf)[offset] = cpu_to_le16(entry_value); + break; + default: + return -1; + } + + return 0; +} + +/* + * Determine the entry value at index 'entry' in a FAT (16/32) table + */ +static __u32 determine_fatent(fsdata *mydata, __u32 entry) +{ + __u32 next_fat, next_entry = entry + 1; + + while (1) { + next_fat = get_fatent_value(mydata, next_entry); + if (next_fat == 0) { + set_fatent_value(mydata, entry, next_entry); + break; + } + next_entry++; + } + debug("FAT%d: entry: %08x, entry_value: %04x\n", + mydata->fatsize, entry, next_entry); + + return next_entry; +} + +/* + * Write at most 'size' bytes from 'buffer' into the specified cluster. + * Return 0 on success, -1 otherwise. + */ +static int +set_cluster(fsdata *mydata, __u32 clustnum, __u8 *buffer, + unsigned long size) +{ + int idx = 0; + __u32 startsect; + + if (clustnum > 0) + startsect = mydata->data_begin + + clustnum * mydata->clust_size; + else + startsect = mydata->rootdir_sect; + + debug("clustnum: %d, startsect: %d\n", clustnum, startsect); + + if (disk_write(startsect, size / mydata->sect_size, buffer) < 0) { + debug("Error writing data\n"); + return -1; + } + + if (size % mydata->sect_size) { + __u8 tmpbuf[mydata->sect_size]; + + idx = size / mydata->sect_size; + buffer += idx * mydata->sect_size; + memcpy(tmpbuf, buffer, size % mydata->sect_size); + + if (disk_write(startsect + idx, 1, tmpbuf) < 0) { + debug("Error writing data\n"); + return -1; + } + + return 0; + } + + return 0; +} + +/* + * Find the first empty cluster + */ +static int find_empty_cluster(fsdata *mydata) +{ + __u32 fat_val, entry = 3; + + while (1) { + fat_val = get_fatent_value(mydata, entry); + if (fat_val == 0) + break; + entry++; + } + + return entry; +} + +/* + * Write directory entries in 'get_dentfromdir_block' to block device + */ +static void flush_dir_table(fsdata *mydata, dir_entry **dentptr) +{ + int dir_newclust = 0; + + if (set_cluster(mydata, dir_curclust, + get_dentfromdir_block, + mydata->clust_size * mydata->sect_size) != 0) { + printf("error: wrinting directory entry\n"); + return; + } + dir_newclust = find_empty_cluster(mydata); + set_fatent_value(mydata, dir_curclust, dir_newclust); + if (mydata->fatsize == 32) + set_fatent_value(mydata, dir_newclust, 0xffffff8); + else if (mydata->fatsize == 16) + set_fatent_value(mydata, dir_newclust, 0xfff8); + + dir_curclust = dir_newclust; + + if (flush_fat_buffer(mydata) < 0) + return; + + memset(get_dentfromdir_block, 0x00, + mydata->clust_size * mydata->sect_size); + + *dentptr = (dir_entry *) get_dentfromdir_block; +} + +/* + * Set empty cluster from 'entry' to the end of a file + */ +static int clear_fatent(fsdata *mydata, __u32 entry) +{ + __u32 fat_val; + + while (1) { + fat_val = get_fatent_value(mydata, entry); + if (fat_val != 0) + set_fatent_value(mydata, entry, 0); + else + break; + + if (fat_val == 0xfffffff || fat_val == 0xffff) + break; + + entry = fat_val; + } + + /* Flush fat buffer */ + if (flush_fat_buffer(mydata) < 0) + return -1; + + return 0; +} + +/* + * Write at most 'maxsize' bytes from 'buffer' into + * the file associated with 'dentptr' + * Return the number of bytes read or -1 on fatal errors. + */ +static int +set_contents(fsdata *mydata, dir_entry *dentptr, __u8 *buffer, + unsigned long maxsize) +{ + unsigned long filesize = FAT2CPU32(dentptr->size), gotsize = 0; + unsigned int bytesperclust = mydata->clust_size * mydata->sect_size; + __u32 curclust = START(dentptr); + __u32 endclust = 0, newclust = 0; + unsigned long actsize; + + debug("Filesize: %ld bytes\n", filesize); + + if (maxsize > 0 && filesize > maxsize) + filesize = maxsize; + + debug("%ld bytes\n", filesize); + + actsize = bytesperclust; + endclust = curclust; + do { + /* search for consecutive clusters */ + while (actsize < filesize) { + newclust = determine_fatent(mydata, endclust); + + if ((newclust - 1) != endclust) + goto getit; + + if (CHECK_CLUST(newclust, mydata->fatsize)) { + debug("curclust: 0x%x\n", newclust); + debug("Invalid FAT entry\n"); + return gotsize; + } + endclust = newclust; + actsize += bytesperclust; + } + /* actsize >= file size */ + actsize -= bytesperclust; + /* set remaining clusters */ + if (set_cluster(mydata, curclust, buffer, (int)actsize) != 0) { + debug("error: writing cluster\n"); + return -1; + } + + /* set remaining bytes */ + gotsize += (int)actsize; + filesize -= actsize; + buffer += actsize; + actsize = filesize; + + if (set_cluster(mydata, endclust, buffer, (int)actsize) != 0) { + debug("error: writing cluster\n"); + return -1; + } + gotsize += actsize; + + /* Mark end of file in FAT */ + if (mydata->fatsize == 16) + newclust = 0xffff; + else if (mydata->fatsize == 32) + newclust = 0xfffffff; + set_fatent_value(mydata, endclust, newclust); + + return gotsize; +getit: + if (set_cluster(mydata, curclust, buffer, (int)actsize) != 0) { + debug("error: writing cluster\n"); + return -1; + } + gotsize += (int)actsize; + filesize -= actsize; + buffer += actsize; + + if (CHECK_CLUST(curclust, mydata->fatsize)) { + debug("curclust: 0x%x\n", curclust); + debug("Invalid FAT entry\n"); + return gotsize; + } + actsize = bytesperclust; + curclust = endclust = newclust; + } while (1); +} + +/* + * Fill dir_entry + */ +static void fill_dentry(fsdata *mydata, dir_entry *dentptr, + const char *filename, __u32 start_cluster, __u32 size, __u8 attr) +{ + if (mydata->fatsize == 32) + dentptr->starthi = + cpu_to_le16((start_cluster & 0xffff0000) >> 16); + dentptr->start = cpu_to_le16(start_cluster & 0xffff); + dentptr->size = cpu_to_le32(size); + + dentptr->attr = attr; + + set_name(dentptr, filename); +} + +/* + * Check whether adding a file makes the file system to + * exceed the size of the block device + * Return -1 when overflow occurs, otherwise return 0 + */ +static int check_overflow(fsdata *mydata, __u32 clustnum, unsigned long size) +{ + __u32 startsect, sect_num; + + if (clustnum > 0) { + startsect = mydata->data_begin + + clustnum * mydata->clust_size; + } else { + startsect = mydata->rootdir_sect; + } + + sect_num = size / mydata->sect_size; + if (size % mydata->sect_size) + sect_num++; + + if (startsect + sect_num > total_sector) + return -1; + + return 0; +} + +/* + * Check if adding several entries exceed one cluster boundary + */ +static int is_next_clust(fsdata *mydata, dir_entry *dentptr) +{ + int cur_position; + + cur_position = (__u8 *)dentptr - get_dentfromdir_block; + + if (cur_position >= mydata->clust_size * mydata->sect_size) + return 1; + else + return 0; +} + +static dir_entry *empty_dentptr; +/* + * Find a directory entry based on filename or start cluster number + * If the directory entry is not found, + * the new position for writing a directory entry will be returned + */ +static dir_entry *find_directory_entry(fsdata *mydata, int startsect, + char *filename, dir_entry *retdent, __u32 start) +{ + __u16 prevcksum = 0xffff; + __u32 curclust = (startsect - mydata->data_begin) / mydata->clust_size; + + debug("get_dentfromdir: %s\n", filename); + + while (1) { + dir_entry *dentptr; + + int i; + + if (get_cluster(mydata, curclust, get_dentfromdir_block, + mydata->clust_size * mydata->sect_size) != 0) { + printf("Error: reading directory block\n"); + return NULL; + } + + dentptr = (dir_entry *)get_dentfromdir_block; + + dir_curclust = curclust; + + for (i = 0; i < DIRENTSPERCLUST; i++) { + char s_name[14], l_name[VFAT_MAXLEN_BYTES]; + + l_name[0] = '\0'; + if (dentptr->name[0] == DELETED_FLAG) { + dentptr++; + if (is_next_clust(mydata, dentptr)) + break; + continue; + } + if ((dentptr->attr & ATTR_VOLUME)) { +#ifdef CONFIG_SUPPORT_VFAT + if ((dentptr->attr & ATTR_VFAT) && + (dentptr->name[0] & LAST_LONG_ENTRY_MASK)) { + prevcksum = + ((dir_slot *)dentptr)->alias_checksum; + get_long_file_name(mydata, curclust, + get_dentfromdir_block, + &dentptr, l_name); + debug("vfatname: |%s|\n", l_name); + } else +#endif + { + /* Volume label or VFAT entry */ + dentptr++; + if (is_next_clust(mydata, dentptr)) + break; + continue; + } + } + if (dentptr->name[0] == 0) { + debug("Dentname == NULL - %d\n", i); + empty_dentptr = dentptr; + return NULL; + } + + get_name(dentptr, s_name); + + if (strcmp(filename, s_name) + && strcmp(filename, l_name)) { + debug("Mismatch: |%s|%s|\n", + s_name, l_name); + dentptr++; + if (is_next_clust(mydata, dentptr)) + break; + continue; + } + + memcpy(retdent, dentptr, sizeof(dir_entry)); + + debug("DentName: %s", s_name); + debug(", start: 0x%x", START(dentptr)); + debug(", size: 0x%x %s\n", + FAT2CPU32(dentptr->size), + (dentptr->attr & ATTR_DIR) ? + "(DIR)" : ""); + + return dentptr; + } + + curclust = get_fatent_value(mydata, dir_curclust); + if ((curclust >= 0xffffff8) || (curclust >= 0xfff8)) { + empty_dentptr = dentptr; + return NULL; + } + if (CHECK_CLUST(curclust, mydata->fatsize)) { + debug("curclust: 0x%x\n", curclust); + debug("Invalid FAT entry\n"); + return NULL; + } + } + + return NULL; +} + +static int do_fat_write(const char *filename, void *buffer, + unsigned long size) +{ + dir_entry *dentptr, *retdent; + dir_slot *slotptr; + __u32 startsect; + __u32 start_cluster; + boot_sector bs; + volume_info volinfo; + fsdata datablock; + fsdata *mydata = &datablock; + int cursect; + int root_cluster, ret = -1, name_len; + char l_filename[VFAT_MAXLEN_BYTES]; + + dir_curclust = 0; + + if (read_bootsectandvi(&bs, &volinfo, &mydata->fatsize)) { + debug("error: reading boot sector\n"); + return -1; + } + + total_sector = bs.total_sect; + if (total_sector == 0) + total_sector = part_size; + + root_cluster = bs.root_cluster; + + if (mydata->fatsize == 32) + mydata->fatlength = bs.fat32_length; + else + mydata->fatlength = bs.fat_length; + + mydata->fat_sect = bs.reserved; + + cursect = mydata->rootdir_sect + = mydata->fat_sect + mydata->fatlength * bs.fats; + + mydata->sect_size = (bs.sector_size[1] << 8) + bs.sector_size[0]; + mydata->clust_size = bs.cluster_size; + + if (mydata->fatsize == 32) { + mydata->data_begin = mydata->rootdir_sect - + (mydata->clust_size * 2); + } else { + int rootdir_size; + + rootdir_size = ((bs.dir_entries[1] * (int)256 + + bs.dir_entries[0]) * + sizeof(dir_entry)) / + mydata->sect_size; + mydata->data_begin = mydata->rootdir_sect + + rootdir_size - + (mydata->clust_size * 2); + } + + mydata->fatbufnum = -1; + mydata->fatbuf = malloc(FATBUFSIZE); + if (mydata->fatbuf == NULL) { + debug("Error: allocating memory\n"); + return -1; + } + + if (disk_read(cursect, + (mydata->fatsize == 32) ? + (mydata->clust_size) : + PREFETCH_BLOCKS, do_fat_read_block) < 0) { + debug("Error: reading rootdir block\n"); + goto exit; + } + dentptr = (dir_entry *) do_fat_read_block; + + name_len = strlen(filename); + memcpy(l_filename, filename, name_len); + downcase(l_filename); + + startsect = mydata->rootdir_sect; + retdent = find_directory_entry(mydata, startsect, + l_filename, dentptr, 0); + if (retdent) { + /* Update file size and start_cluster in a directory entry */ + retdent->size = cpu_to_le32(size); + start_cluster = FAT2CPU16(retdent->start); + if (mydata->fatsize == 32) + start_cluster |= + (FAT2CPU16(retdent->starthi) << 16); + + ret = check_overflow(mydata, start_cluster, size); + if (ret) { + printf("Error: %ld overflow\n", size); + goto exit; + } + + ret = clear_fatent(mydata, start_cluster); + if (ret) { + printf("Error: clearing FAT entries\n"); + goto exit; + } + + ret = set_contents(mydata, retdent, buffer, size); + if (ret) { + printf("Error: writing contents\n"); + goto exit; + } + + /* Flush fat buffer */ + ret = flush_fat_buffer(mydata); + if (ret) { + printf("Error: flush fat buffer\n"); + goto exit; + } + + /* Write directory table to device */ + ret = set_cluster(mydata, dir_curclust, + get_dentfromdir_block, + mydata->clust_size * mydata->sect_size); + if (ret) { + printf("Error: wrinting directory entry\n"); + goto exit; + } + } else { + slotptr = (dir_slot *)empty_dentptr; + + /* Set short name to set alias checksum field in dir_slot */ + set_name(empty_dentptr, filename); + fill_dir_slot(mydata, &empty_dentptr, filename); + + ret = start_cluster = find_empty_cluster(mydata); + if (ret < 0) { + printf("Error: finding empty cluster\n"); + goto exit; + } + + ret = check_overflow(mydata, start_cluster, size); + if (ret) { + printf("Error: %ld overflow\n", size); + goto exit; + } + + /* Set attribute as archieve for regular file */ + fill_dentry(mydata, empty_dentptr, filename, + start_cluster, size, 0x20); + + ret = set_contents(mydata, empty_dentptr, buffer, size); + if (ret) { + printf("Error: writing contents\n"); + goto exit; + } + + /* Flush fat buffer */ + ret = flush_fat_buffer(mydata); + if (ret) { + printf("Error: flush fat buffer\n"); + goto exit; + } + + /* Write directory table to device */ + ret = set_cluster(mydata, dir_curclust, + get_dentfromdir_block, + mydata->clust_size * mydata->sect_size); + if (ret) { + printf("Error: writing directory entry\n"); + goto exit; + } + } + +exit: + free(mydata->fatbuf); + return ret; +} + +int file_fat_write(const char *filename, void *buffer, unsigned long maxsize) +{ + printf("writing %s\n", filename); + return do_fat_write(filename, buffer, maxsize); +} diff --git a/fs/yaffs2/yaffs_guts.c b/fs/yaffs2/yaffs_guts.c index c67a08517b..c4329afbd5 100644 --- a/fs/yaffs2/yaffs_guts.c +++ b/fs/yaffs2/yaffs_guts.c @@ -78,8 +78,6 @@ static int yaffs_UpdateObjectHeader(yaffs_Object * in, const YCHAR * name, int force, int isShrink, int shadows); static void yaffs_RemoveObjectFromDirectory(yaffs_Object * obj); static int yaffs_CheckStructures(void); -static int yaffs_DeleteWorker(yaffs_Object * in, yaffs_Tnode * tn, __u32 level, - int chunkOffset, int *limit); static int yaffs_DoGenericObjectDeletion(yaffs_Object * in); static yaffs_BlockInfo *yaffs_GetBlockInfo(yaffs_Device * dev, int blockNo); @@ -595,55 +593,6 @@ static void yaffs_VerifyObjectHeader(yaffs_Object *obj, yaffs_ObjectHeader *oh, obj->objectId)); } - - -static int yaffs_VerifyTnodeWorker(yaffs_Object * obj, yaffs_Tnode * tn, - __u32 level, int chunkOffset) -{ - int i; - yaffs_Device *dev = obj->myDev; - int ok = 1; - - if (tn) { - if (level > 0) { - - for (i = 0; i < YAFFS_NTNODES_INTERNAL && ok; i++){ - if (tn->internal[i]) { - ok = yaffs_VerifyTnodeWorker(obj, - tn->internal[i], - level - 1, - (chunkOffset<<YAFFS_TNODES_INTERNAL_BITS) + i); - } - } - } else if (level == 0) { - int i; - yaffs_ExtendedTags tags; - __u32 objectId = obj->objectId; - - chunkOffset <<= YAFFS_TNODES_LEVEL0_BITS; - - for(i = 0; i < YAFFS_NTNODES_LEVEL0; i++){ - __u32 theChunk = yaffs_GetChunkGroupBase(dev,tn,i); - - if(theChunk > 0){ - /* T(~0,(TSTR("verifying (%d:%d) %d"TENDSTR),tags.objectId,tags.chunkId,theChunk)); */ - yaffs_ReadChunkWithTagsFromNAND(dev,theChunk,NULL, &tags); - if(tags.objectId != objectId || tags.chunkId != chunkOffset){ - T(~0,(TSTR("Object %d chunkId %d NAND mismatch chunk %d tags (%d:%d)"TENDSTR), - objectId, chunkOffset, theChunk, - tags.objectId, tags.chunkId)); - } - } - chunkOffset++; - } - } - } - - return ok; - -} - - static void yaffs_VerifyFile(yaffs_Object *obj) { int requiredTallness; @@ -1546,104 +1495,6 @@ static int yaffs_FindChunkInGroup(yaffs_Device * dev, int theChunk, return -1; } - -/* DeleteWorker scans backwards through the tnode tree and deletes all the - * chunks and tnodes in the file - * Returns 1 if the tree was deleted. - * Returns 0 if it stopped early due to hitting the limit and the delete is incomplete. - */ - -static int yaffs_DeleteWorker(yaffs_Object * in, yaffs_Tnode * tn, __u32 level, - int chunkOffset, int *limit) -{ - int i; - int chunkInInode; - int theChunk; - yaffs_ExtendedTags tags; - int foundChunk; - yaffs_Device *dev = in->myDev; - - int allDone = 1; - - if (tn) { - if (level > 0) { - - for (i = YAFFS_NTNODES_INTERNAL - 1; allDone && i >= 0; - i--) { - if (tn->internal[i]) { - if (limit && (*limit) < 0) { - allDone = 0; - } else { - allDone = - yaffs_DeleteWorker(in, - tn-> - internal - [i], - level - - 1, - (chunkOffset - << - YAFFS_TNODES_INTERNAL_BITS) - + i, - limit); - } - if (allDone) { - yaffs_FreeTnode(dev, - tn-> - internal[i]); - tn->internal[i] = NULL; - } - } - - } - return (allDone) ? 1 : 0; - } else if (level == 0) { - int hitLimit = 0; - - for (i = YAFFS_NTNODES_LEVEL0 - 1; i >= 0 && !hitLimit; - i--) { - theChunk = yaffs_GetChunkGroupBase(dev,tn,i); - if (theChunk) { - - chunkInInode = - (chunkOffset << - YAFFS_TNODES_LEVEL0_BITS) + i; - - foundChunk = - yaffs_FindChunkInGroup(dev, - theChunk, - &tags, - in->objectId, - chunkInInode); - - if (foundChunk > 0) { - yaffs_DeleteChunk(dev, - foundChunk, 1, - __LINE__); - in->nDataChunks--; - if (limit) { - *limit = *limit - 1; - if (*limit <= 0) { - hitLimit = 1; - } - } - - } - - yaffs_PutLevel0Tnode(dev,tn,i,0); - } - - } - return (i < 0) ? 1 : 0; - - } - - } - - return 1; - -} - static void yaffs_SoftDeleteChunk(yaffs_Device * dev, int chunk) { @@ -4605,8 +4456,8 @@ int yaffs_ReadDataFromFile(yaffs_Object * in, __u8 * buffer, loff_t offset, int nBytes) { - __u32 chunk; - __u32 start; + __u32 chunk = 0; + __u32 start = 0; int nToCopy; int n = nBytes; int nDone = 0; @@ -4725,8 +4576,8 @@ int yaffs_WriteDataToFile(yaffs_Object * in, const __u8 * buffer, loff_t offset, int nBytes, int writeThrough) { - __u32 chunk; - __u32 start; + __u32 chunk = 0; + __u32 start = 0; int nToCopy; int n = nBytes; int nDone = 0; @@ -4960,8 +4811,8 @@ int yaffs_ResizeFile(yaffs_Object * in, loff_t newSize) { int oldFileSize = in->variant.fileVariant.fileSize; - __u32 newSizeOfPartialChunk; - __u32 newFullChunks; + __u32 newSizeOfPartialChunk = 0; + __u32 newFullChunks = 0; yaffs_Device *dev = in->myDev; |