1c30a15e5SDonggeun Kim /* 2c30a15e5SDonggeun Kim * fat_write.c 3c30a15e5SDonggeun Kim * 4c30a15e5SDonggeun Kim * R/W (V)FAT 12/16/32 filesystem implementation by Donggeun Kim 5c30a15e5SDonggeun Kim * 6c30a15e5SDonggeun Kim * See file CREDITS for list of people who contributed to this 7c30a15e5SDonggeun Kim * project. 8c30a15e5SDonggeun Kim * 9c30a15e5SDonggeun Kim * This program is free software; you can redistribute it and/or 10c30a15e5SDonggeun Kim * modify it under the terms of the GNU General Public License as 11c30a15e5SDonggeun Kim * published by the Free Software Foundation; either version 2 of 12c30a15e5SDonggeun Kim * the License, or (at your option) any later version. 13c30a15e5SDonggeun Kim * 14c30a15e5SDonggeun Kim * This program is distributed in the hope that it will be useful, 15c30a15e5SDonggeun Kim * but WITHOUT ANY WARRANTY; without even the implied warranty of 16c30a15e5SDonggeun Kim * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17c30a15e5SDonggeun Kim * GNU General Public License for more details. 18c30a15e5SDonggeun Kim * 19c30a15e5SDonggeun Kim * You should have received a copy of the GNU General Public License 20c30a15e5SDonggeun Kim * along with this program; if not, write to the Free Software 21c30a15e5SDonggeun Kim * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 22c30a15e5SDonggeun Kim * MA 02111-1307 USA 23c30a15e5SDonggeun Kim */ 24c30a15e5SDonggeun Kim 25c30a15e5SDonggeun Kim #include <common.h> 26c30a15e5SDonggeun Kim #include <command.h> 27c30a15e5SDonggeun Kim #include <config.h> 28c30a15e5SDonggeun Kim #include <fat.h> 29c30a15e5SDonggeun Kim #include <asm/byteorder.h> 30c30a15e5SDonggeun Kim #include <part.h> 31c30a15e5SDonggeun Kim #include "fat.c" 32c30a15e5SDonggeun Kim 33c30a15e5SDonggeun Kim static void uppercase(char *str, int len) 34c30a15e5SDonggeun Kim { 35c30a15e5SDonggeun Kim int i; 36c30a15e5SDonggeun Kim 37c30a15e5SDonggeun Kim for (i = 0; i < len; i++) { 38c30a15e5SDonggeun Kim TOUPPER(*str); 39c30a15e5SDonggeun Kim str++; 40c30a15e5SDonggeun Kim } 41c30a15e5SDonggeun Kim } 42c30a15e5SDonggeun Kim 43c30a15e5SDonggeun Kim static int total_sector; 44c30a15e5SDonggeun Kim static int disk_write(__u32 startblock, __u32 getsize, __u8 *bufptr) 45c30a15e5SDonggeun Kim { 46c30a15e5SDonggeun Kim if (cur_dev == NULL) 47c30a15e5SDonggeun Kim return -1; 48c30a15e5SDonggeun Kim 49c30a15e5SDonggeun Kim if (startblock + getsize > total_sector) { 50c30a15e5SDonggeun Kim printf("error: overflow occurs\n"); 51c30a15e5SDonggeun Kim return -1; 52c30a15e5SDonggeun Kim } 53c30a15e5SDonggeun Kim 54c30a15e5SDonggeun Kim startblock += part_offset; 55c30a15e5SDonggeun Kim 56c30a15e5SDonggeun Kim if (cur_dev->block_read) { 57c30a15e5SDonggeun Kim return cur_dev->block_write(cur_dev->dev, startblock, getsize, 58c30a15e5SDonggeun Kim (unsigned long *) bufptr); 59c30a15e5SDonggeun Kim } 60c30a15e5SDonggeun Kim return -1; 61c30a15e5SDonggeun Kim } 62c30a15e5SDonggeun Kim 63c30a15e5SDonggeun Kim /* 64c30a15e5SDonggeun Kim * Set short name in directory entry 65c30a15e5SDonggeun Kim */ 66c30a15e5SDonggeun Kim static void set_name(dir_entry *dirent, const char *filename) 67c30a15e5SDonggeun Kim { 68c30a15e5SDonggeun Kim char s_name[VFAT_MAXLEN_BYTES]; 69c30a15e5SDonggeun Kim char *period; 70c30a15e5SDonggeun Kim int period_location, len, i, ext_num; 71c30a15e5SDonggeun Kim 72c30a15e5SDonggeun Kim if (filename == NULL) 73c30a15e5SDonggeun Kim return; 74c30a15e5SDonggeun Kim 75c30a15e5SDonggeun Kim len = strlen(filename); 76c30a15e5SDonggeun Kim if (len == 0) 77c30a15e5SDonggeun Kim return; 78c30a15e5SDonggeun Kim 79c30a15e5SDonggeun Kim memcpy(s_name, filename, len); 80c30a15e5SDonggeun Kim uppercase(s_name, len); 81c30a15e5SDonggeun Kim 82c30a15e5SDonggeun Kim period = strchr(s_name, '.'); 83c30a15e5SDonggeun Kim if (period == NULL) { 84c30a15e5SDonggeun Kim period_location = len; 85c30a15e5SDonggeun Kim ext_num = 0; 86c30a15e5SDonggeun Kim } else { 87c30a15e5SDonggeun Kim period_location = period - s_name; 88c30a15e5SDonggeun Kim ext_num = len - period_location - 1; 89c30a15e5SDonggeun Kim } 90c30a15e5SDonggeun Kim 91c30a15e5SDonggeun Kim /* Pad spaces when the length of file name is shorter than eight */ 92c30a15e5SDonggeun Kim if (period_location < 8) { 93c30a15e5SDonggeun Kim memcpy(dirent->name, s_name, period_location); 94c30a15e5SDonggeun Kim for (i = period_location; i < 8; i++) 95c30a15e5SDonggeun Kim dirent->name[i] = ' '; 96c30a15e5SDonggeun Kim } else if (period_location == 8) { 97c30a15e5SDonggeun Kim memcpy(dirent->name, s_name, period_location); 98c30a15e5SDonggeun Kim } else { 99c30a15e5SDonggeun Kim memcpy(dirent->name, s_name, 6); 100c30a15e5SDonggeun Kim dirent->name[6] = '~'; 101c30a15e5SDonggeun Kim dirent->name[7] = '1'; 102c30a15e5SDonggeun Kim } 103c30a15e5SDonggeun Kim 104c30a15e5SDonggeun Kim if (ext_num < 3) { 105c30a15e5SDonggeun Kim memcpy(dirent->ext, s_name + period_location + 1, ext_num); 106c30a15e5SDonggeun Kim for (i = ext_num; i < 3; i++) 107c30a15e5SDonggeun Kim dirent->ext[i] = ' '; 108c30a15e5SDonggeun Kim } else 109c30a15e5SDonggeun Kim memcpy(dirent->ext, s_name + period_location + 1, 3); 110c30a15e5SDonggeun Kim 111c30a15e5SDonggeun Kim debug("name : %s\n", dirent->name); 112c30a15e5SDonggeun Kim debug("ext : %s\n", dirent->ext); 113c30a15e5SDonggeun Kim } 114c30a15e5SDonggeun Kim 115*627182eaSDonggeun Kim static __u8 num_of_fats; 116c30a15e5SDonggeun Kim /* 117c30a15e5SDonggeun Kim * Write fat buffer into block device 118c30a15e5SDonggeun Kim */ 119c30a15e5SDonggeun Kim static int flush_fat_buffer(fsdata *mydata) 120c30a15e5SDonggeun Kim { 121c30a15e5SDonggeun Kim int getsize = FATBUFBLOCKS; 122c30a15e5SDonggeun Kim __u32 fatlength = mydata->fatlength; 123c30a15e5SDonggeun Kim __u8 *bufptr = mydata->fatbuf; 124c30a15e5SDonggeun Kim __u32 startblock = mydata->fatbufnum * FATBUFBLOCKS; 125c30a15e5SDonggeun Kim 126c30a15e5SDonggeun Kim fatlength *= mydata->sect_size; 127c30a15e5SDonggeun Kim startblock += mydata->fat_sect; 128c30a15e5SDonggeun Kim 129c30a15e5SDonggeun Kim if (getsize > fatlength) 130c30a15e5SDonggeun Kim getsize = fatlength; 131c30a15e5SDonggeun Kim 132c30a15e5SDonggeun Kim /* Write FAT buf */ 133c30a15e5SDonggeun Kim if (disk_write(startblock, getsize, bufptr) < 0) { 134c30a15e5SDonggeun Kim debug("error: writing FAT blocks\n"); 135c30a15e5SDonggeun Kim return -1; 136c30a15e5SDonggeun Kim } 137c30a15e5SDonggeun Kim 138*627182eaSDonggeun Kim if (num_of_fats == 2) { 139*627182eaSDonggeun Kim /* Update corresponding second FAT blocks */ 140*627182eaSDonggeun Kim startblock += mydata->fatlength; 141*627182eaSDonggeun Kim if (disk_write(startblock, getsize, bufptr) < 0) { 142*627182eaSDonggeun Kim debug("error: writing second FAT blocks\n"); 143*627182eaSDonggeun Kim return -1; 144*627182eaSDonggeun Kim } 145*627182eaSDonggeun Kim } 146*627182eaSDonggeun Kim 147c30a15e5SDonggeun Kim return 0; 148c30a15e5SDonggeun Kim } 149c30a15e5SDonggeun Kim 150c30a15e5SDonggeun Kim /* 151c30a15e5SDonggeun Kim * Get the entry at index 'entry' in a FAT (12/16/32) table. 152c30a15e5SDonggeun Kim * On failure 0x00 is returned. 153c30a15e5SDonggeun Kim * When bufnum is changed, write back the previous fatbuf to the disk. 154c30a15e5SDonggeun Kim */ 155c30a15e5SDonggeun Kim static __u32 get_fatent_value(fsdata *mydata, __u32 entry) 156c30a15e5SDonggeun Kim { 157c30a15e5SDonggeun Kim __u32 bufnum; 158c30a15e5SDonggeun Kim __u32 off16, offset; 159c30a15e5SDonggeun Kim __u32 ret = 0x00; 160c30a15e5SDonggeun Kim __u16 val1, val2; 161c30a15e5SDonggeun Kim 162c30a15e5SDonggeun Kim switch (mydata->fatsize) { 163c30a15e5SDonggeun Kim case 32: 164c30a15e5SDonggeun Kim bufnum = entry / FAT32BUFSIZE; 165c30a15e5SDonggeun Kim offset = entry - bufnum * FAT32BUFSIZE; 166c30a15e5SDonggeun Kim break; 167c30a15e5SDonggeun Kim case 16: 168c30a15e5SDonggeun Kim bufnum = entry / FAT16BUFSIZE; 169c30a15e5SDonggeun Kim offset = entry - bufnum * FAT16BUFSIZE; 170c30a15e5SDonggeun Kim break; 171c30a15e5SDonggeun Kim case 12: 172c30a15e5SDonggeun Kim bufnum = entry / FAT12BUFSIZE; 173c30a15e5SDonggeun Kim offset = entry - bufnum * FAT12BUFSIZE; 174c30a15e5SDonggeun Kim break; 175c30a15e5SDonggeun Kim 176c30a15e5SDonggeun Kim default: 177c30a15e5SDonggeun Kim /* Unsupported FAT size */ 178c30a15e5SDonggeun Kim return ret; 179c30a15e5SDonggeun Kim } 180c30a15e5SDonggeun Kim 181c30a15e5SDonggeun Kim debug("FAT%d: entry: 0x%04x = %d, offset: 0x%04x = %d\n", 182c30a15e5SDonggeun Kim mydata->fatsize, entry, entry, offset, offset); 183c30a15e5SDonggeun Kim 184c30a15e5SDonggeun Kim /* Read a new block of FAT entries into the cache. */ 185c30a15e5SDonggeun Kim if (bufnum != mydata->fatbufnum) { 186c30a15e5SDonggeun Kim int getsize = FATBUFBLOCKS; 187c30a15e5SDonggeun Kim __u8 *bufptr = mydata->fatbuf; 188c30a15e5SDonggeun Kim __u32 fatlength = mydata->fatlength; 189c30a15e5SDonggeun Kim __u32 startblock = bufnum * FATBUFBLOCKS; 190c30a15e5SDonggeun Kim 191c30a15e5SDonggeun Kim if (getsize > fatlength) 192c30a15e5SDonggeun Kim getsize = fatlength; 193c30a15e5SDonggeun Kim 194c30a15e5SDonggeun Kim fatlength *= mydata->sect_size; /* We want it in bytes now */ 195c30a15e5SDonggeun Kim startblock += mydata->fat_sect; /* Offset from start of disk */ 196c30a15e5SDonggeun Kim 197c30a15e5SDonggeun Kim /* Write back the fatbuf to the disk */ 198c30a15e5SDonggeun Kim if (mydata->fatbufnum != -1) { 199c30a15e5SDonggeun Kim if (flush_fat_buffer(mydata) < 0) 200c30a15e5SDonggeun Kim return -1; 201c30a15e5SDonggeun Kim } 202c30a15e5SDonggeun Kim 203c30a15e5SDonggeun Kim if (disk_read(startblock, getsize, bufptr) < 0) { 204c30a15e5SDonggeun Kim debug("Error reading FAT blocks\n"); 205c30a15e5SDonggeun Kim return ret; 206c30a15e5SDonggeun Kim } 207c30a15e5SDonggeun Kim mydata->fatbufnum = bufnum; 208c30a15e5SDonggeun Kim } 209c30a15e5SDonggeun Kim 210c30a15e5SDonggeun Kim /* Get the actual entry from the table */ 211c30a15e5SDonggeun Kim switch (mydata->fatsize) { 212c30a15e5SDonggeun Kim case 32: 213c30a15e5SDonggeun Kim ret = FAT2CPU32(((__u32 *) mydata->fatbuf)[offset]); 214c30a15e5SDonggeun Kim break; 215c30a15e5SDonggeun Kim case 16: 216c30a15e5SDonggeun Kim ret = FAT2CPU16(((__u16 *) mydata->fatbuf)[offset]); 217c30a15e5SDonggeun Kim break; 218c30a15e5SDonggeun Kim case 12: 219c30a15e5SDonggeun Kim off16 = (offset * 3) / 4; 220c30a15e5SDonggeun Kim 221c30a15e5SDonggeun Kim switch (offset & 0x3) { 222c30a15e5SDonggeun Kim case 0: 223c30a15e5SDonggeun Kim ret = FAT2CPU16(((__u16 *) mydata->fatbuf)[off16]); 224c30a15e5SDonggeun Kim ret &= 0xfff; 225c30a15e5SDonggeun Kim break; 226c30a15e5SDonggeun Kim case 1: 227c30a15e5SDonggeun Kim val1 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]); 228c30a15e5SDonggeun Kim val1 &= 0xf000; 229c30a15e5SDonggeun Kim val2 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16 + 1]); 230c30a15e5SDonggeun Kim val2 &= 0x00ff; 231c30a15e5SDonggeun Kim ret = (val2 << 4) | (val1 >> 12); 232c30a15e5SDonggeun Kim break; 233c30a15e5SDonggeun Kim case 2: 234c30a15e5SDonggeun Kim val1 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]); 235c30a15e5SDonggeun Kim val1 &= 0xff00; 236c30a15e5SDonggeun Kim val2 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16 + 1]); 237c30a15e5SDonggeun Kim val2 &= 0x000f; 238c30a15e5SDonggeun Kim ret = (val2 << 8) | (val1 >> 8); 239c30a15e5SDonggeun Kim break; 240c30a15e5SDonggeun Kim case 3: 241c30a15e5SDonggeun Kim ret = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]); 242c30a15e5SDonggeun Kim ret = (ret & 0xfff0) >> 4; 243c30a15e5SDonggeun Kim break; 244c30a15e5SDonggeun Kim default: 245c30a15e5SDonggeun Kim break; 246c30a15e5SDonggeun Kim } 247c30a15e5SDonggeun Kim break; 248c30a15e5SDonggeun Kim } 249c30a15e5SDonggeun Kim debug("FAT%d: ret: %08x, entry: %08x, offset: %04x\n", 250c30a15e5SDonggeun Kim mydata->fatsize, ret, entry, offset); 251c30a15e5SDonggeun Kim 252c30a15e5SDonggeun Kim return ret; 253c30a15e5SDonggeun Kim } 254c30a15e5SDonggeun Kim 255c30a15e5SDonggeun Kim #ifdef CONFIG_SUPPORT_VFAT 256c30a15e5SDonggeun Kim /* 257c30a15e5SDonggeun Kim * Set the file name information from 'name' into 'slotptr', 258c30a15e5SDonggeun Kim */ 259c30a15e5SDonggeun Kim static int str2slot(dir_slot *slotptr, const char *name, int *idx) 260c30a15e5SDonggeun Kim { 261c30a15e5SDonggeun Kim int j, end_idx = 0; 262c30a15e5SDonggeun Kim 263c30a15e5SDonggeun Kim for (j = 0; j <= 8; j += 2) { 264c30a15e5SDonggeun Kim if (name[*idx] == 0x00) { 265c30a15e5SDonggeun Kim slotptr->name0_4[j] = 0; 266c30a15e5SDonggeun Kim slotptr->name0_4[j + 1] = 0; 267c30a15e5SDonggeun Kim end_idx++; 268c30a15e5SDonggeun Kim goto name0_4; 269c30a15e5SDonggeun Kim } 270c30a15e5SDonggeun Kim slotptr->name0_4[j] = name[*idx]; 271c30a15e5SDonggeun Kim (*idx)++; 272c30a15e5SDonggeun Kim end_idx++; 273c30a15e5SDonggeun Kim } 274c30a15e5SDonggeun Kim for (j = 0; j <= 10; j += 2) { 275c30a15e5SDonggeun Kim if (name[*idx] == 0x00) { 276c30a15e5SDonggeun Kim slotptr->name5_10[j] = 0; 277c30a15e5SDonggeun Kim slotptr->name5_10[j + 1] = 0; 278c30a15e5SDonggeun Kim end_idx++; 279c30a15e5SDonggeun Kim goto name5_10; 280c30a15e5SDonggeun Kim } 281c30a15e5SDonggeun Kim slotptr->name5_10[j] = name[*idx]; 282c30a15e5SDonggeun Kim (*idx)++; 283c30a15e5SDonggeun Kim end_idx++; 284c30a15e5SDonggeun Kim } 285c30a15e5SDonggeun Kim for (j = 0; j <= 2; j += 2) { 286c30a15e5SDonggeun Kim if (name[*idx] == 0x00) { 287c30a15e5SDonggeun Kim slotptr->name11_12[j] = 0; 288c30a15e5SDonggeun Kim slotptr->name11_12[j + 1] = 0; 289c30a15e5SDonggeun Kim end_idx++; 290c30a15e5SDonggeun Kim goto name11_12; 291c30a15e5SDonggeun Kim } 292c30a15e5SDonggeun Kim slotptr->name11_12[j] = name[*idx]; 293c30a15e5SDonggeun Kim (*idx)++; 294c30a15e5SDonggeun Kim end_idx++; 295c30a15e5SDonggeun Kim } 296c30a15e5SDonggeun Kim 297c30a15e5SDonggeun Kim if (name[*idx] == 0x00) 298c30a15e5SDonggeun Kim return 1; 299c30a15e5SDonggeun Kim 300c30a15e5SDonggeun Kim return 0; 301c30a15e5SDonggeun Kim /* Not used characters are filled with 0xff 0xff */ 302c30a15e5SDonggeun Kim name0_4: 303c30a15e5SDonggeun Kim for (; end_idx < 5; end_idx++) { 304c30a15e5SDonggeun Kim slotptr->name0_4[end_idx * 2] = 0xff; 305c30a15e5SDonggeun Kim slotptr->name0_4[end_idx * 2 + 1] = 0xff; 306c30a15e5SDonggeun Kim } 307c30a15e5SDonggeun Kim end_idx = 5; 308c30a15e5SDonggeun Kim name5_10: 309c30a15e5SDonggeun Kim end_idx -= 5; 310c30a15e5SDonggeun Kim for (; end_idx < 6; end_idx++) { 311c30a15e5SDonggeun Kim slotptr->name5_10[end_idx * 2] = 0xff; 312c30a15e5SDonggeun Kim slotptr->name5_10[end_idx * 2 + 1] = 0xff; 313c30a15e5SDonggeun Kim } 314c30a15e5SDonggeun Kim end_idx = 11; 315c30a15e5SDonggeun Kim name11_12: 316c30a15e5SDonggeun Kim end_idx -= 11; 317c30a15e5SDonggeun Kim for (; end_idx < 2; end_idx++) { 318c30a15e5SDonggeun Kim slotptr->name11_12[end_idx * 2] = 0xff; 319c30a15e5SDonggeun Kim slotptr->name11_12[end_idx * 2 + 1] = 0xff; 320c30a15e5SDonggeun Kim } 321c30a15e5SDonggeun Kim 322c30a15e5SDonggeun Kim return 1; 323c30a15e5SDonggeun Kim } 324c30a15e5SDonggeun Kim 325c30a15e5SDonggeun Kim static int is_next_clust(fsdata *mydata, dir_entry *dentptr); 326c30a15e5SDonggeun Kim static void flush_dir_table(fsdata *mydata, dir_entry **dentptr); 327c30a15e5SDonggeun Kim 328c30a15e5SDonggeun Kim /* 329c30a15e5SDonggeun Kim * Fill dir_slot entries with appropriate name, id, and attr 330c30a15e5SDonggeun Kim * The real directory entry is returned by 'dentptr' 331c30a15e5SDonggeun Kim */ 332c30a15e5SDonggeun Kim static void 333c30a15e5SDonggeun Kim fill_dir_slot(fsdata *mydata, dir_entry **dentptr, const char *l_name) 334c30a15e5SDonggeun Kim { 335c30a15e5SDonggeun Kim dir_slot *slotptr = (dir_slot *)get_vfatname_block; 3368506eb8dSAnatolij Gustschin __u8 counter = 0, checksum; 337c30a15e5SDonggeun Kim int idx = 0, ret; 338c30a15e5SDonggeun Kim char s_name[16]; 339c30a15e5SDonggeun Kim 340c30a15e5SDonggeun Kim /* Get short file name and checksum value */ 341c30a15e5SDonggeun Kim strncpy(s_name, (*dentptr)->name, 16); 342c30a15e5SDonggeun Kim checksum = mkcksum(s_name); 343c30a15e5SDonggeun Kim 344c30a15e5SDonggeun Kim do { 345c30a15e5SDonggeun Kim memset(slotptr, 0x00, sizeof(dir_slot)); 346c30a15e5SDonggeun Kim ret = str2slot(slotptr, l_name, &idx); 347c30a15e5SDonggeun Kim slotptr->id = ++counter; 348c30a15e5SDonggeun Kim slotptr->attr = ATTR_VFAT; 349c30a15e5SDonggeun Kim slotptr->alias_checksum = checksum; 350c30a15e5SDonggeun Kim slotptr++; 351c30a15e5SDonggeun Kim } while (ret == 0); 352c30a15e5SDonggeun Kim 353c30a15e5SDonggeun Kim slotptr--; 354c30a15e5SDonggeun Kim slotptr->id |= LAST_LONG_ENTRY_MASK; 355c30a15e5SDonggeun Kim 356c30a15e5SDonggeun Kim while (counter >= 1) { 357c30a15e5SDonggeun Kim if (is_next_clust(mydata, *dentptr)) { 358c30a15e5SDonggeun Kim /* A new cluster is allocated for directory table */ 359c30a15e5SDonggeun Kim flush_dir_table(mydata, dentptr); 360c30a15e5SDonggeun Kim } 361c30a15e5SDonggeun Kim memcpy(*dentptr, slotptr, sizeof(dir_slot)); 362c30a15e5SDonggeun Kim (*dentptr)++; 363c30a15e5SDonggeun Kim slotptr--; 364c30a15e5SDonggeun Kim counter--; 365c30a15e5SDonggeun Kim } 366c30a15e5SDonggeun Kim 367c30a15e5SDonggeun Kim if (is_next_clust(mydata, *dentptr)) { 368c30a15e5SDonggeun Kim /* A new cluster is allocated for directory table */ 369c30a15e5SDonggeun Kim flush_dir_table(mydata, dentptr); 370c30a15e5SDonggeun Kim } 371c30a15e5SDonggeun Kim } 372c30a15e5SDonggeun Kim 373c30a15e5SDonggeun Kim static __u32 dir_curclust; 374c30a15e5SDonggeun Kim 375c30a15e5SDonggeun Kim /* 376c30a15e5SDonggeun Kim * Extract the full long filename starting at 'retdent' (which is really 377c30a15e5SDonggeun Kim * a slot) into 'l_name'. If successful also copy the real directory entry 378c30a15e5SDonggeun Kim * into 'retdent' 379c30a15e5SDonggeun Kim * If additional adjacent cluster for directory entries is read into memory, 380c30a15e5SDonggeun Kim * then 'get_vfatname_block' is copied into 'get_dentfromdir_block' and 381c30a15e5SDonggeun Kim * the location of the real directory entry is returned by 'retdent' 382c30a15e5SDonggeun Kim * Return 0 on success, -1 otherwise. 383c30a15e5SDonggeun Kim */ 384c30a15e5SDonggeun Kim static int 385c30a15e5SDonggeun Kim get_long_file_name(fsdata *mydata, int curclust, __u8 *cluster, 386c30a15e5SDonggeun Kim dir_entry **retdent, char *l_name) 387c30a15e5SDonggeun Kim { 388c30a15e5SDonggeun Kim dir_entry *realdent; 389c30a15e5SDonggeun Kim dir_slot *slotptr = (dir_slot *)(*retdent); 390c30a15e5SDonggeun Kim dir_slot *slotptr2 = NULL; 391c30a15e5SDonggeun Kim __u8 *buflimit = cluster + mydata->sect_size * ((curclust == 0) ? 392c30a15e5SDonggeun Kim PREFETCH_BLOCKS : 393c30a15e5SDonggeun Kim mydata->clust_size); 394c30a15e5SDonggeun Kim __u8 counter = (slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff; 395c30a15e5SDonggeun Kim int idx = 0, cur_position = 0; 396c30a15e5SDonggeun Kim 397c30a15e5SDonggeun Kim if (counter > VFAT_MAXSEQ) { 398c30a15e5SDonggeun Kim debug("Error: VFAT name is too long\n"); 399c30a15e5SDonggeun Kim return -1; 400c30a15e5SDonggeun Kim } 401c30a15e5SDonggeun Kim 402c30a15e5SDonggeun Kim while ((__u8 *)slotptr < buflimit) { 403c30a15e5SDonggeun Kim if (counter == 0) 404c30a15e5SDonggeun Kim break; 405c30a15e5SDonggeun Kim if (((slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff) != counter) 406c30a15e5SDonggeun Kim return -1; 407c30a15e5SDonggeun Kim slotptr++; 408c30a15e5SDonggeun Kim counter--; 409c30a15e5SDonggeun Kim } 410c30a15e5SDonggeun Kim 411c30a15e5SDonggeun Kim if ((__u8 *)slotptr >= buflimit) { 412c30a15e5SDonggeun Kim if (curclust == 0) 413c30a15e5SDonggeun Kim return -1; 414c30a15e5SDonggeun Kim curclust = get_fatent_value(mydata, dir_curclust); 415c30a15e5SDonggeun Kim if (CHECK_CLUST(curclust, mydata->fatsize)) { 416c30a15e5SDonggeun Kim debug("curclust: 0x%x\n", curclust); 417c30a15e5SDonggeun Kim printf("Invalid FAT entry\n"); 418c30a15e5SDonggeun Kim return -1; 419c30a15e5SDonggeun Kim } 420c30a15e5SDonggeun Kim 421c30a15e5SDonggeun Kim dir_curclust = curclust; 422c30a15e5SDonggeun Kim 423c30a15e5SDonggeun Kim if (get_cluster(mydata, curclust, get_vfatname_block, 424c30a15e5SDonggeun Kim mydata->clust_size * mydata->sect_size) != 0) { 425c30a15e5SDonggeun Kim debug("Error: reading directory block\n"); 426c30a15e5SDonggeun Kim return -1; 427c30a15e5SDonggeun Kim } 428c30a15e5SDonggeun Kim 429c30a15e5SDonggeun Kim slotptr2 = (dir_slot *)get_vfatname_block; 430c30a15e5SDonggeun Kim while (counter > 0) { 431c30a15e5SDonggeun Kim if (((slotptr2->id & ~LAST_LONG_ENTRY_MASK) 432c30a15e5SDonggeun Kim & 0xff) != counter) 433c30a15e5SDonggeun Kim return -1; 434c30a15e5SDonggeun Kim slotptr2++; 435c30a15e5SDonggeun Kim counter--; 436c30a15e5SDonggeun Kim } 437c30a15e5SDonggeun Kim 438c30a15e5SDonggeun Kim /* Save the real directory entry */ 439c30a15e5SDonggeun Kim realdent = (dir_entry *)slotptr2; 440c30a15e5SDonggeun Kim while ((__u8 *)slotptr2 > get_vfatname_block) { 441c30a15e5SDonggeun Kim slotptr2--; 442c30a15e5SDonggeun Kim slot2str(slotptr2, l_name, &idx); 443c30a15e5SDonggeun Kim } 444c30a15e5SDonggeun Kim } else { 445c30a15e5SDonggeun Kim /* Save the real directory entry */ 446c30a15e5SDonggeun Kim realdent = (dir_entry *)slotptr; 447c30a15e5SDonggeun Kim } 448c30a15e5SDonggeun Kim 449c30a15e5SDonggeun Kim do { 450c30a15e5SDonggeun Kim slotptr--; 451c30a15e5SDonggeun Kim if (slot2str(slotptr, l_name, &idx)) 452c30a15e5SDonggeun Kim break; 453c30a15e5SDonggeun Kim } while (!(slotptr->id & LAST_LONG_ENTRY_MASK)); 454c30a15e5SDonggeun Kim 455c30a15e5SDonggeun Kim l_name[idx] = '\0'; 456c30a15e5SDonggeun Kim if (*l_name == DELETED_FLAG) 457c30a15e5SDonggeun Kim *l_name = '\0'; 458c30a15e5SDonggeun Kim else if (*l_name == aRING) 459c30a15e5SDonggeun Kim *l_name = DELETED_FLAG; 460c30a15e5SDonggeun Kim downcase(l_name); 461c30a15e5SDonggeun Kim 462c30a15e5SDonggeun Kim /* Return the real directory entry */ 463c30a15e5SDonggeun Kim *retdent = realdent; 464c30a15e5SDonggeun Kim 465c30a15e5SDonggeun Kim if (slotptr2) { 466c30a15e5SDonggeun Kim memcpy(get_dentfromdir_block, get_vfatname_block, 467c30a15e5SDonggeun Kim mydata->clust_size * mydata->sect_size); 468c30a15e5SDonggeun Kim cur_position = (__u8 *)realdent - get_vfatname_block; 469c30a15e5SDonggeun Kim *retdent = (dir_entry *) &get_dentfromdir_block[cur_position]; 470c30a15e5SDonggeun Kim } 471c30a15e5SDonggeun Kim 472c30a15e5SDonggeun Kim return 0; 473c30a15e5SDonggeun Kim } 474c30a15e5SDonggeun Kim 475c30a15e5SDonggeun Kim #endif 476c30a15e5SDonggeun Kim 477c30a15e5SDonggeun Kim /* 478c30a15e5SDonggeun Kim * Set the entry at index 'entry' in a FAT (16/32) table. 479c30a15e5SDonggeun Kim */ 480c30a15e5SDonggeun Kim static int set_fatent_value(fsdata *mydata, __u32 entry, __u32 entry_value) 481c30a15e5SDonggeun Kim { 482c30a15e5SDonggeun Kim __u32 bufnum, offset; 483c30a15e5SDonggeun Kim 484c30a15e5SDonggeun Kim switch (mydata->fatsize) { 485c30a15e5SDonggeun Kim case 32: 486c30a15e5SDonggeun Kim bufnum = entry / FAT32BUFSIZE; 487c30a15e5SDonggeun Kim offset = entry - bufnum * FAT32BUFSIZE; 488c30a15e5SDonggeun Kim break; 489c30a15e5SDonggeun Kim case 16: 490c30a15e5SDonggeun Kim bufnum = entry / FAT16BUFSIZE; 491c30a15e5SDonggeun Kim offset = entry - bufnum * FAT16BUFSIZE; 492c30a15e5SDonggeun Kim break; 493c30a15e5SDonggeun Kim default: 494c30a15e5SDonggeun Kim /* Unsupported FAT size */ 495c30a15e5SDonggeun Kim return -1; 496c30a15e5SDonggeun Kim } 497c30a15e5SDonggeun Kim 498c30a15e5SDonggeun Kim /* Read a new block of FAT entries into the cache. */ 499c30a15e5SDonggeun Kim if (bufnum != mydata->fatbufnum) { 500c30a15e5SDonggeun Kim int getsize = FATBUFBLOCKS; 501c30a15e5SDonggeun Kim __u8 *bufptr = mydata->fatbuf; 502c30a15e5SDonggeun Kim __u32 fatlength = mydata->fatlength; 503c30a15e5SDonggeun Kim __u32 startblock = bufnum * FATBUFBLOCKS; 504c30a15e5SDonggeun Kim 505c30a15e5SDonggeun Kim fatlength *= mydata->sect_size; 506c30a15e5SDonggeun Kim startblock += mydata->fat_sect; 507c30a15e5SDonggeun Kim 508c30a15e5SDonggeun Kim if (getsize > fatlength) 509c30a15e5SDonggeun Kim getsize = fatlength; 510c30a15e5SDonggeun Kim 511c30a15e5SDonggeun Kim if (mydata->fatbufnum != -1) { 512c30a15e5SDonggeun Kim if (flush_fat_buffer(mydata) < 0) 513c30a15e5SDonggeun Kim return -1; 514c30a15e5SDonggeun Kim } 515c30a15e5SDonggeun Kim 516c30a15e5SDonggeun Kim if (disk_read(startblock, getsize, bufptr) < 0) { 517c30a15e5SDonggeun Kim debug("Error reading FAT blocks\n"); 518c30a15e5SDonggeun Kim return -1; 519c30a15e5SDonggeun Kim } 520c30a15e5SDonggeun Kim mydata->fatbufnum = bufnum; 521c30a15e5SDonggeun Kim } 522c30a15e5SDonggeun Kim 523c30a15e5SDonggeun Kim /* Set the actual entry */ 524c30a15e5SDonggeun Kim switch (mydata->fatsize) { 525c30a15e5SDonggeun Kim case 32: 526c30a15e5SDonggeun Kim ((__u32 *) mydata->fatbuf)[offset] = cpu_to_le32(entry_value); 527c30a15e5SDonggeun Kim break; 528c30a15e5SDonggeun Kim case 16: 529c30a15e5SDonggeun Kim ((__u16 *) mydata->fatbuf)[offset] = cpu_to_le16(entry_value); 530c30a15e5SDonggeun Kim break; 531c30a15e5SDonggeun Kim default: 532c30a15e5SDonggeun Kim return -1; 533c30a15e5SDonggeun Kim } 534c30a15e5SDonggeun Kim 535c30a15e5SDonggeun Kim return 0; 536c30a15e5SDonggeun Kim } 537c30a15e5SDonggeun Kim 538c30a15e5SDonggeun Kim /* 539c30a15e5SDonggeun Kim * Determine the entry value at index 'entry' in a FAT (16/32) table 540c30a15e5SDonggeun Kim */ 541c30a15e5SDonggeun Kim static __u32 determine_fatent(fsdata *mydata, __u32 entry) 542c30a15e5SDonggeun Kim { 543c30a15e5SDonggeun Kim __u32 next_fat, next_entry = entry + 1; 544c30a15e5SDonggeun Kim 545c30a15e5SDonggeun Kim while (1) { 546c30a15e5SDonggeun Kim next_fat = get_fatent_value(mydata, next_entry); 547c30a15e5SDonggeun Kim if (next_fat == 0) { 548c30a15e5SDonggeun Kim set_fatent_value(mydata, entry, next_entry); 549c30a15e5SDonggeun Kim break; 550c30a15e5SDonggeun Kim } 551c30a15e5SDonggeun Kim next_entry++; 552c30a15e5SDonggeun Kim } 553c30a15e5SDonggeun Kim debug("FAT%d: entry: %08x, entry_value: %04x\n", 554c30a15e5SDonggeun Kim mydata->fatsize, entry, next_entry); 555c30a15e5SDonggeun Kim 556c30a15e5SDonggeun Kim return next_entry; 557c30a15e5SDonggeun Kim } 558c30a15e5SDonggeun Kim 559c30a15e5SDonggeun Kim /* 560c30a15e5SDonggeun Kim * Write at most 'size' bytes from 'buffer' into the specified cluster. 561c30a15e5SDonggeun Kim * Return 0 on success, -1 otherwise. 562c30a15e5SDonggeun Kim */ 563c30a15e5SDonggeun Kim static int 564c30a15e5SDonggeun Kim set_cluster(fsdata *mydata, __u32 clustnum, __u8 *buffer, 565c30a15e5SDonggeun Kim unsigned long size) 566c30a15e5SDonggeun Kim { 567c30a15e5SDonggeun Kim int idx = 0; 568c30a15e5SDonggeun Kim __u32 startsect; 569c30a15e5SDonggeun Kim 570c30a15e5SDonggeun Kim if (clustnum > 0) 571c30a15e5SDonggeun Kim startsect = mydata->data_begin + 572c30a15e5SDonggeun Kim clustnum * mydata->clust_size; 573c30a15e5SDonggeun Kim else 574c30a15e5SDonggeun Kim startsect = mydata->rootdir_sect; 575c30a15e5SDonggeun Kim 576c30a15e5SDonggeun Kim debug("clustnum: %d, startsect: %d\n", clustnum, startsect); 577c30a15e5SDonggeun Kim 578c30a15e5SDonggeun Kim if (disk_write(startsect, size / mydata->sect_size, buffer) < 0) { 579c30a15e5SDonggeun Kim debug("Error writing data\n"); 580c30a15e5SDonggeun Kim return -1; 581c30a15e5SDonggeun Kim } 582c30a15e5SDonggeun Kim 583c30a15e5SDonggeun Kim if (size % mydata->sect_size) { 584c30a15e5SDonggeun Kim __u8 tmpbuf[mydata->sect_size]; 585c30a15e5SDonggeun Kim 586c30a15e5SDonggeun Kim idx = size / mydata->sect_size; 587c30a15e5SDonggeun Kim buffer += idx * mydata->sect_size; 588c30a15e5SDonggeun Kim memcpy(tmpbuf, buffer, size % mydata->sect_size); 589c30a15e5SDonggeun Kim 590c30a15e5SDonggeun Kim if (disk_write(startsect + idx, 1, tmpbuf) < 0) { 591c30a15e5SDonggeun Kim debug("Error writing data\n"); 592c30a15e5SDonggeun Kim return -1; 593c30a15e5SDonggeun Kim } 594c30a15e5SDonggeun Kim 595c30a15e5SDonggeun Kim return 0; 596c30a15e5SDonggeun Kim } 597c30a15e5SDonggeun Kim 598c30a15e5SDonggeun Kim return 0; 599c30a15e5SDonggeun Kim } 600c30a15e5SDonggeun Kim 601c30a15e5SDonggeun Kim /* 602c30a15e5SDonggeun Kim * Find the first empty cluster 603c30a15e5SDonggeun Kim */ 604c30a15e5SDonggeun Kim static int find_empty_cluster(fsdata *mydata) 605c30a15e5SDonggeun Kim { 606c30a15e5SDonggeun Kim __u32 fat_val, entry = 3; 607c30a15e5SDonggeun Kim 608c30a15e5SDonggeun Kim while (1) { 609c30a15e5SDonggeun Kim fat_val = get_fatent_value(mydata, entry); 610c30a15e5SDonggeun Kim if (fat_val == 0) 611c30a15e5SDonggeun Kim break; 612c30a15e5SDonggeun Kim entry++; 613c30a15e5SDonggeun Kim } 614c30a15e5SDonggeun Kim 615c30a15e5SDonggeun Kim return entry; 616c30a15e5SDonggeun Kim } 617c30a15e5SDonggeun Kim 618c30a15e5SDonggeun Kim /* 619c30a15e5SDonggeun Kim * Write directory entries in 'get_dentfromdir_block' to block device 620c30a15e5SDonggeun Kim */ 621c30a15e5SDonggeun Kim static void flush_dir_table(fsdata *mydata, dir_entry **dentptr) 622c30a15e5SDonggeun Kim { 623c30a15e5SDonggeun Kim int dir_newclust = 0; 624c30a15e5SDonggeun Kim 625c30a15e5SDonggeun Kim if (set_cluster(mydata, dir_curclust, 626c30a15e5SDonggeun Kim get_dentfromdir_block, 627c30a15e5SDonggeun Kim mydata->clust_size * mydata->sect_size) != 0) { 628c30a15e5SDonggeun Kim printf("error: wrinting directory entry\n"); 629c30a15e5SDonggeun Kim return; 630c30a15e5SDonggeun Kim } 631c30a15e5SDonggeun Kim dir_newclust = find_empty_cluster(mydata); 632c30a15e5SDonggeun Kim set_fatent_value(mydata, dir_curclust, dir_newclust); 633c30a15e5SDonggeun Kim if (mydata->fatsize == 32) 634c30a15e5SDonggeun Kim set_fatent_value(mydata, dir_newclust, 0xffffff8); 635c30a15e5SDonggeun Kim else if (mydata->fatsize == 16) 636c30a15e5SDonggeun Kim set_fatent_value(mydata, dir_newclust, 0xfff8); 637c30a15e5SDonggeun Kim 638c30a15e5SDonggeun Kim dir_curclust = dir_newclust; 639c30a15e5SDonggeun Kim 640c30a15e5SDonggeun Kim if (flush_fat_buffer(mydata) < 0) 641c30a15e5SDonggeun Kim return; 642c30a15e5SDonggeun Kim 643c30a15e5SDonggeun Kim memset(get_dentfromdir_block, 0x00, 644c30a15e5SDonggeun Kim mydata->clust_size * mydata->sect_size); 645c30a15e5SDonggeun Kim 646c30a15e5SDonggeun Kim *dentptr = (dir_entry *) get_dentfromdir_block; 647c30a15e5SDonggeun Kim } 648c30a15e5SDonggeun Kim 649c30a15e5SDonggeun Kim /* 650c30a15e5SDonggeun Kim * Set empty cluster from 'entry' to the end of a file 651c30a15e5SDonggeun Kim */ 652c30a15e5SDonggeun Kim static int clear_fatent(fsdata *mydata, __u32 entry) 653c30a15e5SDonggeun Kim { 654c30a15e5SDonggeun Kim __u32 fat_val; 655c30a15e5SDonggeun Kim 656c30a15e5SDonggeun Kim while (1) { 657c30a15e5SDonggeun Kim fat_val = get_fatent_value(mydata, entry); 658c30a15e5SDonggeun Kim if (fat_val != 0) 659c30a15e5SDonggeun Kim set_fatent_value(mydata, entry, 0); 660c30a15e5SDonggeun Kim else 661c30a15e5SDonggeun Kim break; 662c30a15e5SDonggeun Kim 663c30a15e5SDonggeun Kim if (fat_val == 0xfffffff || fat_val == 0xffff) 664c30a15e5SDonggeun Kim break; 665c30a15e5SDonggeun Kim 666c30a15e5SDonggeun Kim entry = fat_val; 667c30a15e5SDonggeun Kim } 668c30a15e5SDonggeun Kim 669c30a15e5SDonggeun Kim /* Flush fat buffer */ 670c30a15e5SDonggeun Kim if (flush_fat_buffer(mydata) < 0) 671c30a15e5SDonggeun Kim return -1; 672c30a15e5SDonggeun Kim 673c30a15e5SDonggeun Kim return 0; 674c30a15e5SDonggeun Kim } 675c30a15e5SDonggeun Kim 676c30a15e5SDonggeun Kim /* 677c30a15e5SDonggeun Kim * Write at most 'maxsize' bytes from 'buffer' into 678c30a15e5SDonggeun Kim * the file associated with 'dentptr' 679c30a15e5SDonggeun Kim * Return the number of bytes read or -1 on fatal errors. 680c30a15e5SDonggeun Kim */ 681c30a15e5SDonggeun Kim static int 682c30a15e5SDonggeun Kim set_contents(fsdata *mydata, dir_entry *dentptr, __u8 *buffer, 683c30a15e5SDonggeun Kim unsigned long maxsize) 684c30a15e5SDonggeun Kim { 685c30a15e5SDonggeun Kim unsigned long filesize = FAT2CPU32(dentptr->size), gotsize = 0; 686c30a15e5SDonggeun Kim unsigned int bytesperclust = mydata->clust_size * mydata->sect_size; 687c30a15e5SDonggeun Kim __u32 curclust = START(dentptr); 688c30a15e5SDonggeun Kim __u32 endclust = 0, newclust = 0; 689c30a15e5SDonggeun Kim unsigned long actsize; 690c30a15e5SDonggeun Kim 691c30a15e5SDonggeun Kim debug("Filesize: %ld bytes\n", filesize); 692c30a15e5SDonggeun Kim 693c30a15e5SDonggeun Kim if (maxsize > 0 && filesize > maxsize) 694c30a15e5SDonggeun Kim filesize = maxsize; 695c30a15e5SDonggeun Kim 696c30a15e5SDonggeun Kim debug("%ld bytes\n", filesize); 697c30a15e5SDonggeun Kim 698c30a15e5SDonggeun Kim actsize = bytesperclust; 699c30a15e5SDonggeun Kim endclust = curclust; 700c30a15e5SDonggeun Kim do { 701c30a15e5SDonggeun Kim /* search for consecutive clusters */ 702c30a15e5SDonggeun Kim while (actsize < filesize) { 703c30a15e5SDonggeun Kim newclust = determine_fatent(mydata, endclust); 704c30a15e5SDonggeun Kim 705c30a15e5SDonggeun Kim if ((newclust - 1) != endclust) 706c30a15e5SDonggeun Kim goto getit; 707c30a15e5SDonggeun Kim 708c30a15e5SDonggeun Kim if (CHECK_CLUST(newclust, mydata->fatsize)) { 709c30a15e5SDonggeun Kim debug("curclust: 0x%x\n", newclust); 710c30a15e5SDonggeun Kim debug("Invalid FAT entry\n"); 711c30a15e5SDonggeun Kim return gotsize; 712c30a15e5SDonggeun Kim } 713c30a15e5SDonggeun Kim endclust = newclust; 714c30a15e5SDonggeun Kim actsize += bytesperclust; 715c30a15e5SDonggeun Kim } 716c30a15e5SDonggeun Kim /* actsize >= file size */ 717c30a15e5SDonggeun Kim actsize -= bytesperclust; 718c30a15e5SDonggeun Kim /* set remaining clusters */ 719c30a15e5SDonggeun Kim if (set_cluster(mydata, curclust, buffer, (int)actsize) != 0) { 720c30a15e5SDonggeun Kim debug("error: writing cluster\n"); 721c30a15e5SDonggeun Kim return -1; 722c30a15e5SDonggeun Kim } 723c30a15e5SDonggeun Kim 724c30a15e5SDonggeun Kim /* set remaining bytes */ 725c30a15e5SDonggeun Kim gotsize += (int)actsize; 726c30a15e5SDonggeun Kim filesize -= actsize; 727c30a15e5SDonggeun Kim buffer += actsize; 728c30a15e5SDonggeun Kim actsize = filesize; 729c30a15e5SDonggeun Kim 730c30a15e5SDonggeun Kim if (set_cluster(mydata, endclust, buffer, (int)actsize) != 0) { 731c30a15e5SDonggeun Kim debug("error: writing cluster\n"); 732c30a15e5SDonggeun Kim return -1; 733c30a15e5SDonggeun Kim } 734c30a15e5SDonggeun Kim gotsize += actsize; 735c30a15e5SDonggeun Kim 736c30a15e5SDonggeun Kim /* Mark end of file in FAT */ 737c30a15e5SDonggeun Kim if (mydata->fatsize == 16) 738c30a15e5SDonggeun Kim newclust = 0xffff; 739c30a15e5SDonggeun Kim else if (mydata->fatsize == 32) 740c30a15e5SDonggeun Kim newclust = 0xfffffff; 741c30a15e5SDonggeun Kim set_fatent_value(mydata, endclust, newclust); 742c30a15e5SDonggeun Kim 743c30a15e5SDonggeun Kim return gotsize; 744c30a15e5SDonggeun Kim getit: 745c30a15e5SDonggeun Kim if (set_cluster(mydata, curclust, buffer, (int)actsize) != 0) { 746c30a15e5SDonggeun Kim debug("error: writing cluster\n"); 747c30a15e5SDonggeun Kim return -1; 748c30a15e5SDonggeun Kim } 749c30a15e5SDonggeun Kim gotsize += (int)actsize; 750c30a15e5SDonggeun Kim filesize -= actsize; 751c30a15e5SDonggeun Kim buffer += actsize; 752c30a15e5SDonggeun Kim 753c30a15e5SDonggeun Kim if (CHECK_CLUST(curclust, mydata->fatsize)) { 754c30a15e5SDonggeun Kim debug("curclust: 0x%x\n", curclust); 755c30a15e5SDonggeun Kim debug("Invalid FAT entry\n"); 756c30a15e5SDonggeun Kim return gotsize; 757c30a15e5SDonggeun Kim } 758c30a15e5SDonggeun Kim actsize = bytesperclust; 759c30a15e5SDonggeun Kim curclust = endclust = newclust; 760c30a15e5SDonggeun Kim } while (1); 761c30a15e5SDonggeun Kim } 762c30a15e5SDonggeun Kim 763c30a15e5SDonggeun Kim /* 764c30a15e5SDonggeun Kim * Fill dir_entry 765c30a15e5SDonggeun Kim */ 766c30a15e5SDonggeun Kim static void fill_dentry(fsdata *mydata, dir_entry *dentptr, 767c30a15e5SDonggeun Kim const char *filename, __u32 start_cluster, __u32 size, __u8 attr) 768c30a15e5SDonggeun Kim { 769c30a15e5SDonggeun Kim if (mydata->fatsize == 32) 770c30a15e5SDonggeun Kim dentptr->starthi = 771c30a15e5SDonggeun Kim cpu_to_le16((start_cluster & 0xffff0000) >> 16); 772c30a15e5SDonggeun Kim dentptr->start = cpu_to_le16(start_cluster & 0xffff); 773c30a15e5SDonggeun Kim dentptr->size = cpu_to_le32(size); 774c30a15e5SDonggeun Kim 775c30a15e5SDonggeun Kim dentptr->attr = attr; 776c30a15e5SDonggeun Kim 777c30a15e5SDonggeun Kim set_name(dentptr, filename); 778c30a15e5SDonggeun Kim } 779c30a15e5SDonggeun Kim 780c30a15e5SDonggeun Kim /* 781c30a15e5SDonggeun Kim * Check whether adding a file makes the file system to 782c30a15e5SDonggeun Kim * exceed the size of the block device 783c30a15e5SDonggeun Kim * Return -1 when overflow occurs, otherwise return 0 784c30a15e5SDonggeun Kim */ 785c30a15e5SDonggeun Kim static int check_overflow(fsdata *mydata, __u32 clustnum, unsigned long size) 786c30a15e5SDonggeun Kim { 787c30a15e5SDonggeun Kim __u32 startsect, sect_num; 788c30a15e5SDonggeun Kim 789c30a15e5SDonggeun Kim if (clustnum > 0) { 790c30a15e5SDonggeun Kim startsect = mydata->data_begin + 791c30a15e5SDonggeun Kim clustnum * mydata->clust_size; 792c30a15e5SDonggeun Kim } else { 793c30a15e5SDonggeun Kim startsect = mydata->rootdir_sect; 794c30a15e5SDonggeun Kim } 795c30a15e5SDonggeun Kim 796c30a15e5SDonggeun Kim sect_num = size / mydata->sect_size; 797c30a15e5SDonggeun Kim if (size % mydata->sect_size) 798c30a15e5SDonggeun Kim sect_num++; 799c30a15e5SDonggeun Kim 800c30a15e5SDonggeun Kim if (startsect + sect_num > total_sector) 801c30a15e5SDonggeun Kim return -1; 802c30a15e5SDonggeun Kim 803c30a15e5SDonggeun Kim return 0; 804c30a15e5SDonggeun Kim } 805c30a15e5SDonggeun Kim 806c30a15e5SDonggeun Kim /* 807c30a15e5SDonggeun Kim * Check if adding several entries exceed one cluster boundary 808c30a15e5SDonggeun Kim */ 809c30a15e5SDonggeun Kim static int is_next_clust(fsdata *mydata, dir_entry *dentptr) 810c30a15e5SDonggeun Kim { 811c30a15e5SDonggeun Kim int cur_position; 812c30a15e5SDonggeun Kim 813c30a15e5SDonggeun Kim cur_position = (__u8 *)dentptr - get_dentfromdir_block; 814c30a15e5SDonggeun Kim 815c30a15e5SDonggeun Kim if (cur_position >= mydata->clust_size * mydata->sect_size) 816c30a15e5SDonggeun Kim return 1; 817c30a15e5SDonggeun Kim else 818c30a15e5SDonggeun Kim return 0; 819c30a15e5SDonggeun Kim } 820c30a15e5SDonggeun Kim 821c30a15e5SDonggeun Kim static dir_entry *empty_dentptr; 822c30a15e5SDonggeun Kim /* 823c30a15e5SDonggeun Kim * Find a directory entry based on filename or start cluster number 824c30a15e5SDonggeun Kim * If the directory entry is not found, 825c30a15e5SDonggeun Kim * the new position for writing a directory entry will be returned 826c30a15e5SDonggeun Kim */ 827c30a15e5SDonggeun Kim static dir_entry *find_directory_entry(fsdata *mydata, int startsect, 828c30a15e5SDonggeun Kim char *filename, dir_entry *retdent, __u32 start) 829c30a15e5SDonggeun Kim { 830c30a15e5SDonggeun Kim __u16 prevcksum = 0xffff; 831c30a15e5SDonggeun Kim __u32 curclust = (startsect - mydata->data_begin) / mydata->clust_size; 832c30a15e5SDonggeun Kim 833c30a15e5SDonggeun Kim debug("get_dentfromdir: %s\n", filename); 834c30a15e5SDonggeun Kim 835c30a15e5SDonggeun Kim while (1) { 836c30a15e5SDonggeun Kim dir_entry *dentptr; 837c30a15e5SDonggeun Kim 838c30a15e5SDonggeun Kim int i; 839c30a15e5SDonggeun Kim 840c30a15e5SDonggeun Kim if (get_cluster(mydata, curclust, get_dentfromdir_block, 841c30a15e5SDonggeun Kim mydata->clust_size * mydata->sect_size) != 0) { 842c30a15e5SDonggeun Kim printf("Error: reading directory block\n"); 843c30a15e5SDonggeun Kim return NULL; 844c30a15e5SDonggeun Kim } 845c30a15e5SDonggeun Kim 846c30a15e5SDonggeun Kim dentptr = (dir_entry *)get_dentfromdir_block; 847c30a15e5SDonggeun Kim 848c30a15e5SDonggeun Kim dir_curclust = curclust; 849c30a15e5SDonggeun Kim 850c30a15e5SDonggeun Kim for (i = 0; i < DIRENTSPERCLUST; i++) { 851c30a15e5SDonggeun Kim char s_name[14], l_name[VFAT_MAXLEN_BYTES]; 852c30a15e5SDonggeun Kim 853c30a15e5SDonggeun Kim l_name[0] = '\0'; 854c30a15e5SDonggeun Kim if (dentptr->name[0] == DELETED_FLAG) { 855c30a15e5SDonggeun Kim dentptr++; 856c30a15e5SDonggeun Kim if (is_next_clust(mydata, dentptr)) 857c30a15e5SDonggeun Kim break; 858c30a15e5SDonggeun Kim continue; 859c30a15e5SDonggeun Kim } 860c30a15e5SDonggeun Kim if ((dentptr->attr & ATTR_VOLUME)) { 861c30a15e5SDonggeun Kim #ifdef CONFIG_SUPPORT_VFAT 862c30a15e5SDonggeun Kim if ((dentptr->attr & ATTR_VFAT) && 863c30a15e5SDonggeun Kim (dentptr->name[0] & LAST_LONG_ENTRY_MASK)) { 864c30a15e5SDonggeun Kim prevcksum = 865c30a15e5SDonggeun Kim ((dir_slot *)dentptr)->alias_checksum; 866c30a15e5SDonggeun Kim get_long_file_name(mydata, curclust, 867c30a15e5SDonggeun Kim get_dentfromdir_block, 868c30a15e5SDonggeun Kim &dentptr, l_name); 869c30a15e5SDonggeun Kim debug("vfatname: |%s|\n", l_name); 870c30a15e5SDonggeun Kim } else 871c30a15e5SDonggeun Kim #endif 872c30a15e5SDonggeun Kim { 873c30a15e5SDonggeun Kim /* Volume label or VFAT entry */ 874c30a15e5SDonggeun Kim dentptr++; 875c30a15e5SDonggeun Kim if (is_next_clust(mydata, dentptr)) 876c30a15e5SDonggeun Kim break; 877c30a15e5SDonggeun Kim continue; 878c30a15e5SDonggeun Kim } 879c30a15e5SDonggeun Kim } 880c30a15e5SDonggeun Kim if (dentptr->name[0] == 0) { 881c30a15e5SDonggeun Kim debug("Dentname == NULL - %d\n", i); 882c30a15e5SDonggeun Kim empty_dentptr = dentptr; 883c30a15e5SDonggeun Kim return NULL; 884c30a15e5SDonggeun Kim } 885c30a15e5SDonggeun Kim 886c30a15e5SDonggeun Kim get_name(dentptr, s_name); 887c30a15e5SDonggeun Kim 888c30a15e5SDonggeun Kim if (strcmp(filename, s_name) 889c30a15e5SDonggeun Kim && strcmp(filename, l_name)) { 890c30a15e5SDonggeun Kim debug("Mismatch: |%s|%s|\n", 891c30a15e5SDonggeun Kim s_name, l_name); 892c30a15e5SDonggeun Kim dentptr++; 893c30a15e5SDonggeun Kim if (is_next_clust(mydata, dentptr)) 894c30a15e5SDonggeun Kim break; 895c30a15e5SDonggeun Kim continue; 896c30a15e5SDonggeun Kim } 897c30a15e5SDonggeun Kim 898c30a15e5SDonggeun Kim memcpy(retdent, dentptr, sizeof(dir_entry)); 899c30a15e5SDonggeun Kim 900c30a15e5SDonggeun Kim debug("DentName: %s", s_name); 901c30a15e5SDonggeun Kim debug(", start: 0x%x", START(dentptr)); 902c30a15e5SDonggeun Kim debug(", size: 0x%x %s\n", 903c30a15e5SDonggeun Kim FAT2CPU32(dentptr->size), 904c30a15e5SDonggeun Kim (dentptr->attr & ATTR_DIR) ? 905c30a15e5SDonggeun Kim "(DIR)" : ""); 906c30a15e5SDonggeun Kim 907c30a15e5SDonggeun Kim return dentptr; 908c30a15e5SDonggeun Kim } 909c30a15e5SDonggeun Kim 910c30a15e5SDonggeun Kim curclust = get_fatent_value(mydata, dir_curclust); 911c30a15e5SDonggeun Kim if ((curclust >= 0xffffff8) || (curclust >= 0xfff8)) { 912c30a15e5SDonggeun Kim empty_dentptr = dentptr; 913c30a15e5SDonggeun Kim return NULL; 914c30a15e5SDonggeun Kim } 915c30a15e5SDonggeun Kim if (CHECK_CLUST(curclust, mydata->fatsize)) { 916c30a15e5SDonggeun Kim debug("curclust: 0x%x\n", curclust); 917c30a15e5SDonggeun Kim debug("Invalid FAT entry\n"); 918c30a15e5SDonggeun Kim return NULL; 919c30a15e5SDonggeun Kim } 920c30a15e5SDonggeun Kim } 921c30a15e5SDonggeun Kim 922c30a15e5SDonggeun Kim return NULL; 923c30a15e5SDonggeun Kim } 924c30a15e5SDonggeun Kim 925c30a15e5SDonggeun Kim static int do_fat_write(const char *filename, void *buffer, 926c30a15e5SDonggeun Kim unsigned long size) 927c30a15e5SDonggeun Kim { 928c30a15e5SDonggeun Kim dir_entry *dentptr, *retdent; 929c30a15e5SDonggeun Kim dir_slot *slotptr; 930c30a15e5SDonggeun Kim __u32 startsect; 931c30a15e5SDonggeun Kim __u32 start_cluster; 932c30a15e5SDonggeun Kim boot_sector bs; 933c30a15e5SDonggeun Kim volume_info volinfo; 934c30a15e5SDonggeun Kim fsdata datablock; 935c30a15e5SDonggeun Kim fsdata *mydata = &datablock; 936c30a15e5SDonggeun Kim int cursect; 937c30a15e5SDonggeun Kim int root_cluster, ret = -1, name_len; 938c30a15e5SDonggeun Kim char l_filename[VFAT_MAXLEN_BYTES]; 9398506eb8dSAnatolij Gustschin int write_size = size; 940c30a15e5SDonggeun Kim 941c30a15e5SDonggeun Kim dir_curclust = 0; 942c30a15e5SDonggeun Kim 943c30a15e5SDonggeun Kim if (read_bootsectandvi(&bs, &volinfo, &mydata->fatsize)) { 944c30a15e5SDonggeun Kim debug("error: reading boot sector\n"); 945c30a15e5SDonggeun Kim return -1; 946c30a15e5SDonggeun Kim } 947c30a15e5SDonggeun Kim 948c30a15e5SDonggeun Kim total_sector = bs.total_sect; 949c30a15e5SDonggeun Kim if (total_sector == 0) 950c30a15e5SDonggeun Kim total_sector = part_size; 951c30a15e5SDonggeun Kim 952c30a15e5SDonggeun Kim root_cluster = bs.root_cluster; 953c30a15e5SDonggeun Kim 954c30a15e5SDonggeun Kim if (mydata->fatsize == 32) 955c30a15e5SDonggeun Kim mydata->fatlength = bs.fat32_length; 956c30a15e5SDonggeun Kim else 957c30a15e5SDonggeun Kim mydata->fatlength = bs.fat_length; 958c30a15e5SDonggeun Kim 959c30a15e5SDonggeun Kim mydata->fat_sect = bs.reserved; 960c30a15e5SDonggeun Kim 961c30a15e5SDonggeun Kim cursect = mydata->rootdir_sect 962c30a15e5SDonggeun Kim = mydata->fat_sect + mydata->fatlength * bs.fats; 963*627182eaSDonggeun Kim num_of_fats = bs.fats; 964c30a15e5SDonggeun Kim 965c30a15e5SDonggeun Kim mydata->sect_size = (bs.sector_size[1] << 8) + bs.sector_size[0]; 966c30a15e5SDonggeun Kim mydata->clust_size = bs.cluster_size; 967c30a15e5SDonggeun Kim 968c30a15e5SDonggeun Kim if (mydata->fatsize == 32) { 969c30a15e5SDonggeun Kim mydata->data_begin = mydata->rootdir_sect - 970c30a15e5SDonggeun Kim (mydata->clust_size * 2); 971c30a15e5SDonggeun Kim } else { 972c30a15e5SDonggeun Kim int rootdir_size; 973c30a15e5SDonggeun Kim 974c30a15e5SDonggeun Kim rootdir_size = ((bs.dir_entries[1] * (int)256 + 975c30a15e5SDonggeun Kim bs.dir_entries[0]) * 976c30a15e5SDonggeun Kim sizeof(dir_entry)) / 977c30a15e5SDonggeun Kim mydata->sect_size; 978c30a15e5SDonggeun Kim mydata->data_begin = mydata->rootdir_sect + 979c30a15e5SDonggeun Kim rootdir_size - 980c30a15e5SDonggeun Kim (mydata->clust_size * 2); 981c30a15e5SDonggeun Kim } 982c30a15e5SDonggeun Kim 983c30a15e5SDonggeun Kim mydata->fatbufnum = -1; 984c30a15e5SDonggeun Kim mydata->fatbuf = malloc(FATBUFSIZE); 985c30a15e5SDonggeun Kim if (mydata->fatbuf == NULL) { 986c30a15e5SDonggeun Kim debug("Error: allocating memory\n"); 987c30a15e5SDonggeun Kim return -1; 988c30a15e5SDonggeun Kim } 989c30a15e5SDonggeun Kim 990c30a15e5SDonggeun Kim if (disk_read(cursect, 991c30a15e5SDonggeun Kim (mydata->fatsize == 32) ? 992c30a15e5SDonggeun Kim (mydata->clust_size) : 993c30a15e5SDonggeun Kim PREFETCH_BLOCKS, do_fat_read_block) < 0) { 994c30a15e5SDonggeun Kim debug("Error: reading rootdir block\n"); 995c30a15e5SDonggeun Kim goto exit; 996c30a15e5SDonggeun Kim } 997c30a15e5SDonggeun Kim dentptr = (dir_entry *) do_fat_read_block; 998c30a15e5SDonggeun Kim 999c30a15e5SDonggeun Kim name_len = strlen(filename); 10008506eb8dSAnatolij Gustschin if (name_len >= VFAT_MAXLEN_BYTES) 10018506eb8dSAnatolij Gustschin name_len = VFAT_MAXLEN_BYTES - 1; 10028506eb8dSAnatolij Gustschin 1003c30a15e5SDonggeun Kim memcpy(l_filename, filename, name_len); 10048506eb8dSAnatolij Gustschin l_filename[name_len] = 0; /* terminate the string */ 1005c30a15e5SDonggeun Kim downcase(l_filename); 1006c30a15e5SDonggeun Kim 1007c30a15e5SDonggeun Kim startsect = mydata->rootdir_sect; 1008c30a15e5SDonggeun Kim retdent = find_directory_entry(mydata, startsect, 1009c30a15e5SDonggeun Kim l_filename, dentptr, 0); 1010c30a15e5SDonggeun Kim if (retdent) { 1011c30a15e5SDonggeun Kim /* Update file size and start_cluster in a directory entry */ 1012c30a15e5SDonggeun Kim retdent->size = cpu_to_le32(size); 1013c30a15e5SDonggeun Kim start_cluster = FAT2CPU16(retdent->start); 1014c30a15e5SDonggeun Kim if (mydata->fatsize == 32) 1015c30a15e5SDonggeun Kim start_cluster |= 1016c30a15e5SDonggeun Kim (FAT2CPU16(retdent->starthi) << 16); 1017c30a15e5SDonggeun Kim 1018c30a15e5SDonggeun Kim ret = check_overflow(mydata, start_cluster, size); 1019c30a15e5SDonggeun Kim if (ret) { 1020c30a15e5SDonggeun Kim printf("Error: %ld overflow\n", size); 1021c30a15e5SDonggeun Kim goto exit; 1022c30a15e5SDonggeun Kim } 1023c30a15e5SDonggeun Kim 1024c30a15e5SDonggeun Kim ret = clear_fatent(mydata, start_cluster); 1025c30a15e5SDonggeun Kim if (ret) { 1026c30a15e5SDonggeun Kim printf("Error: clearing FAT entries\n"); 1027c30a15e5SDonggeun Kim goto exit; 1028c30a15e5SDonggeun Kim } 1029c30a15e5SDonggeun Kim 1030c30a15e5SDonggeun Kim ret = set_contents(mydata, retdent, buffer, size); 10318506eb8dSAnatolij Gustschin if (ret < 0) { 1032c30a15e5SDonggeun Kim printf("Error: writing contents\n"); 1033c30a15e5SDonggeun Kim goto exit; 1034c30a15e5SDonggeun Kim } 10358506eb8dSAnatolij Gustschin write_size = ret; 10368506eb8dSAnatolij Gustschin debug("attempt to write 0x%x bytes\n", write_size); 1037c30a15e5SDonggeun Kim 1038c30a15e5SDonggeun Kim /* Flush fat buffer */ 1039c30a15e5SDonggeun Kim ret = flush_fat_buffer(mydata); 1040c30a15e5SDonggeun Kim if (ret) { 1041c30a15e5SDonggeun Kim printf("Error: flush fat buffer\n"); 1042c30a15e5SDonggeun Kim goto exit; 1043c30a15e5SDonggeun Kim } 1044c30a15e5SDonggeun Kim 1045c30a15e5SDonggeun Kim /* Write directory table to device */ 1046c30a15e5SDonggeun Kim ret = set_cluster(mydata, dir_curclust, 1047c30a15e5SDonggeun Kim get_dentfromdir_block, 1048c30a15e5SDonggeun Kim mydata->clust_size * mydata->sect_size); 1049c30a15e5SDonggeun Kim if (ret) { 10508506eb8dSAnatolij Gustschin printf("Error: writing directory entry\n"); 1051c30a15e5SDonggeun Kim goto exit; 1052c30a15e5SDonggeun Kim } 1053c30a15e5SDonggeun Kim } else { 1054c30a15e5SDonggeun Kim slotptr = (dir_slot *)empty_dentptr; 1055c30a15e5SDonggeun Kim 1056c30a15e5SDonggeun Kim /* Set short name to set alias checksum field in dir_slot */ 1057c30a15e5SDonggeun Kim set_name(empty_dentptr, filename); 1058c30a15e5SDonggeun Kim fill_dir_slot(mydata, &empty_dentptr, filename); 1059c30a15e5SDonggeun Kim 1060c30a15e5SDonggeun Kim ret = start_cluster = find_empty_cluster(mydata); 1061c30a15e5SDonggeun Kim if (ret < 0) { 1062c30a15e5SDonggeun Kim printf("Error: finding empty cluster\n"); 1063c30a15e5SDonggeun Kim goto exit; 1064c30a15e5SDonggeun Kim } 1065c30a15e5SDonggeun Kim 1066c30a15e5SDonggeun Kim ret = check_overflow(mydata, start_cluster, size); 1067c30a15e5SDonggeun Kim if (ret) { 1068c30a15e5SDonggeun Kim printf("Error: %ld overflow\n", size); 1069c30a15e5SDonggeun Kim goto exit; 1070c30a15e5SDonggeun Kim } 1071c30a15e5SDonggeun Kim 1072c30a15e5SDonggeun Kim /* Set attribute as archieve for regular file */ 1073c30a15e5SDonggeun Kim fill_dentry(mydata, empty_dentptr, filename, 1074c30a15e5SDonggeun Kim start_cluster, size, 0x20); 1075c30a15e5SDonggeun Kim 1076c30a15e5SDonggeun Kim ret = set_contents(mydata, empty_dentptr, buffer, size); 10778506eb8dSAnatolij Gustschin if (ret < 0) { 1078c30a15e5SDonggeun Kim printf("Error: writing contents\n"); 1079c30a15e5SDonggeun Kim goto exit; 1080c30a15e5SDonggeun Kim } 10818506eb8dSAnatolij Gustschin write_size = ret; 10828506eb8dSAnatolij Gustschin debug("attempt to write 0x%x bytes\n", write_size); 1083c30a15e5SDonggeun Kim 1084c30a15e5SDonggeun Kim /* Flush fat buffer */ 1085c30a15e5SDonggeun Kim ret = flush_fat_buffer(mydata); 1086c30a15e5SDonggeun Kim if (ret) { 1087c30a15e5SDonggeun Kim printf("Error: flush fat buffer\n"); 1088c30a15e5SDonggeun Kim goto exit; 1089c30a15e5SDonggeun Kim } 1090c30a15e5SDonggeun Kim 1091c30a15e5SDonggeun Kim /* Write directory table to device */ 1092c30a15e5SDonggeun Kim ret = set_cluster(mydata, dir_curclust, 1093c30a15e5SDonggeun Kim get_dentfromdir_block, 1094c30a15e5SDonggeun Kim mydata->clust_size * mydata->sect_size); 1095c30a15e5SDonggeun Kim if (ret) { 1096c30a15e5SDonggeun Kim printf("Error: writing directory entry\n"); 1097c30a15e5SDonggeun Kim goto exit; 1098c30a15e5SDonggeun Kim } 1099c30a15e5SDonggeun Kim } 1100c30a15e5SDonggeun Kim 1101c30a15e5SDonggeun Kim exit: 1102c30a15e5SDonggeun Kim free(mydata->fatbuf); 11038506eb8dSAnatolij Gustschin return ret < 0 ? ret : write_size; 1104c30a15e5SDonggeun Kim } 1105c30a15e5SDonggeun Kim 1106c30a15e5SDonggeun Kim int file_fat_write(const char *filename, void *buffer, unsigned long maxsize) 1107c30a15e5SDonggeun Kim { 1108c30a15e5SDonggeun Kim printf("writing %s\n", filename); 1109c30a15e5SDonggeun Kim return do_fat_write(filename, buffer, maxsize); 1110c30a15e5SDonggeun Kim } 1111