summaryrefslogtreecommitdiff
path: root/fs/fat/fat_write.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/fat/fat_write.c')
-rw-r--r--fs/fat/fat_write.c165
1 files changed, 93 insertions, 72 deletions
diff --git a/fs/fat/fat_write.c b/fs/fat/fat_write.c
index 852f874e581..729cf39630d 100644
--- a/fs/fat/fat_write.c
+++ b/fs/fat/fat_write.c
@@ -209,7 +209,8 @@ name11_12:
return 1;
}
-static int flush_dir_table(fat_itr *itr);
+static int new_dir_table(fat_itr *itr);
+static int flush_dir(fat_itr *itr);
/*
* Fill dir_slot entries with appropriate name, id, and attr
@@ -242,19 +243,18 @@ fill_dir_slot(fat_itr *itr, const char *l_name)
memcpy(itr->dent, slotptr, sizeof(dir_slot));
slotptr--;
counter--;
+
+ if (itr->remaining == 0)
+ flush_dir(itr);
+
+ /* allocate a cluster for more entries */
if (!fat_itr_next(itr))
- if (!itr->dent && !itr->is_root && flush_dir_table(itr))
+ if (!itr->dent &&
+ (!itr->is_root || itr->fsdata->fatsize == 32) &&
+ new_dir_table(itr))
return -1;
}
- if (!itr->dent && !itr->is_root)
- /*
- * don't care return value here because we have already
- * finished completing an entry with name, only ending up
- * no more entry left
- */
- flush_dir_table(itr);
-
return 0;
}
@@ -388,29 +388,23 @@ static __u32 determine_fatent(fsdata *mydata, __u32 entry)
}
/**
- * set_cluster() - write data to cluster
+ * set_sectors() - write data to sectors
*
- * Write 'size' bytes from 'buffer' into the specified cluster.
+ * Write 'size' bytes from 'buffer' into the specified sector.
*
* @mydata: data to be written
- * @clustnum: cluster to be written to
+ * @startsect: sector to be written to
* @buffer: data to be written
* @size: bytes to be written (but not more than the size of a cluster)
* Return: 0 on success, -1 otherwise
*/
static int
-set_cluster(fsdata *mydata, u32 clustnum, u8 *buffer, u32 size)
+set_sectors(fsdata *mydata, u32 startsect, u8 *buffer, u32 size)
{
- u32 idx = 0;
- u32 startsect;
+ u32 nsects = 0;
int ret;
- if (clustnum > 0)
- startsect = clust_to_sect(mydata, clustnum);
- else
- startsect = mydata->rootdir_sect;
-
- debug("clustnum: %d, startsect: %d\n", clustnum, startsect);
+ debug("startsect: %d\n", startsect);
if ((unsigned long)buffer & (ARCH_DMA_MINALIGN - 1)) {
ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size);
@@ -429,17 +423,16 @@ set_cluster(fsdata *mydata, u32 clustnum, u8 *buffer, u32 size)
size -= mydata->sect_size;
}
} else if (size >= mydata->sect_size) {
- idx = size / mydata->sect_size;
- ret = disk_write(startsect, idx, buffer);
- if (ret != idx) {
+ nsects = size / mydata->sect_size;
+ ret = disk_write(startsect, nsects, buffer);
+ if (ret != nsects) {
debug("Error writing data (got %d)\n", ret);
return -1;
}
- startsect += idx;
- idx *= mydata->sect_size;
- buffer += idx;
- size -= idx;
+ startsect += nsects;
+ buffer += nsects * mydata->sect_size;
+ size -= nsects * mydata->sect_size;
}
if (size) {
@@ -457,6 +450,44 @@ set_cluster(fsdata *mydata, u32 clustnum, u8 *buffer, u32 size)
return 0;
}
+/**
+ * set_cluster() - write data to cluster
+ *
+ * Write 'size' bytes from 'buffer' into the specified cluster.
+ *
+ * @mydata: data to be written
+ * @clustnum: cluster to be written to
+ * @buffer: data to be written
+ * @size: bytes to be written (but not more than the size of a cluster)
+ * Return: 0 on success, -1 otherwise
+ */
+static int
+set_cluster(fsdata *mydata, u32 clustnum, u8 *buffer, u32 size)
+{
+ return set_sectors(mydata, clust_to_sect(mydata, clustnum),
+ buffer, size);
+}
+
+static int
+flush_dir(fat_itr *itr)
+{
+ fsdata *mydata = itr->fsdata;
+ u32 startsect, sect_offset, nsects;
+
+ if (!itr->is_root || mydata->fatsize == 32)
+ return set_cluster(mydata, itr->clust, itr->block,
+ mydata->clust_size * mydata->sect_size);
+
+ sect_offset = itr->clust * mydata->clust_size;
+ startsect = mydata->rootdir_sect + sect_offset;
+ /* do not write past the end of rootdir */
+ nsects = min_t(u32, mydata->clust_size,
+ mydata->rootdir_size - sect_offset);
+
+ return set_sectors(mydata, startsect, itr->block,
+ nsects * mydata->sect_size);
+}
+
static __u8 tmpbuf_cluster[MAX_CLUSTSIZE] __aligned(ARCH_DMA_MINALIGN);
/*
@@ -590,18 +621,14 @@ static int find_empty_cluster(fsdata *mydata)
}
/*
- * Write directory entries in itr's buffer to block device
+ * Allocate a cluster for additional directory entries
*/
-static int flush_dir_table(fat_itr *itr)
+static int new_dir_table(fat_itr *itr)
{
fsdata *mydata = itr->fsdata;
int dir_newclust = 0;
unsigned int bytesperclust = mydata->clust_size * mydata->sect_size;
- if (set_cluster(mydata, itr->clust, itr->block, bytesperclust) != 0) {
- printf("error: writing directory entry\n");
- return -1;
- }
dir_newclust = find_empty_cluster(mydata);
set_fatent_value(mydata, itr->clust, dir_newclust);
if (mydata->fatsize == 32)
@@ -956,7 +983,10 @@ static dir_entry *find_directory_entry(fat_itr *itr, char *filename)
return itr->dent;
}
- if (!itr->dent && !itr->is_root && flush_dir_table(itr))
+ /* allocate a cluster for more entries */
+ if (!itr->dent &&
+ (!itr->is_root || itr->fsdata->fatsize == 32) &&
+ new_dir_table(itr))
/* indicate that allocating dent failed */
itr->dent = NULL;
@@ -1009,40 +1039,32 @@ again:
return 0;
}
+/**
+ * normalize_longname() - check long file name and convert to lower case
+ *
+ * We assume here that the FAT file system is using an 8bit code page.
+ * Linux typically uses CP437, EDK2 assumes CP1250.
+ *
+ * @l_filename: preallocated buffer receiving the normalized name
+ * @filename: filename to normalize
+ * Return: 0 on success, -1 on failure
+ */
static int normalize_longname(char *l_filename, const char *filename)
{
- const char *p, legal[] = "!#$%&\'()-.@^`_{}~";
- unsigned char c;
- int name_len;
-
- /* Check that the filename is valid */
- for (p = filename; p < filename + strlen(filename); p++) {
- c = *p;
-
- if (('0' <= c) && (c <= '9'))
- continue;
- if (('A' <= c) && (c <= 'Z'))
- continue;
- if (('a' <= c) && (c <= 'z'))
- continue;
- if (strchr(legal, c))
- continue;
- /* extended code */
- if ((0x80 <= c) && (c <= 0xff))
- continue;
+ const char *p, illegal[] = "<>:\"/\\|?*";
+ if (strlen(filename) >= VFAT_MAXLEN_BYTES)
return -1;
- }
- /* Normalize it */
- name_len = strlen(filename);
- if (name_len >= VFAT_MAXLEN_BYTES)
- /* should return an error? */
- name_len = VFAT_MAXLEN_BYTES - 1;
+ for (p = filename; *p; ++p) {
+ if ((unsigned char)*p < 0x20)
+ return -1;
+ if (strchr(illegal, *p))
+ return -1;
+ }
- memcpy(l_filename, filename, name_len);
- l_filename[name_len] = 0; /* terminate the string */
- downcase(l_filename, INT_MAX);
+ strcpy(l_filename, filename);
+ downcase(l_filename, VFAT_MAXLEN_BYTES);
return 0;
}
@@ -1141,14 +1163,16 @@ int file_fat_write_at(const char *filename, loff_t pos, void *buffer,
memset(itr->dent, 0, sizeof(*itr->dent));
- /* Set short name to set alias checksum field in dir_slot */
+ /* Calculate checksum for short name */
set_name(itr->dent, filename);
+
+ /* Set long name entries */
if (fill_dir_slot(itr, filename)) {
ret = -EIO;
goto exit;
}
- /* Set attribute as archive for regular file */
+ /* Set short name entry */
fill_dentry(itr->fsdata, itr->dent, filename, 0, size, 0x20);
retdent = itr->dent;
@@ -1171,8 +1195,7 @@ int file_fat_write_at(const char *filename, loff_t pos, void *buffer,
}
/* Write directory table to device */
- ret = set_cluster(mydata, itr->clust, itr->block,
- mydata->clust_size * mydata->sect_size);
+ ret = flush_dir(itr);
if (ret) {
printf("Error: writing directory entry\n");
ret = -EIO;
@@ -1249,8 +1272,7 @@ static int delete_dentry(fat_itr *itr)
memset(dentptr, 0, sizeof(*dentptr));
dentptr->name[0] = 0xe5;
- if (set_cluster(mydata, itr->clust, itr->block,
- mydata->clust_size * mydata->sect_size) != 0) {
+ if (flush_dir(itr)) {
printf("error: writing directory entry\n");
return -EIO;
}
@@ -1452,8 +1474,7 @@ int fat_mkdir(const char *new_dirname)
}
/* Write directory table to device */
- ret = set_cluster(mydata, itr->clust, itr->block,
- mydata->clust_size * mydata->sect_size);
+ ret = flush_dir(itr);
if (ret)
printf("Error: writing directory entry\n");