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 * 61a459660SWolfgang Denk * SPDX-License-Identifier: GPL-2.0+ 7c30a15e5SDonggeun Kim */ 8c30a15e5SDonggeun Kim 9c30a15e5SDonggeun Kim #include <common.h> 10c30a15e5SDonggeun Kim #include <command.h> 11c30a15e5SDonggeun Kim #include <config.h> 12c30a15e5SDonggeun Kim #include <fat.h> 13c30a15e5SDonggeun Kim #include <asm/byteorder.h> 14c30a15e5SDonggeun Kim #include <part.h> 15fb7e16ccSRichard Genoud #include <linux/ctype.h> 169e374e7bSTom Rini #include <div64.h> 179e374e7bSTom Rini #include <linux/math64.h> 18c30a15e5SDonggeun Kim #include "fat.c" 19c30a15e5SDonggeun Kim 20c30a15e5SDonggeun Kim static void uppercase(char *str, int len) 21c30a15e5SDonggeun Kim { 22c30a15e5SDonggeun Kim int i; 23c30a15e5SDonggeun Kim 24c30a15e5SDonggeun Kim for (i = 0; i < len; i++) { 25fb7e16ccSRichard Genoud *str = toupper(*str); 26c30a15e5SDonggeun Kim str++; 27c30a15e5SDonggeun Kim } 28c30a15e5SDonggeun Kim } 29c30a15e5SDonggeun Kim 30c30a15e5SDonggeun Kim static int total_sector; 31079df722SDonggeun Kim static int disk_write(__u32 block, __u32 nr_blocks, void *buf) 32c30a15e5SDonggeun Kim { 330a04ed86SŁukasz Majewski ulong ret; 340a04ed86SŁukasz Majewski 352a981dc2SSimon Glass if (!cur_dev) 36c30a15e5SDonggeun Kim return -1; 37c30a15e5SDonggeun Kim 38079df722SDonggeun Kim if (cur_part_info.start + block + nr_blocks > 39079df722SDonggeun Kim cur_part_info.start + total_sector) { 40c30a15e5SDonggeun Kim printf("error: overflow occurs\n"); 41c30a15e5SDonggeun Kim return -1; 42c30a15e5SDonggeun Kim } 43c30a15e5SDonggeun Kim 442a981dc2SSimon Glass ret = blk_dwrite(cur_dev, cur_part_info.start + block, nr_blocks, buf); 450a04ed86SŁukasz Majewski if (nr_blocks && ret == 0) 460a04ed86SŁukasz Majewski return -1; 470a04ed86SŁukasz Majewski 480a04ed86SŁukasz Majewski return ret; 49c30a15e5SDonggeun Kim } 50c30a15e5SDonggeun Kim 51c30a15e5SDonggeun Kim /* 52c30a15e5SDonggeun Kim * Set short name in directory entry 53c30a15e5SDonggeun Kim */ 54c30a15e5SDonggeun Kim static void set_name(dir_entry *dirent, const char *filename) 55c30a15e5SDonggeun Kim { 56c30a15e5SDonggeun Kim char s_name[VFAT_MAXLEN_BYTES]; 57c30a15e5SDonggeun Kim char *period; 58c30a15e5SDonggeun Kim int period_location, len, i, ext_num; 59c30a15e5SDonggeun Kim 60c30a15e5SDonggeun Kim if (filename == NULL) 61c30a15e5SDonggeun Kim return; 62c30a15e5SDonggeun Kim 63c30a15e5SDonggeun Kim len = strlen(filename); 64c30a15e5SDonggeun Kim if (len == 0) 65c30a15e5SDonggeun Kim return; 66c30a15e5SDonggeun Kim 6773dc8328SPiotr Wilczek strcpy(s_name, filename); 68c30a15e5SDonggeun Kim uppercase(s_name, len); 69c30a15e5SDonggeun Kim 70c30a15e5SDonggeun Kim period = strchr(s_name, '.'); 71c30a15e5SDonggeun Kim if (period == NULL) { 72c30a15e5SDonggeun Kim period_location = len; 73c30a15e5SDonggeun Kim ext_num = 0; 74c30a15e5SDonggeun Kim } else { 75c30a15e5SDonggeun Kim period_location = period - s_name; 76c30a15e5SDonggeun Kim ext_num = len - period_location - 1; 77c30a15e5SDonggeun Kim } 78c30a15e5SDonggeun Kim 79c30a15e5SDonggeun Kim /* Pad spaces when the length of file name is shorter than eight */ 80c30a15e5SDonggeun Kim if (period_location < 8) { 81c30a15e5SDonggeun Kim memcpy(dirent->name, s_name, period_location); 82c30a15e5SDonggeun Kim for (i = period_location; i < 8; i++) 83c30a15e5SDonggeun Kim dirent->name[i] = ' '; 84c30a15e5SDonggeun Kim } else if (period_location == 8) { 85c30a15e5SDonggeun Kim memcpy(dirent->name, s_name, period_location); 86c30a15e5SDonggeun Kim } else { 87c30a15e5SDonggeun Kim memcpy(dirent->name, s_name, 6); 88c30a15e5SDonggeun Kim dirent->name[6] = '~'; 89c30a15e5SDonggeun Kim dirent->name[7] = '1'; 90c30a15e5SDonggeun Kim } 91c30a15e5SDonggeun Kim 92c30a15e5SDonggeun Kim if (ext_num < 3) { 93c30a15e5SDonggeun Kim memcpy(dirent->ext, s_name + period_location + 1, ext_num); 94c30a15e5SDonggeun Kim for (i = ext_num; i < 3; i++) 95c30a15e5SDonggeun Kim dirent->ext[i] = ' '; 96c30a15e5SDonggeun Kim } else 97c30a15e5SDonggeun Kim memcpy(dirent->ext, s_name + period_location + 1, 3); 98c30a15e5SDonggeun Kim 99c30a15e5SDonggeun Kim debug("name : %s\n", dirent->name); 100c30a15e5SDonggeun Kim debug("ext : %s\n", dirent->ext); 101c30a15e5SDonggeun Kim } 102c30a15e5SDonggeun Kim 103627182eaSDonggeun Kim static __u8 num_of_fats; 104c30a15e5SDonggeun Kim /* 105c30a15e5SDonggeun Kim * Write fat buffer into block device 106c30a15e5SDonggeun Kim */ 1073c0ed9c3SStefan Brüns static int flush_dirty_fat_buffer(fsdata *mydata) 108c30a15e5SDonggeun Kim { 109c30a15e5SDonggeun Kim int getsize = FATBUFBLOCKS; 110c30a15e5SDonggeun Kim __u32 fatlength = mydata->fatlength; 111c30a15e5SDonggeun Kim __u8 *bufptr = mydata->fatbuf; 112c30a15e5SDonggeun Kim __u32 startblock = mydata->fatbufnum * FATBUFBLOCKS; 113c30a15e5SDonggeun Kim 1143c0ed9c3SStefan Brüns debug("debug: evicting %d, dirty: %d\n", mydata->fatbufnum, 1153c0ed9c3SStefan Brüns (int)mydata->fat_dirty); 1163c0ed9c3SStefan Brüns 1173c0ed9c3SStefan Brüns if ((!mydata->fat_dirty) || (mydata->fatbufnum == -1)) 1183c0ed9c3SStefan Brüns return 0; 1193c0ed9c3SStefan Brüns 1206c1a8080SStefan Brüns /* Cap length if fatlength is not a multiple of FATBUFBLOCKS */ 1216c1a8080SStefan Brüns if (startblock + getsize > fatlength) 1226c1a8080SStefan Brüns getsize = fatlength - startblock; 123c30a15e5SDonggeun Kim 1246c1a8080SStefan Brüns startblock += mydata->fat_sect; 125c30a15e5SDonggeun Kim 126c30a15e5SDonggeun Kim /* Write FAT buf */ 127c30a15e5SDonggeun Kim if (disk_write(startblock, getsize, bufptr) < 0) { 128c30a15e5SDonggeun Kim debug("error: writing FAT blocks\n"); 129c30a15e5SDonggeun Kim return -1; 130c30a15e5SDonggeun Kim } 131c30a15e5SDonggeun Kim 132627182eaSDonggeun Kim if (num_of_fats == 2) { 133627182eaSDonggeun Kim /* Update corresponding second FAT blocks */ 134627182eaSDonggeun Kim startblock += mydata->fatlength; 135627182eaSDonggeun Kim if (disk_write(startblock, getsize, bufptr) < 0) { 136627182eaSDonggeun Kim debug("error: writing second FAT blocks\n"); 137627182eaSDonggeun Kim return -1; 138627182eaSDonggeun Kim } 139627182eaSDonggeun Kim } 1403c0ed9c3SStefan Brüns mydata->fat_dirty = 0; 141627182eaSDonggeun Kim 142c30a15e5SDonggeun Kim return 0; 143c30a15e5SDonggeun Kim } 144c30a15e5SDonggeun Kim 145c30a15e5SDonggeun Kim /* 146c30a15e5SDonggeun Kim * Set the file name information from 'name' into 'slotptr', 147c30a15e5SDonggeun Kim */ 148c30a15e5SDonggeun Kim static int str2slot(dir_slot *slotptr, const char *name, int *idx) 149c30a15e5SDonggeun Kim { 150c30a15e5SDonggeun Kim int j, end_idx = 0; 151c30a15e5SDonggeun Kim 152c30a15e5SDonggeun Kim for (j = 0; j <= 8; j += 2) { 153c30a15e5SDonggeun Kim if (name[*idx] == 0x00) { 154c30a15e5SDonggeun Kim slotptr->name0_4[j] = 0; 155c30a15e5SDonggeun Kim slotptr->name0_4[j + 1] = 0; 156c30a15e5SDonggeun Kim end_idx++; 157c30a15e5SDonggeun Kim goto name0_4; 158c30a15e5SDonggeun Kim } 159c30a15e5SDonggeun Kim slotptr->name0_4[j] = name[*idx]; 160c30a15e5SDonggeun Kim (*idx)++; 161c30a15e5SDonggeun Kim end_idx++; 162c30a15e5SDonggeun Kim } 163c30a15e5SDonggeun Kim for (j = 0; j <= 10; j += 2) { 164c30a15e5SDonggeun Kim if (name[*idx] == 0x00) { 165c30a15e5SDonggeun Kim slotptr->name5_10[j] = 0; 166c30a15e5SDonggeun Kim slotptr->name5_10[j + 1] = 0; 167c30a15e5SDonggeun Kim end_idx++; 168c30a15e5SDonggeun Kim goto name5_10; 169c30a15e5SDonggeun Kim } 170c30a15e5SDonggeun Kim slotptr->name5_10[j] = name[*idx]; 171c30a15e5SDonggeun Kim (*idx)++; 172c30a15e5SDonggeun Kim end_idx++; 173c30a15e5SDonggeun Kim } 174c30a15e5SDonggeun Kim for (j = 0; j <= 2; j += 2) { 175c30a15e5SDonggeun Kim if (name[*idx] == 0x00) { 176c30a15e5SDonggeun Kim slotptr->name11_12[j] = 0; 177c30a15e5SDonggeun Kim slotptr->name11_12[j + 1] = 0; 178c30a15e5SDonggeun Kim end_idx++; 179c30a15e5SDonggeun Kim goto name11_12; 180c30a15e5SDonggeun Kim } 181c30a15e5SDonggeun Kim slotptr->name11_12[j] = name[*idx]; 182c30a15e5SDonggeun Kim (*idx)++; 183c30a15e5SDonggeun Kim end_idx++; 184c30a15e5SDonggeun Kim } 185c30a15e5SDonggeun Kim 186c30a15e5SDonggeun Kim if (name[*idx] == 0x00) 187c30a15e5SDonggeun Kim return 1; 188c30a15e5SDonggeun Kim 189c30a15e5SDonggeun Kim return 0; 190c30a15e5SDonggeun Kim /* Not used characters are filled with 0xff 0xff */ 191c30a15e5SDonggeun Kim name0_4: 192c30a15e5SDonggeun Kim for (; end_idx < 5; end_idx++) { 193c30a15e5SDonggeun Kim slotptr->name0_4[end_idx * 2] = 0xff; 194c30a15e5SDonggeun Kim slotptr->name0_4[end_idx * 2 + 1] = 0xff; 195c30a15e5SDonggeun Kim } 196c30a15e5SDonggeun Kim end_idx = 5; 197c30a15e5SDonggeun Kim name5_10: 198c30a15e5SDonggeun Kim end_idx -= 5; 199c30a15e5SDonggeun Kim for (; end_idx < 6; end_idx++) { 200c30a15e5SDonggeun Kim slotptr->name5_10[end_idx * 2] = 0xff; 201c30a15e5SDonggeun Kim slotptr->name5_10[end_idx * 2 + 1] = 0xff; 202c30a15e5SDonggeun Kim } 203c30a15e5SDonggeun Kim end_idx = 11; 204c30a15e5SDonggeun Kim name11_12: 205c30a15e5SDonggeun Kim end_idx -= 11; 206c30a15e5SDonggeun Kim for (; end_idx < 2; end_idx++) { 207c30a15e5SDonggeun Kim slotptr->name11_12[end_idx * 2] = 0xff; 208c30a15e5SDonggeun Kim slotptr->name11_12[end_idx * 2 + 1] = 0xff; 209c30a15e5SDonggeun Kim } 210c30a15e5SDonggeun Kim 211c30a15e5SDonggeun Kim return 1; 212c30a15e5SDonggeun Kim } 213c30a15e5SDonggeun Kim 214c30a15e5SDonggeun Kim static int is_next_clust(fsdata *mydata, dir_entry *dentptr); 215c30a15e5SDonggeun Kim static void flush_dir_table(fsdata *mydata, dir_entry **dentptr); 216c30a15e5SDonggeun Kim 217c30a15e5SDonggeun Kim /* 218c30a15e5SDonggeun Kim * Fill dir_slot entries with appropriate name, id, and attr 219c30a15e5SDonggeun Kim * The real directory entry is returned by 'dentptr' 220c30a15e5SDonggeun Kim */ 221c30a15e5SDonggeun Kim static void 222c30a15e5SDonggeun Kim fill_dir_slot(fsdata *mydata, dir_entry **dentptr, const char *l_name) 223c30a15e5SDonggeun Kim { 2247aa1a6b7STien Fong Chee __u8 temp_dir_slot_buffer[MAX_LFN_SLOT * sizeof(dir_slot)]; 2257aa1a6b7STien Fong Chee dir_slot *slotptr = (dir_slot *)temp_dir_slot_buffer; 2268506eb8dSAnatolij Gustschin __u8 counter = 0, checksum; 227c30a15e5SDonggeun Kim int idx = 0, ret; 228c30a15e5SDonggeun Kim 229ed76f912SStefan Brüns /* Get short file name checksum value */ 230ff04f6d1SMarek Vasut checksum = mkcksum((*dentptr)->name, (*dentptr)->ext); 231c30a15e5SDonggeun Kim 232c30a15e5SDonggeun Kim do { 233c30a15e5SDonggeun Kim memset(slotptr, 0x00, sizeof(dir_slot)); 234c30a15e5SDonggeun Kim ret = str2slot(slotptr, l_name, &idx); 235c30a15e5SDonggeun Kim slotptr->id = ++counter; 236c30a15e5SDonggeun Kim slotptr->attr = ATTR_VFAT; 237c30a15e5SDonggeun Kim slotptr->alias_checksum = checksum; 238c30a15e5SDonggeun Kim slotptr++; 239c30a15e5SDonggeun Kim } while (ret == 0); 240c30a15e5SDonggeun Kim 241c30a15e5SDonggeun Kim slotptr--; 242c30a15e5SDonggeun Kim slotptr->id |= LAST_LONG_ENTRY_MASK; 243c30a15e5SDonggeun Kim 244c30a15e5SDonggeun Kim while (counter >= 1) { 245c30a15e5SDonggeun Kim if (is_next_clust(mydata, *dentptr)) { 246c30a15e5SDonggeun Kim /* A new cluster is allocated for directory table */ 247c30a15e5SDonggeun Kim flush_dir_table(mydata, dentptr); 248c30a15e5SDonggeun Kim } 249c30a15e5SDonggeun Kim memcpy(*dentptr, slotptr, sizeof(dir_slot)); 250c30a15e5SDonggeun Kim (*dentptr)++; 251c30a15e5SDonggeun Kim slotptr--; 252c30a15e5SDonggeun Kim counter--; 253c30a15e5SDonggeun Kim } 254c30a15e5SDonggeun Kim 255c30a15e5SDonggeun Kim if (is_next_clust(mydata, *dentptr)) { 256c30a15e5SDonggeun Kim /* A new cluster is allocated for directory table */ 257c30a15e5SDonggeun Kim flush_dir_table(mydata, dentptr); 258c30a15e5SDonggeun Kim } 259c30a15e5SDonggeun Kim } 260c30a15e5SDonggeun Kim 261c30a15e5SDonggeun Kim static __u32 dir_curclust; 262c30a15e5SDonggeun Kim 263c30a15e5SDonggeun Kim /* 264c30a15e5SDonggeun Kim * Extract the full long filename starting at 'retdent' (which is really 265c30a15e5SDonggeun Kim * a slot) into 'l_name'. If successful also copy the real directory entry 266c30a15e5SDonggeun Kim * into 'retdent' 267c30a15e5SDonggeun Kim * If additional adjacent cluster for directory entries is read into memory, 2681170e634SBenoît Thébaudeau * then 'get_contents_vfatname_block' is copied into 'get_dentfromdir_block' and 269c30a15e5SDonggeun Kim * the location of the real directory entry is returned by 'retdent' 270c30a15e5SDonggeun Kim * Return 0 on success, -1 otherwise. 271c30a15e5SDonggeun Kim */ 272c30a15e5SDonggeun Kim static int 273c30a15e5SDonggeun Kim get_long_file_name(fsdata *mydata, int curclust, __u8 *cluster, 274c30a15e5SDonggeun Kim dir_entry **retdent, char *l_name) 275c30a15e5SDonggeun Kim { 276c30a15e5SDonggeun Kim dir_entry *realdent; 277c30a15e5SDonggeun Kim dir_slot *slotptr = (dir_slot *)(*retdent); 278c30a15e5SDonggeun Kim dir_slot *slotptr2 = NULL; 279c30a15e5SDonggeun Kim __u8 *buflimit = cluster + mydata->sect_size * ((curclust == 0) ? 280c30a15e5SDonggeun Kim PREFETCH_BLOCKS : 281c30a15e5SDonggeun Kim mydata->clust_size); 282c30a15e5SDonggeun Kim __u8 counter = (slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff; 283c30a15e5SDonggeun Kim int idx = 0, cur_position = 0; 284c30a15e5SDonggeun Kim 285c30a15e5SDonggeun Kim if (counter > VFAT_MAXSEQ) { 286c30a15e5SDonggeun Kim debug("Error: VFAT name is too long\n"); 287c30a15e5SDonggeun Kim return -1; 288c30a15e5SDonggeun Kim } 289c30a15e5SDonggeun Kim 290c30a15e5SDonggeun Kim while ((__u8 *)slotptr < buflimit) { 291c30a15e5SDonggeun Kim if (counter == 0) 292c30a15e5SDonggeun Kim break; 293c30a15e5SDonggeun Kim if (((slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff) != counter) 294c30a15e5SDonggeun Kim return -1; 295c30a15e5SDonggeun Kim slotptr++; 296c30a15e5SDonggeun Kim counter--; 297c30a15e5SDonggeun Kim } 298c30a15e5SDonggeun Kim 299c30a15e5SDonggeun Kim if ((__u8 *)slotptr >= buflimit) { 300c30a15e5SDonggeun Kim if (curclust == 0) 301c30a15e5SDonggeun Kim return -1; 302*b8948d2aSStefan Brüns curclust = get_fatent(mydata, dir_curclust); 303c30a15e5SDonggeun Kim if (CHECK_CLUST(curclust, mydata->fatsize)) { 304c30a15e5SDonggeun Kim debug("curclust: 0x%x\n", curclust); 305c30a15e5SDonggeun Kim printf("Invalid FAT entry\n"); 306c30a15e5SDonggeun Kim return -1; 307c30a15e5SDonggeun Kim } 308c30a15e5SDonggeun Kim 309c30a15e5SDonggeun Kim dir_curclust = curclust; 310c30a15e5SDonggeun Kim 3111170e634SBenoît Thébaudeau if (get_cluster(mydata, curclust, get_contents_vfatname_block, 312c30a15e5SDonggeun Kim mydata->clust_size * mydata->sect_size) != 0) { 313c30a15e5SDonggeun Kim debug("Error: reading directory block\n"); 314c30a15e5SDonggeun Kim return -1; 315c30a15e5SDonggeun Kim } 316c30a15e5SDonggeun Kim 3171170e634SBenoît Thébaudeau slotptr2 = (dir_slot *)get_contents_vfatname_block; 318c30a15e5SDonggeun Kim while (counter > 0) { 319c30a15e5SDonggeun Kim if (((slotptr2->id & ~LAST_LONG_ENTRY_MASK) 320c30a15e5SDonggeun Kim & 0xff) != counter) 321c30a15e5SDonggeun Kim return -1; 322c30a15e5SDonggeun Kim slotptr2++; 323c30a15e5SDonggeun Kim counter--; 324c30a15e5SDonggeun Kim } 325c30a15e5SDonggeun Kim 326c30a15e5SDonggeun Kim /* Save the real directory entry */ 327c30a15e5SDonggeun Kim realdent = (dir_entry *)slotptr2; 3281170e634SBenoît Thébaudeau while ((__u8 *)slotptr2 > get_contents_vfatname_block) { 329c30a15e5SDonggeun Kim slotptr2--; 330c30a15e5SDonggeun Kim slot2str(slotptr2, l_name, &idx); 331c30a15e5SDonggeun Kim } 332c30a15e5SDonggeun Kim } else { 333c30a15e5SDonggeun Kim /* Save the real directory entry */ 334c30a15e5SDonggeun Kim realdent = (dir_entry *)slotptr; 335c30a15e5SDonggeun Kim } 336c30a15e5SDonggeun Kim 337c30a15e5SDonggeun Kim do { 338c30a15e5SDonggeun Kim slotptr--; 339c30a15e5SDonggeun Kim if (slot2str(slotptr, l_name, &idx)) 340c30a15e5SDonggeun Kim break; 341c30a15e5SDonggeun Kim } while (!(slotptr->id & LAST_LONG_ENTRY_MASK)); 342c30a15e5SDonggeun Kim 343c30a15e5SDonggeun Kim l_name[idx] = '\0'; 344c30a15e5SDonggeun Kim if (*l_name == DELETED_FLAG) 345c30a15e5SDonggeun Kim *l_name = '\0'; 346c30a15e5SDonggeun Kim else if (*l_name == aRING) 347c30a15e5SDonggeun Kim *l_name = DELETED_FLAG; 348c30a15e5SDonggeun Kim downcase(l_name); 349c30a15e5SDonggeun Kim 350c30a15e5SDonggeun Kim /* Return the real directory entry */ 351c30a15e5SDonggeun Kim *retdent = realdent; 352c30a15e5SDonggeun Kim 353c30a15e5SDonggeun Kim if (slotptr2) { 3541170e634SBenoît Thébaudeau memcpy(get_dentfromdir_block, get_contents_vfatname_block, 355c30a15e5SDonggeun Kim mydata->clust_size * mydata->sect_size); 3561170e634SBenoît Thébaudeau cur_position = (__u8 *)realdent - get_contents_vfatname_block; 357c30a15e5SDonggeun Kim *retdent = (dir_entry *) &get_dentfromdir_block[cur_position]; 358c30a15e5SDonggeun Kim } 359c30a15e5SDonggeun Kim 360c30a15e5SDonggeun Kim return 0; 361c30a15e5SDonggeun Kim } 362c30a15e5SDonggeun Kim 363c30a15e5SDonggeun Kim /* 36449abbd9cSPhilipp Skadorov * Set the entry at index 'entry' in a FAT (12/16/32) table. 365c30a15e5SDonggeun Kim */ 366c30a15e5SDonggeun Kim static int set_fatent_value(fsdata *mydata, __u32 entry, __u32 entry_value) 367c30a15e5SDonggeun Kim { 36849abbd9cSPhilipp Skadorov __u32 bufnum, offset, off16; 36949abbd9cSPhilipp Skadorov __u16 val1, val2; 370c30a15e5SDonggeun Kim 371c30a15e5SDonggeun Kim switch (mydata->fatsize) { 372c30a15e5SDonggeun Kim case 32: 373c30a15e5SDonggeun Kim bufnum = entry / FAT32BUFSIZE; 374c30a15e5SDonggeun Kim offset = entry - bufnum * FAT32BUFSIZE; 375c30a15e5SDonggeun Kim break; 376c30a15e5SDonggeun Kim case 16: 377c30a15e5SDonggeun Kim bufnum = entry / FAT16BUFSIZE; 378c30a15e5SDonggeun Kim offset = entry - bufnum * FAT16BUFSIZE; 379c30a15e5SDonggeun Kim break; 38049abbd9cSPhilipp Skadorov case 12: 38149abbd9cSPhilipp Skadorov bufnum = entry / FAT12BUFSIZE; 38249abbd9cSPhilipp Skadorov offset = entry - bufnum * FAT12BUFSIZE; 38349abbd9cSPhilipp Skadorov break; 384c30a15e5SDonggeun Kim default: 385c30a15e5SDonggeun Kim /* Unsupported FAT size */ 386c30a15e5SDonggeun Kim return -1; 387c30a15e5SDonggeun Kim } 388c30a15e5SDonggeun Kim 389c30a15e5SDonggeun Kim /* Read a new block of FAT entries into the cache. */ 390c30a15e5SDonggeun Kim if (bufnum != mydata->fatbufnum) { 391c30a15e5SDonggeun Kim int getsize = FATBUFBLOCKS; 392c30a15e5SDonggeun Kim __u8 *bufptr = mydata->fatbuf; 393c30a15e5SDonggeun Kim __u32 fatlength = mydata->fatlength; 394c30a15e5SDonggeun Kim __u32 startblock = bufnum * FATBUFBLOCKS; 395c30a15e5SDonggeun Kim 3966c1a8080SStefan Brüns /* Cap length if fatlength is not a multiple of FATBUFBLOCKS */ 3976c1a8080SStefan Brüns if (startblock + getsize > fatlength) 3986c1a8080SStefan Brüns getsize = fatlength - startblock; 399c30a15e5SDonggeun Kim 4003c0ed9c3SStefan Brüns if (flush_dirty_fat_buffer(mydata) < 0) 401c30a15e5SDonggeun Kim return -1; 402c30a15e5SDonggeun Kim 4036c1a8080SStefan Brüns startblock += mydata->fat_sect; 4046c1a8080SStefan Brüns 405c30a15e5SDonggeun Kim if (disk_read(startblock, getsize, bufptr) < 0) { 406c30a15e5SDonggeun Kim debug("Error reading FAT blocks\n"); 407c30a15e5SDonggeun Kim return -1; 408c30a15e5SDonggeun Kim } 409c30a15e5SDonggeun Kim mydata->fatbufnum = bufnum; 410c30a15e5SDonggeun Kim } 411c30a15e5SDonggeun Kim 4123c0ed9c3SStefan Brüns /* Mark as dirty */ 4133c0ed9c3SStefan Brüns mydata->fat_dirty = 1; 4143c0ed9c3SStefan Brüns 415c30a15e5SDonggeun Kim /* Set the actual entry */ 416c30a15e5SDonggeun Kim switch (mydata->fatsize) { 417c30a15e5SDonggeun Kim case 32: 418c30a15e5SDonggeun Kim ((__u32 *) mydata->fatbuf)[offset] = cpu_to_le32(entry_value); 419c30a15e5SDonggeun Kim break; 420c30a15e5SDonggeun Kim case 16: 421c30a15e5SDonggeun Kim ((__u16 *) mydata->fatbuf)[offset] = cpu_to_le16(entry_value); 422c30a15e5SDonggeun Kim break; 42349abbd9cSPhilipp Skadorov case 12: 42449abbd9cSPhilipp Skadorov off16 = (offset * 3) / 4; 42549abbd9cSPhilipp Skadorov 42649abbd9cSPhilipp Skadorov switch (offset & 0x3) { 42749abbd9cSPhilipp Skadorov case 0: 42849abbd9cSPhilipp Skadorov val1 = cpu_to_le16(entry_value) & 0xfff; 42949abbd9cSPhilipp Skadorov ((__u16 *)mydata->fatbuf)[off16] &= ~0xfff; 43049abbd9cSPhilipp Skadorov ((__u16 *)mydata->fatbuf)[off16] |= val1; 43149abbd9cSPhilipp Skadorov break; 43249abbd9cSPhilipp Skadorov case 1: 43349abbd9cSPhilipp Skadorov val1 = cpu_to_le16(entry_value) & 0xf; 43449abbd9cSPhilipp Skadorov val2 = (cpu_to_le16(entry_value) >> 4) & 0xff; 43549abbd9cSPhilipp Skadorov 43649abbd9cSPhilipp Skadorov ((__u16 *)mydata->fatbuf)[off16] &= ~0xf000; 43749abbd9cSPhilipp Skadorov ((__u16 *)mydata->fatbuf)[off16] |= (val1 << 12); 43849abbd9cSPhilipp Skadorov 43949abbd9cSPhilipp Skadorov ((__u16 *)mydata->fatbuf)[off16 + 1] &= ~0xff; 44049abbd9cSPhilipp Skadorov ((__u16 *)mydata->fatbuf)[off16 + 1] |= val2; 44149abbd9cSPhilipp Skadorov break; 44249abbd9cSPhilipp Skadorov case 2: 44349abbd9cSPhilipp Skadorov val1 = cpu_to_le16(entry_value) & 0xff; 44449abbd9cSPhilipp Skadorov val2 = (cpu_to_le16(entry_value) >> 8) & 0xf; 44549abbd9cSPhilipp Skadorov 44649abbd9cSPhilipp Skadorov ((__u16 *)mydata->fatbuf)[off16] &= ~0xff00; 44749abbd9cSPhilipp Skadorov ((__u16 *)mydata->fatbuf)[off16] |= (val1 << 8); 44849abbd9cSPhilipp Skadorov 44949abbd9cSPhilipp Skadorov ((__u16 *)mydata->fatbuf)[off16 + 1] &= ~0xf; 45049abbd9cSPhilipp Skadorov ((__u16 *)mydata->fatbuf)[off16 + 1] |= val2; 45149abbd9cSPhilipp Skadorov break; 45249abbd9cSPhilipp Skadorov case 3: 45349abbd9cSPhilipp Skadorov val1 = cpu_to_le16(entry_value) & 0xfff; 45449abbd9cSPhilipp Skadorov ((__u16 *)mydata->fatbuf)[off16] &= ~0xfff0; 45549abbd9cSPhilipp Skadorov ((__u16 *)mydata->fatbuf)[off16] |= (val1 << 4); 45649abbd9cSPhilipp Skadorov break; 45749abbd9cSPhilipp Skadorov default: 45849abbd9cSPhilipp Skadorov break; 45949abbd9cSPhilipp Skadorov } 46049abbd9cSPhilipp Skadorov 46149abbd9cSPhilipp Skadorov break; 462c30a15e5SDonggeun Kim default: 463c30a15e5SDonggeun Kim return -1; 464c30a15e5SDonggeun Kim } 465c30a15e5SDonggeun Kim 466c30a15e5SDonggeun Kim return 0; 467c30a15e5SDonggeun Kim } 468c30a15e5SDonggeun Kim 469c30a15e5SDonggeun Kim /* 47049abbd9cSPhilipp Skadorov * Determine the next free cluster after 'entry' in a FAT (12/16/32) table 471ae1755beSStefan Brüns * and link it to 'entry'. EOC marker is not set on returned entry. 472c30a15e5SDonggeun Kim */ 473c30a15e5SDonggeun Kim static __u32 determine_fatent(fsdata *mydata, __u32 entry) 474c30a15e5SDonggeun Kim { 475c30a15e5SDonggeun Kim __u32 next_fat, next_entry = entry + 1; 476c30a15e5SDonggeun Kim 477c30a15e5SDonggeun Kim while (1) { 478*b8948d2aSStefan Brüns next_fat = get_fatent(mydata, next_entry); 479c30a15e5SDonggeun Kim if (next_fat == 0) { 480ae1755beSStefan Brüns /* found free entry, link to entry */ 481c30a15e5SDonggeun Kim set_fatent_value(mydata, entry, next_entry); 482c30a15e5SDonggeun Kim break; 483c30a15e5SDonggeun Kim } 484c30a15e5SDonggeun Kim next_entry++; 485c30a15e5SDonggeun Kim } 486c30a15e5SDonggeun Kim debug("FAT%d: entry: %08x, entry_value: %04x\n", 487c30a15e5SDonggeun Kim mydata->fatsize, entry, next_entry); 488c30a15e5SDonggeun Kim 489c30a15e5SDonggeun Kim return next_entry; 490c30a15e5SDonggeun Kim } 491c30a15e5SDonggeun Kim 492c30a15e5SDonggeun Kim /* 493c30a15e5SDonggeun Kim * Write at most 'size' bytes from 'buffer' into the specified cluster. 494c30a15e5SDonggeun Kim * Return 0 on success, -1 otherwise. 495c30a15e5SDonggeun Kim */ 496c30a15e5SDonggeun Kim static int 497c30a15e5SDonggeun Kim set_cluster(fsdata *mydata, __u32 clustnum, __u8 *buffer, 498c30a15e5SDonggeun Kim unsigned long size) 499c30a15e5SDonggeun Kim { 5008133f43dSBenoît Thébaudeau __u32 idx = 0; 501c30a15e5SDonggeun Kim __u32 startsect; 5028133f43dSBenoît Thébaudeau int ret; 503c30a15e5SDonggeun Kim 504c30a15e5SDonggeun Kim if (clustnum > 0) 505c30a15e5SDonggeun Kim startsect = mydata->data_begin + 506c30a15e5SDonggeun Kim clustnum * mydata->clust_size; 507c30a15e5SDonggeun Kim else 508c30a15e5SDonggeun Kim startsect = mydata->rootdir_sect; 509c30a15e5SDonggeun Kim 510c30a15e5SDonggeun Kim debug("clustnum: %d, startsect: %d\n", clustnum, startsect); 511c30a15e5SDonggeun Kim 5128133f43dSBenoît Thébaudeau if ((unsigned long)buffer & (ARCH_DMA_MINALIGN - 1)) { 5138133f43dSBenoît Thébaudeau ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size); 5148133f43dSBenoît Thébaudeau 5158133f43dSBenoît Thébaudeau printf("FAT: Misaligned buffer address (%p)\n", buffer); 5168133f43dSBenoît Thébaudeau 5178133f43dSBenoît Thébaudeau while (size >= mydata->sect_size) { 5188133f43dSBenoît Thébaudeau memcpy(tmpbuf, buffer, mydata->sect_size); 5198133f43dSBenoît Thébaudeau ret = disk_write(startsect++, 1, tmpbuf); 5208133f43dSBenoît Thébaudeau if (ret != 1) { 5218133f43dSBenoît Thébaudeau debug("Error writing data (got %d)\n", ret); 522c30a15e5SDonggeun Kim return -1; 523c30a15e5SDonggeun Kim } 5248133f43dSBenoît Thébaudeau 5258133f43dSBenoît Thébaudeau buffer += mydata->sect_size; 5268133f43dSBenoît Thébaudeau size -= mydata->sect_size; 5276b8f185fSWu, Josh } 5288133f43dSBenoît Thébaudeau } else if (size >= mydata->sect_size) { 529c30a15e5SDonggeun Kim idx = size / mydata->sect_size; 5308133f43dSBenoît Thébaudeau ret = disk_write(startsect, idx, buffer); 5318133f43dSBenoît Thébaudeau if (ret != idx) { 5328133f43dSBenoît Thébaudeau debug("Error writing data (got %d)\n", ret); 533c30a15e5SDonggeun Kim return -1; 534c30a15e5SDonggeun Kim } 535c30a15e5SDonggeun Kim 5368133f43dSBenoît Thébaudeau startsect += idx; 5378133f43dSBenoît Thébaudeau idx *= mydata->sect_size; 5388133f43dSBenoît Thébaudeau buffer += idx; 5398133f43dSBenoît Thébaudeau size -= idx; 5408133f43dSBenoît Thébaudeau } 5418133f43dSBenoît Thébaudeau 5428133f43dSBenoît Thébaudeau if (size) { 5438133f43dSBenoît Thébaudeau ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size); 5448133f43dSBenoît Thébaudeau 5458133f43dSBenoît Thébaudeau memcpy(tmpbuf, buffer, size); 5468133f43dSBenoît Thébaudeau ret = disk_write(startsect, 1, tmpbuf); 5478133f43dSBenoît Thébaudeau if (ret != 1) { 5488133f43dSBenoît Thébaudeau debug("Error writing data (got %d)\n", ret); 5498133f43dSBenoît Thébaudeau return -1; 5508133f43dSBenoît Thébaudeau } 551c30a15e5SDonggeun Kim } 552c30a15e5SDonggeun Kim 553c30a15e5SDonggeun Kim return 0; 554c30a15e5SDonggeun Kim } 555c30a15e5SDonggeun Kim 556c30a15e5SDonggeun Kim /* 557c30a15e5SDonggeun Kim * Find the first empty cluster 558c30a15e5SDonggeun Kim */ 559c30a15e5SDonggeun Kim static int find_empty_cluster(fsdata *mydata) 560c30a15e5SDonggeun Kim { 561c30a15e5SDonggeun Kim __u32 fat_val, entry = 3; 562c30a15e5SDonggeun Kim 563c30a15e5SDonggeun Kim while (1) { 564*b8948d2aSStefan Brüns fat_val = get_fatent(mydata, entry); 565c30a15e5SDonggeun Kim if (fat_val == 0) 566c30a15e5SDonggeun Kim break; 567c30a15e5SDonggeun Kim entry++; 568c30a15e5SDonggeun Kim } 569c30a15e5SDonggeun Kim 570c30a15e5SDonggeun Kim return entry; 571c30a15e5SDonggeun Kim } 572c30a15e5SDonggeun Kim 573c30a15e5SDonggeun Kim /* 574c30a15e5SDonggeun Kim * Write directory entries in 'get_dentfromdir_block' to block device 575c30a15e5SDonggeun Kim */ 576c30a15e5SDonggeun Kim static void flush_dir_table(fsdata *mydata, dir_entry **dentptr) 577c30a15e5SDonggeun Kim { 578c30a15e5SDonggeun Kim int dir_newclust = 0; 579c30a15e5SDonggeun Kim 580c30a15e5SDonggeun Kim if (set_cluster(mydata, dir_curclust, 581c30a15e5SDonggeun Kim get_dentfromdir_block, 582c30a15e5SDonggeun Kim mydata->clust_size * mydata->sect_size) != 0) { 583c30a15e5SDonggeun Kim printf("error: wrinting directory entry\n"); 584c30a15e5SDonggeun Kim return; 585c30a15e5SDonggeun Kim } 586c30a15e5SDonggeun Kim dir_newclust = find_empty_cluster(mydata); 587c30a15e5SDonggeun Kim set_fatent_value(mydata, dir_curclust, dir_newclust); 588c30a15e5SDonggeun Kim if (mydata->fatsize == 32) 589c30a15e5SDonggeun Kim set_fatent_value(mydata, dir_newclust, 0xffffff8); 590c30a15e5SDonggeun Kim else if (mydata->fatsize == 16) 591c30a15e5SDonggeun Kim set_fatent_value(mydata, dir_newclust, 0xfff8); 59249abbd9cSPhilipp Skadorov else if (mydata->fatsize == 12) 59349abbd9cSPhilipp Skadorov set_fatent_value(mydata, dir_newclust, 0xff8); 594c30a15e5SDonggeun Kim 595c30a15e5SDonggeun Kim dir_curclust = dir_newclust; 596c30a15e5SDonggeun Kim 5973c0ed9c3SStefan Brüns if (flush_dirty_fat_buffer(mydata) < 0) 598c30a15e5SDonggeun Kim return; 599c30a15e5SDonggeun Kim 600c30a15e5SDonggeun Kim memset(get_dentfromdir_block, 0x00, 601c30a15e5SDonggeun Kim mydata->clust_size * mydata->sect_size); 602c30a15e5SDonggeun Kim 603c30a15e5SDonggeun Kim *dentptr = (dir_entry *) get_dentfromdir_block; 604c30a15e5SDonggeun Kim } 605c30a15e5SDonggeun Kim 606c30a15e5SDonggeun Kim /* 607c30a15e5SDonggeun Kim * Set empty cluster from 'entry' to the end of a file 608c30a15e5SDonggeun Kim */ 609c30a15e5SDonggeun Kim static int clear_fatent(fsdata *mydata, __u32 entry) 610c30a15e5SDonggeun Kim { 611c30a15e5SDonggeun Kim __u32 fat_val; 612c30a15e5SDonggeun Kim 61349abbd9cSPhilipp Skadorov while (!CHECK_CLUST(entry, mydata->fatsize)) { 614*b8948d2aSStefan Brüns fat_val = get_fatent(mydata, entry); 615c30a15e5SDonggeun Kim if (fat_val != 0) 616c30a15e5SDonggeun Kim set_fatent_value(mydata, entry, 0); 617c30a15e5SDonggeun Kim else 618c30a15e5SDonggeun Kim break; 619c30a15e5SDonggeun Kim 620c30a15e5SDonggeun Kim entry = fat_val; 621c30a15e5SDonggeun Kim } 622c30a15e5SDonggeun Kim 623c30a15e5SDonggeun Kim /* Flush fat buffer */ 6243c0ed9c3SStefan Brüns if (flush_dirty_fat_buffer(mydata) < 0) 625c30a15e5SDonggeun Kim return -1; 626c30a15e5SDonggeun Kim 627c30a15e5SDonggeun Kim return 0; 628c30a15e5SDonggeun Kim } 629c30a15e5SDonggeun Kim 630c30a15e5SDonggeun Kim /* 631c30a15e5SDonggeun Kim * Write at most 'maxsize' bytes from 'buffer' into 632c30a15e5SDonggeun Kim * the file associated with 'dentptr' 6331ad0b98aSSuriyan Ramasami * Update the number of bytes written in *gotsize and return 0 6341ad0b98aSSuriyan Ramasami * or return -1 on fatal errors. 635c30a15e5SDonggeun Kim */ 636c30a15e5SDonggeun Kim static int 637c30a15e5SDonggeun Kim set_contents(fsdata *mydata, dir_entry *dentptr, __u8 *buffer, 6381ad0b98aSSuriyan Ramasami loff_t maxsize, loff_t *gotsize) 639c30a15e5SDonggeun Kim { 6401ad0b98aSSuriyan Ramasami loff_t filesize = FAT2CPU32(dentptr->size); 641c30a15e5SDonggeun Kim unsigned int bytesperclust = mydata->clust_size * mydata->sect_size; 642c30a15e5SDonggeun Kim __u32 curclust = START(dentptr); 643c30a15e5SDonggeun Kim __u32 endclust = 0, newclust = 0; 6441ad0b98aSSuriyan Ramasami loff_t actsize; 645c30a15e5SDonggeun Kim 6461ad0b98aSSuriyan Ramasami *gotsize = 0; 6471ad0b98aSSuriyan Ramasami debug("Filesize: %llu bytes\n", filesize); 648c30a15e5SDonggeun Kim 649c30a15e5SDonggeun Kim if (maxsize > 0 && filesize > maxsize) 650c30a15e5SDonggeun Kim filesize = maxsize; 651c30a15e5SDonggeun Kim 6521ad0b98aSSuriyan Ramasami debug("%llu bytes\n", filesize); 653c30a15e5SDonggeun Kim 6541254b44aSBenoît Thébaudeau if (!curclust) { 6551254b44aSBenoît Thébaudeau if (filesize) { 6561254b44aSBenoît Thébaudeau debug("error: nonempty clusterless file!\n"); 6571254b44aSBenoît Thébaudeau return -1; 6581254b44aSBenoît Thébaudeau } 6591254b44aSBenoît Thébaudeau return 0; 6601254b44aSBenoît Thébaudeau } 6611254b44aSBenoît Thébaudeau 662c30a15e5SDonggeun Kim actsize = bytesperclust; 663c30a15e5SDonggeun Kim endclust = curclust; 664c30a15e5SDonggeun Kim do { 665c30a15e5SDonggeun Kim /* search for consecutive clusters */ 666c30a15e5SDonggeun Kim while (actsize < filesize) { 667c30a15e5SDonggeun Kim newclust = determine_fatent(mydata, endclust); 668c30a15e5SDonggeun Kim 669c30a15e5SDonggeun Kim if ((newclust - 1) != endclust) 670c30a15e5SDonggeun Kim goto getit; 671c30a15e5SDonggeun Kim 672c30a15e5SDonggeun Kim if (CHECK_CLUST(newclust, mydata->fatsize)) { 6735e1a860eSBenoît Thébaudeau debug("newclust: 0x%x\n", newclust); 674c30a15e5SDonggeun Kim debug("Invalid FAT entry\n"); 6751ad0b98aSSuriyan Ramasami return 0; 676c30a15e5SDonggeun Kim } 677c30a15e5SDonggeun Kim endclust = newclust; 678c30a15e5SDonggeun Kim actsize += bytesperclust; 679c30a15e5SDonggeun Kim } 680c30a15e5SDonggeun Kim 681c30a15e5SDonggeun Kim /* set remaining bytes */ 682c30a15e5SDonggeun Kim actsize = filesize; 6831d7f2eceSBenoît Thébaudeau if (set_cluster(mydata, curclust, buffer, (int)actsize) != 0) { 684c30a15e5SDonggeun Kim debug("error: writing cluster\n"); 685c30a15e5SDonggeun Kim return -1; 686c30a15e5SDonggeun Kim } 6871ad0b98aSSuriyan Ramasami *gotsize += actsize; 688c30a15e5SDonggeun Kim 689c30a15e5SDonggeun Kim /* Mark end of file in FAT */ 69049abbd9cSPhilipp Skadorov if (mydata->fatsize == 12) 69149abbd9cSPhilipp Skadorov newclust = 0xfff; 69249abbd9cSPhilipp Skadorov else if (mydata->fatsize == 16) 693c30a15e5SDonggeun Kim newclust = 0xffff; 694c30a15e5SDonggeun Kim else if (mydata->fatsize == 32) 695c30a15e5SDonggeun Kim newclust = 0xfffffff; 696c30a15e5SDonggeun Kim set_fatent_value(mydata, endclust, newclust); 697c30a15e5SDonggeun Kim 6981ad0b98aSSuriyan Ramasami return 0; 699c30a15e5SDonggeun Kim getit: 700c30a15e5SDonggeun Kim if (set_cluster(mydata, curclust, buffer, (int)actsize) != 0) { 701c30a15e5SDonggeun Kim debug("error: writing cluster\n"); 702c30a15e5SDonggeun Kim return -1; 703c30a15e5SDonggeun Kim } 7041ad0b98aSSuriyan Ramasami *gotsize += actsize; 705c30a15e5SDonggeun Kim filesize -= actsize; 706c30a15e5SDonggeun Kim buffer += actsize; 707c30a15e5SDonggeun Kim 7085e1a860eSBenoît Thébaudeau if (CHECK_CLUST(newclust, mydata->fatsize)) { 7095e1a860eSBenoît Thébaudeau debug("newclust: 0x%x\n", newclust); 710c30a15e5SDonggeun Kim debug("Invalid FAT entry\n"); 7111ad0b98aSSuriyan Ramasami return 0; 712c30a15e5SDonggeun Kim } 713c30a15e5SDonggeun Kim actsize = bytesperclust; 714c30a15e5SDonggeun Kim curclust = endclust = newclust; 715c30a15e5SDonggeun Kim } while (1); 716c30a15e5SDonggeun Kim } 717c30a15e5SDonggeun Kim 718c30a15e5SDonggeun Kim /* 7191254b44aSBenoît Thébaudeau * Set start cluster in directory entry 720c30a15e5SDonggeun Kim */ 7211254b44aSBenoît Thébaudeau static void set_start_cluster(const fsdata *mydata, dir_entry *dentptr, 7221254b44aSBenoît Thébaudeau __u32 start_cluster) 723c30a15e5SDonggeun Kim { 724c30a15e5SDonggeun Kim if (mydata->fatsize == 32) 725c30a15e5SDonggeun Kim dentptr->starthi = 726c30a15e5SDonggeun Kim cpu_to_le16((start_cluster & 0xffff0000) >> 16); 727c30a15e5SDonggeun Kim dentptr->start = cpu_to_le16(start_cluster & 0xffff); 7281254b44aSBenoît Thébaudeau } 7291254b44aSBenoît Thébaudeau 7301254b44aSBenoît Thébaudeau /* 7311254b44aSBenoît Thébaudeau * Fill dir_entry 7321254b44aSBenoît Thébaudeau */ 7331254b44aSBenoît Thébaudeau static void fill_dentry(fsdata *mydata, dir_entry *dentptr, 7341254b44aSBenoît Thébaudeau const char *filename, __u32 start_cluster, __u32 size, __u8 attr) 7351254b44aSBenoît Thébaudeau { 7361254b44aSBenoît Thébaudeau set_start_cluster(mydata, dentptr, start_cluster); 737c30a15e5SDonggeun Kim dentptr->size = cpu_to_le32(size); 738c30a15e5SDonggeun Kim 739c30a15e5SDonggeun Kim dentptr->attr = attr; 740c30a15e5SDonggeun Kim 741c30a15e5SDonggeun Kim set_name(dentptr, filename); 742c30a15e5SDonggeun Kim } 743c30a15e5SDonggeun Kim 744c30a15e5SDonggeun Kim /* 745c30a15e5SDonggeun Kim * Check whether adding a file makes the file system to 746c30a15e5SDonggeun Kim * exceed the size of the block device 747c30a15e5SDonggeun Kim * Return -1 when overflow occurs, otherwise return 0 748c30a15e5SDonggeun Kim */ 7491ad0b98aSSuriyan Ramasami static int check_overflow(fsdata *mydata, __u32 clustnum, loff_t size) 750c30a15e5SDonggeun Kim { 7519e374e7bSTom Rini __u32 startsect, sect_num, offset; 752c30a15e5SDonggeun Kim 753c30a15e5SDonggeun Kim if (clustnum > 0) { 754c30a15e5SDonggeun Kim startsect = mydata->data_begin + 755c30a15e5SDonggeun Kim clustnum * mydata->clust_size; 756c30a15e5SDonggeun Kim } else { 757c30a15e5SDonggeun Kim startsect = mydata->rootdir_sect; 758c30a15e5SDonggeun Kim } 759c30a15e5SDonggeun Kim 7609e374e7bSTom Rini sect_num = div_u64_rem(size, mydata->sect_size, &offset); 7619e374e7bSTom Rini 7629e374e7bSTom Rini if (offset != 0) 763c30a15e5SDonggeun Kim sect_num++; 764c30a15e5SDonggeun Kim 765079df722SDonggeun Kim if (startsect + sect_num > cur_part_info.start + total_sector) 766c30a15e5SDonggeun Kim return -1; 767c30a15e5SDonggeun Kim return 0; 768c30a15e5SDonggeun Kim } 769c30a15e5SDonggeun Kim 770c30a15e5SDonggeun Kim /* 771c30a15e5SDonggeun Kim * Check if adding several entries exceed one cluster boundary 772c30a15e5SDonggeun Kim */ 773c30a15e5SDonggeun Kim static int is_next_clust(fsdata *mydata, dir_entry *dentptr) 774c30a15e5SDonggeun Kim { 775c30a15e5SDonggeun Kim int cur_position; 776c30a15e5SDonggeun Kim 777c30a15e5SDonggeun Kim cur_position = (__u8 *)dentptr - get_dentfromdir_block; 778c30a15e5SDonggeun Kim 779c30a15e5SDonggeun Kim if (cur_position >= mydata->clust_size * mydata->sect_size) 780c30a15e5SDonggeun Kim return 1; 781c30a15e5SDonggeun Kim else 782c30a15e5SDonggeun Kim return 0; 783c30a15e5SDonggeun Kim } 784c30a15e5SDonggeun Kim 785c30a15e5SDonggeun Kim static dir_entry *empty_dentptr; 786c30a15e5SDonggeun Kim /* 787c30a15e5SDonggeun Kim * Find a directory entry based on filename or start cluster number 788c30a15e5SDonggeun Kim * If the directory entry is not found, 789c30a15e5SDonggeun Kim * the new position for writing a directory entry will be returned 790c30a15e5SDonggeun Kim */ 791c30a15e5SDonggeun Kim static dir_entry *find_directory_entry(fsdata *mydata, int startsect, 792c30a15e5SDonggeun Kim char *filename, dir_entry *retdent, __u32 start) 793c30a15e5SDonggeun Kim { 794c30a15e5SDonggeun Kim __u32 curclust = (startsect - mydata->data_begin) / mydata->clust_size; 795c30a15e5SDonggeun Kim 796c30a15e5SDonggeun Kim debug("get_dentfromdir: %s\n", filename); 797c30a15e5SDonggeun Kim 798c30a15e5SDonggeun Kim while (1) { 799c30a15e5SDonggeun Kim dir_entry *dentptr; 800c30a15e5SDonggeun Kim 801c30a15e5SDonggeun Kim int i; 802c30a15e5SDonggeun Kim 803c30a15e5SDonggeun Kim if (get_cluster(mydata, curclust, get_dentfromdir_block, 804c30a15e5SDonggeun Kim mydata->clust_size * mydata->sect_size) != 0) { 805c30a15e5SDonggeun Kim printf("Error: reading directory block\n"); 806c30a15e5SDonggeun Kim return NULL; 807c30a15e5SDonggeun Kim } 808c30a15e5SDonggeun Kim 809c30a15e5SDonggeun Kim dentptr = (dir_entry *)get_dentfromdir_block; 810c30a15e5SDonggeun Kim 811c30a15e5SDonggeun Kim dir_curclust = curclust; 812c30a15e5SDonggeun Kim 813c30a15e5SDonggeun Kim for (i = 0; i < DIRENTSPERCLUST; i++) { 814c30a15e5SDonggeun Kim char s_name[14], l_name[VFAT_MAXLEN_BYTES]; 815c30a15e5SDonggeun Kim 816c30a15e5SDonggeun Kim l_name[0] = '\0'; 817c30a15e5SDonggeun Kim if (dentptr->name[0] == DELETED_FLAG) { 818c30a15e5SDonggeun Kim dentptr++; 819c30a15e5SDonggeun Kim if (is_next_clust(mydata, dentptr)) 820c30a15e5SDonggeun Kim break; 821c30a15e5SDonggeun Kim continue; 822c30a15e5SDonggeun Kim } 823c30a15e5SDonggeun Kim if ((dentptr->attr & ATTR_VOLUME)) { 824cb940c7eSRichard Genoud if (vfat_enabled && 825cb940c7eSRichard Genoud (dentptr->attr & ATTR_VFAT) && 826c30a15e5SDonggeun Kim (dentptr->name[0] & LAST_LONG_ENTRY_MASK)) { 827c30a15e5SDonggeun Kim get_long_file_name(mydata, curclust, 828c30a15e5SDonggeun Kim get_dentfromdir_block, 829c30a15e5SDonggeun Kim &dentptr, l_name); 830c30a15e5SDonggeun Kim debug("vfatname: |%s|\n", l_name); 831cb940c7eSRichard Genoud } else { 832c30a15e5SDonggeun Kim /* Volume label or VFAT entry */ 833c30a15e5SDonggeun Kim dentptr++; 834c30a15e5SDonggeun Kim if (is_next_clust(mydata, dentptr)) 835c30a15e5SDonggeun Kim break; 836c30a15e5SDonggeun Kim continue; 837c30a15e5SDonggeun Kim } 838c30a15e5SDonggeun Kim } 839c30a15e5SDonggeun Kim if (dentptr->name[0] == 0) { 840c30a15e5SDonggeun Kim debug("Dentname == NULL - %d\n", i); 841c30a15e5SDonggeun Kim empty_dentptr = dentptr; 842c30a15e5SDonggeun Kim return NULL; 843c30a15e5SDonggeun Kim } 844c30a15e5SDonggeun Kim 845c30a15e5SDonggeun Kim get_name(dentptr, s_name); 846c30a15e5SDonggeun Kim 847c30a15e5SDonggeun Kim if (strcmp(filename, s_name) 848c30a15e5SDonggeun Kim && strcmp(filename, l_name)) { 849c30a15e5SDonggeun Kim debug("Mismatch: |%s|%s|\n", 850c30a15e5SDonggeun Kim s_name, l_name); 851c30a15e5SDonggeun Kim dentptr++; 852c30a15e5SDonggeun Kim if (is_next_clust(mydata, dentptr)) 853c30a15e5SDonggeun Kim break; 854c30a15e5SDonggeun Kim continue; 855c30a15e5SDonggeun Kim } 856c30a15e5SDonggeun Kim 857c30a15e5SDonggeun Kim memcpy(retdent, dentptr, sizeof(dir_entry)); 858c30a15e5SDonggeun Kim 859c30a15e5SDonggeun Kim debug("DentName: %s", s_name); 860c30a15e5SDonggeun Kim debug(", start: 0x%x", START(dentptr)); 861c30a15e5SDonggeun Kim debug(", size: 0x%x %s\n", 862c30a15e5SDonggeun Kim FAT2CPU32(dentptr->size), 863c30a15e5SDonggeun Kim (dentptr->attr & ATTR_DIR) ? 864c30a15e5SDonggeun Kim "(DIR)" : ""); 865c30a15e5SDonggeun Kim 866c30a15e5SDonggeun Kim return dentptr; 867c30a15e5SDonggeun Kim } 868c30a15e5SDonggeun Kim 869dd6d7967SWu, Josh /* 870dd6d7967SWu, Josh * In FAT16/12, the root dir is locate before data area, shows 871dd6d7967SWu, Josh * in following: 872dd6d7967SWu, Josh * ------------------------------------------------------------- 873dd6d7967SWu, Josh * | Boot | FAT1 & 2 | Root dir | Data (start from cluster #2) | 874dd6d7967SWu, Josh * ------------------------------------------------------------- 875dd6d7967SWu, Josh * 876dd6d7967SWu, Josh * As a result if curclust is in Root dir, it is a negative 877dd6d7967SWu, Josh * number or 0, 1. 878dd6d7967SWu, Josh * 879dd6d7967SWu, Josh */ 880dd6d7967SWu, Josh if (mydata->fatsize != 32 && (int)curclust <= 1) { 881dd6d7967SWu, Josh /* Current clust is in root dir, set to next clust */ 882dd6d7967SWu, Josh curclust++; 883dd6d7967SWu, Josh if ((int)curclust <= 1) 884dd6d7967SWu, Josh continue; /* continue to find */ 885dd6d7967SWu, Josh 886dd6d7967SWu, Josh /* Reach the end of root dir */ 887dd6d7967SWu, Josh empty_dentptr = dentptr; 888dd6d7967SWu, Josh return NULL; 889dd6d7967SWu, Josh } 890dd6d7967SWu, Josh 891*b8948d2aSStefan Brüns curclust = get_fatent(mydata, dir_curclust); 8922e98f708SWu, Josh if (IS_LAST_CLUST(curclust, mydata->fatsize)) { 893c30a15e5SDonggeun Kim empty_dentptr = dentptr; 894c30a15e5SDonggeun Kim return NULL; 895c30a15e5SDonggeun Kim } 896c30a15e5SDonggeun Kim if (CHECK_CLUST(curclust, mydata->fatsize)) { 897c30a15e5SDonggeun Kim debug("curclust: 0x%x\n", curclust); 898c30a15e5SDonggeun Kim debug("Invalid FAT entry\n"); 899c30a15e5SDonggeun Kim return NULL; 900c30a15e5SDonggeun Kim } 901c30a15e5SDonggeun Kim } 902c30a15e5SDonggeun Kim 903c30a15e5SDonggeun Kim return NULL; 904c30a15e5SDonggeun Kim } 905c30a15e5SDonggeun Kim 9061ad0b98aSSuriyan Ramasami static int do_fat_write(const char *filename, void *buffer, loff_t size, 9071ad0b98aSSuriyan Ramasami loff_t *actwrite) 908c30a15e5SDonggeun Kim { 909c30a15e5SDonggeun Kim dir_entry *dentptr, *retdent; 910c30a15e5SDonggeun Kim __u32 startsect; 911c30a15e5SDonggeun Kim __u32 start_cluster; 912c30a15e5SDonggeun Kim boot_sector bs; 913c30a15e5SDonggeun Kim volume_info volinfo; 914c30a15e5SDonggeun Kim fsdata datablock; 915c30a15e5SDonggeun Kim fsdata *mydata = &datablock; 916c30a15e5SDonggeun Kim int cursect; 917bf6b6af7SAnatolij Gustschin int ret = -1, name_len; 918c30a15e5SDonggeun Kim char l_filename[VFAT_MAXLEN_BYTES]; 919c30a15e5SDonggeun Kim 9201ad0b98aSSuriyan Ramasami *actwrite = size; 921c30a15e5SDonggeun Kim dir_curclust = 0; 922c30a15e5SDonggeun Kim 923c30a15e5SDonggeun Kim if (read_bootsectandvi(&bs, &volinfo, &mydata->fatsize)) { 924c30a15e5SDonggeun Kim debug("error: reading boot sector\n"); 925c30a15e5SDonggeun Kim return -1; 926c30a15e5SDonggeun Kim } 927c30a15e5SDonggeun Kim 928c30a15e5SDonggeun Kim total_sector = bs.total_sect; 929c30a15e5SDonggeun Kim if (total_sector == 0) 930e04350d2SSteve Rae total_sector = (int)cur_part_info.size; /* cast of lbaint_t */ 931c30a15e5SDonggeun Kim 932c30a15e5SDonggeun Kim if (mydata->fatsize == 32) 933c30a15e5SDonggeun Kim mydata->fatlength = bs.fat32_length; 934c30a15e5SDonggeun Kim else 935c30a15e5SDonggeun Kim mydata->fatlength = bs.fat_length; 936c30a15e5SDonggeun Kim 937c30a15e5SDonggeun Kim mydata->fat_sect = bs.reserved; 938c30a15e5SDonggeun Kim 939c30a15e5SDonggeun Kim cursect = mydata->rootdir_sect 940c30a15e5SDonggeun Kim = mydata->fat_sect + mydata->fatlength * bs.fats; 941627182eaSDonggeun Kim num_of_fats = bs.fats; 942c30a15e5SDonggeun Kim 943c30a15e5SDonggeun Kim mydata->sect_size = (bs.sector_size[1] << 8) + bs.sector_size[0]; 944c30a15e5SDonggeun Kim mydata->clust_size = bs.cluster_size; 945c30a15e5SDonggeun Kim 946c30a15e5SDonggeun Kim if (mydata->fatsize == 32) { 947c30a15e5SDonggeun Kim mydata->data_begin = mydata->rootdir_sect - 948c30a15e5SDonggeun Kim (mydata->clust_size * 2); 949c30a15e5SDonggeun Kim } else { 950c30a15e5SDonggeun Kim int rootdir_size; 951c30a15e5SDonggeun Kim 952c30a15e5SDonggeun Kim rootdir_size = ((bs.dir_entries[1] * (int)256 + 953c30a15e5SDonggeun Kim bs.dir_entries[0]) * 954c30a15e5SDonggeun Kim sizeof(dir_entry)) / 955c30a15e5SDonggeun Kim mydata->sect_size; 956c30a15e5SDonggeun Kim mydata->data_begin = mydata->rootdir_sect + 957c30a15e5SDonggeun Kim rootdir_size - 958c30a15e5SDonggeun Kim (mydata->clust_size * 2); 959c30a15e5SDonggeun Kim } 960c30a15e5SDonggeun Kim 961c30a15e5SDonggeun Kim mydata->fatbufnum = -1; 9623c0ed9c3SStefan Brüns mydata->fat_dirty = 0; 9638abd053cSNobuhiro Iwamatsu mydata->fatbuf = memalign(ARCH_DMA_MINALIGN, FATBUFSIZE); 964c30a15e5SDonggeun Kim if (mydata->fatbuf == NULL) { 965c30a15e5SDonggeun Kim debug("Error: allocating memory\n"); 966c30a15e5SDonggeun Kim return -1; 967c30a15e5SDonggeun Kim } 968c30a15e5SDonggeun Kim 969c30a15e5SDonggeun Kim if (disk_read(cursect, 970c30a15e5SDonggeun Kim (mydata->fatsize == 32) ? 971c30a15e5SDonggeun Kim (mydata->clust_size) : 9721170e634SBenoît Thébaudeau PREFETCH_BLOCKS, do_fat_read_at_block) < 0) { 973c30a15e5SDonggeun Kim debug("Error: reading rootdir block\n"); 974c30a15e5SDonggeun Kim goto exit; 975c30a15e5SDonggeun Kim } 9761170e634SBenoît Thébaudeau dentptr = (dir_entry *) do_fat_read_at_block; 977c30a15e5SDonggeun Kim 978c30a15e5SDonggeun Kim name_len = strlen(filename); 9798506eb8dSAnatolij Gustschin if (name_len >= VFAT_MAXLEN_BYTES) 9808506eb8dSAnatolij Gustschin name_len = VFAT_MAXLEN_BYTES - 1; 9818506eb8dSAnatolij Gustschin 982c30a15e5SDonggeun Kim memcpy(l_filename, filename, name_len); 9838506eb8dSAnatolij Gustschin l_filename[name_len] = 0; /* terminate the string */ 984c30a15e5SDonggeun Kim downcase(l_filename); 985c30a15e5SDonggeun Kim 986c30a15e5SDonggeun Kim startsect = mydata->rootdir_sect; 987c30a15e5SDonggeun Kim retdent = find_directory_entry(mydata, startsect, 988c30a15e5SDonggeun Kim l_filename, dentptr, 0); 989c30a15e5SDonggeun Kim if (retdent) { 990c30a15e5SDonggeun Kim /* Update file size and start_cluster in a directory entry */ 991c30a15e5SDonggeun Kim retdent->size = cpu_to_le32(size); 992e876be4bSBenoît Thébaudeau start_cluster = START(retdent); 993c30a15e5SDonggeun Kim 9941254b44aSBenoît Thébaudeau if (start_cluster) { 9951254b44aSBenoît Thébaudeau if (size) { 9961254b44aSBenoît Thébaudeau ret = check_overflow(mydata, start_cluster, 9971254b44aSBenoît Thébaudeau size); 998c30a15e5SDonggeun Kim if (ret) { 9991ad0b98aSSuriyan Ramasami printf("Error: %llu overflow\n", size); 1000c30a15e5SDonggeun Kim goto exit; 1001c30a15e5SDonggeun Kim } 10021254b44aSBenoît Thébaudeau } 1003c30a15e5SDonggeun Kim 1004c30a15e5SDonggeun Kim ret = clear_fatent(mydata, start_cluster); 1005c30a15e5SDonggeun Kim if (ret) { 1006c30a15e5SDonggeun Kim printf("Error: clearing FAT entries\n"); 1007c30a15e5SDonggeun Kim goto exit; 1008c30a15e5SDonggeun Kim } 1009c30a15e5SDonggeun Kim 10101254b44aSBenoît Thébaudeau if (!size) 10111254b44aSBenoît Thébaudeau set_start_cluster(mydata, retdent, 0); 10121254b44aSBenoît Thébaudeau } else if (size) { 1013c30a15e5SDonggeun Kim ret = start_cluster = find_empty_cluster(mydata); 1014c30a15e5SDonggeun Kim if (ret < 0) { 1015c30a15e5SDonggeun Kim printf("Error: finding empty cluster\n"); 1016c30a15e5SDonggeun Kim goto exit; 1017c30a15e5SDonggeun Kim } 1018c30a15e5SDonggeun Kim 1019c30a15e5SDonggeun Kim ret = check_overflow(mydata, start_cluster, size); 1020c30a15e5SDonggeun Kim if (ret) { 10211ad0b98aSSuriyan Ramasami printf("Error: %llu overflow\n", size); 1022c30a15e5SDonggeun Kim goto exit; 1023c30a15e5SDonggeun Kim } 1024c30a15e5SDonggeun Kim 10251254b44aSBenoît Thébaudeau set_start_cluster(mydata, retdent, start_cluster); 10261254b44aSBenoît Thébaudeau } 10271254b44aSBenoît Thébaudeau } else { 10281254b44aSBenoît Thébaudeau /* Set short name to set alias checksum field in dir_slot */ 10291254b44aSBenoît Thébaudeau set_name(empty_dentptr, filename); 10301254b44aSBenoît Thébaudeau fill_dir_slot(mydata, &empty_dentptr, filename); 10311254b44aSBenoît Thébaudeau 10321254b44aSBenoît Thébaudeau if (size) { 10331254b44aSBenoît Thébaudeau ret = start_cluster = find_empty_cluster(mydata); 10341254b44aSBenoît Thébaudeau if (ret < 0) { 10351254b44aSBenoît Thébaudeau printf("Error: finding empty cluster\n"); 10361254b44aSBenoît Thébaudeau goto exit; 10371254b44aSBenoît Thébaudeau } 10381254b44aSBenoît Thébaudeau 10391254b44aSBenoît Thébaudeau ret = check_overflow(mydata, start_cluster, size); 10401254b44aSBenoît Thébaudeau if (ret) { 10411254b44aSBenoît Thébaudeau printf("Error: %llu overflow\n", size); 10421254b44aSBenoît Thébaudeau goto exit; 10431254b44aSBenoît Thébaudeau } 10441254b44aSBenoît Thébaudeau } else { 10451254b44aSBenoît Thébaudeau start_cluster = 0; 10461254b44aSBenoît Thébaudeau } 10471254b44aSBenoît Thébaudeau 1048c30a15e5SDonggeun Kim /* Set attribute as archieve for regular file */ 1049c30a15e5SDonggeun Kim fill_dentry(mydata, empty_dentptr, filename, 1050c30a15e5SDonggeun Kim start_cluster, size, 0x20); 1051c30a15e5SDonggeun Kim 1052e876be4bSBenoît Thébaudeau retdent = empty_dentptr; 1053e876be4bSBenoît Thébaudeau } 1054e876be4bSBenoît Thébaudeau 1055e876be4bSBenoît Thébaudeau ret = set_contents(mydata, retdent, buffer, size, actwrite); 10568506eb8dSAnatolij Gustschin if (ret < 0) { 1057c30a15e5SDonggeun Kim printf("Error: writing contents\n"); 1058c30a15e5SDonggeun Kim goto exit; 1059c30a15e5SDonggeun Kim } 10601ad0b98aSSuriyan Ramasami debug("attempt to write 0x%llx bytes\n", *actwrite); 1061c30a15e5SDonggeun Kim 1062c30a15e5SDonggeun Kim /* Flush fat buffer */ 10633c0ed9c3SStefan Brüns ret = flush_dirty_fat_buffer(mydata); 1064c30a15e5SDonggeun Kim if (ret) { 1065c30a15e5SDonggeun Kim printf("Error: flush fat buffer\n"); 1066c30a15e5SDonggeun Kim goto exit; 1067c30a15e5SDonggeun Kim } 1068c30a15e5SDonggeun Kim 1069c30a15e5SDonggeun Kim /* Write directory table to device */ 1070e876be4bSBenoît Thébaudeau ret = set_cluster(mydata, dir_curclust, get_dentfromdir_block, 1071c30a15e5SDonggeun Kim mydata->clust_size * mydata->sect_size); 1072e876be4bSBenoît Thébaudeau if (ret) 1073c30a15e5SDonggeun Kim printf("Error: writing directory entry\n"); 1074c30a15e5SDonggeun Kim 1075c30a15e5SDonggeun Kim exit: 1076c30a15e5SDonggeun Kim free(mydata->fatbuf); 10771ad0b98aSSuriyan Ramasami return ret; 1078c30a15e5SDonggeun Kim } 1079c30a15e5SDonggeun Kim 10801ad0b98aSSuriyan Ramasami int file_fat_write(const char *filename, void *buffer, loff_t offset, 10811ad0b98aSSuriyan Ramasami loff_t maxsize, loff_t *actwrite) 1082c30a15e5SDonggeun Kim { 10831ad0b98aSSuriyan Ramasami if (offset != 0) { 10840af49b95SVagrant Cascadian printf("Error: non zero offset is currently not supported.\n"); 10851ad0b98aSSuriyan Ramasami return -1; 10861ad0b98aSSuriyan Ramasami } 10871ad0b98aSSuriyan Ramasami 1088c30a15e5SDonggeun Kim printf("writing %s\n", filename); 10891ad0b98aSSuriyan Ramasami return do_fat_write(filename, buffer, maxsize, actwrite); 1090c30a15e5SDonggeun Kim } 1091