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