From 57b745e2387a7dafd2d29004351cdd3ffffcc5b9 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sun, 22 Nov 2020 19:19:39 +0100 Subject: fs: fat: call set_name() only once In set_name() we select the short name. Once this is correctly implemented this will be a performance intensive operation because we need to check that the name does not exist yet. So set_name should only be called once. Signed-off-by: Heinrich Schuchardt --- fs/fat/fat_write.c | 87 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 54 insertions(+), 33 deletions(-) diff --git a/fs/fat/fat_write.c b/fs/fat/fat_write.c index 7e8886791c..716b6a6627 100644 --- a/fs/fat/fat_write.c +++ b/fs/fat/fat_write.c @@ -23,6 +23,9 @@ /* Characters that may only be used in long file names */ static const char LONG_ONLY_CHARS[] = "+,;=[]"; +/* Combined size of the name and ext fields in the directory entry */ +#define SHORT_NAME_SIZE 11 + /** * str2fat() - convert string to valid FAT name characters * @@ -68,24 +71,28 @@ static int str2fat(char *dest, char *src, int length) * * If a long name is needed, a short name is constructed. * - * @dirent: directory entry * @filename: long file name + * @shortname: buffer of 11 bytes to receive chosen short name and extension * Return: number of directory entries needed, negative on error */ -static int set_name(dir_entry *dirent, const char *filename) +static int set_name(const char *filename, char *shortname) { char *period; char *pos; int period_location; char buf[13]; int i; + int ret; + struct { + char name[8]; + char ext[3]; + } dirent; if (!filename) return -EIO; - /* Initialize buffers */ - memset(dirent->name, ' ', sizeof(dirent->name)); - memset(dirent->ext, ' ', sizeof(dirent->ext)); + /* Initialize buffer */ + memset(&dirent, ' ', sizeof(dirent)); /* Convert filename to upper case short name */ period = strrchr(filename, '.'); @@ -95,20 +102,22 @@ static int set_name(dir_entry *dirent, const char *filename) period = 0; } if (period) - str2fat(dirent->ext, period + 1, sizeof(dirent->ext)); - period_location = str2fat(dirent->name, pos, sizeof(dirent->name)); + str2fat(dirent.ext, period + 1, sizeof(dirent.ext)); + period_location = str2fat(dirent.name, pos, sizeof(dirent.name)); if (period_location < 0) return period_location; - if (*dirent->name == ' ') - *dirent->name = '_'; + if (*dirent.name == ' ') + *dirent.name = '_'; /* 0xe5 signals a deleted directory entry. Replace it by 0x05. */ - if (*dirent->name == 0xe5) - *dirent->name = 0x05; + if (*dirent.name == 0xe5) + *dirent.name = 0x05; /* If filename and short name are the same, quit. */ - sprintf(buf, "%.*s.%.3s", period_location, dirent->name, dirent->ext); - if (!strcmp(buf, filename)) - return 1; + sprintf(buf, "%.*s.%.3s", period_location, dirent.name, dirent.ext); + if (!strcmp(buf, filename)) { + ret = 1; + goto out; + } /* Construct an indexed short name */ for (i = 1; i < 0x200000; ++i) { @@ -128,20 +137,24 @@ static int set_name(dir_entry *dirent, const char *filename) suffix_start = 8 - suffix_len; if (suffix_start > period_location) suffix_start = period_location; - memcpy(dirent->name + suffix_start, buf, suffix_len); - if (*dirent->ext != ' ') + memcpy(dirent.name + suffix_start, buf, suffix_len); + if (*dirent.ext != ' ') sprintf(buf, "%.*s.%.3s", suffix_start + suffix_len, - dirent->name, dirent->ext); + dirent.name, dirent.ext); else sprintf(buf, "%.*s", suffix_start + suffix_len, - dirent->name); + dirent.name); debug("short name: %s\n", buf); /* TODO: Check that the short name does not exist yet. */ /* Each long name directory entry takes 13 characters. */ - return (strlen(filename) + 25) / 13; + ret = (strlen(filename) + 25) / 13; + goto out; } return -EIO; +out: + memcpy(shortname, dirent.name, SHORT_NAME_SIZE); + return ret; } static int total_sector; @@ -1019,18 +1032,27 @@ getit: return 0; } -/* - * Fill dir_entry +/** + * fill_dentry() - fill directory entry with shortname + * + * @mydata: private filesystem parameters + * @dentptr: directory entry + * @shortname: chosen short name + * @start_cluster: first cluster of file + * @size: file size + * @attr: file attributes */ static void fill_dentry(fsdata *mydata, dir_entry *dentptr, - const char *filename, __u32 start_cluster, __u32 size, __u8 attr) + const char *shortname, __u32 start_cluster, __u32 size, __u8 attr) { + memset(dentptr, 0, sizeof(*dentptr)); + set_start_cluster(mydata, dentptr, start_cluster); dentptr->size = cpu_to_le32(size); dentptr->attr = attr; - set_name(dentptr, filename); + memcpy(dentptr->name, shortname, SHORT_NAME_SIZE); } /* @@ -1215,6 +1237,7 @@ int file_fat_write_at(const char *filename, loff_t pos, void *buffer, retdent->size = cpu_to_le32(pos + size); } else { /* Create a new file */ + char shortname[SHORT_NAME_SIZE]; if (itr->is_root) { /* root dir cannot have "." or ".." */ @@ -1237,21 +1260,19 @@ int file_fat_write_at(const char *filename, loff_t pos, void *buffer, goto exit; } - memset(itr->dent, 0, sizeof(*itr->dent)); - /* Check if long name is needed */ - ret = set_name(itr->dent, filename); + ret = set_name(filename, shortname); if (ret < 0) goto exit; if (ret > 1) { /* Set long name entries */ - ret = fill_dir_slot(itr, filename, itr->dent->name); + ret = fill_dir_slot(itr, filename, shortname); if (ret) goto exit; } /* Set short name entry */ - fill_dentry(itr->fsdata, itr->dent, filename, 0, size, + fill_dentry(itr->fsdata, itr->dent, shortname, 0, size, ATTR_ARCH); retdent = itr->dent; @@ -1484,6 +1505,8 @@ int fat_mkdir(const char *new_dirname) ret = -EEXIST; goto exit; } else { + char shortname[SHORT_NAME_SIZE]; + if (itr->is_root) { /* root dir cannot have "." or ".." */ if (!strcmp(l_dirname, ".") || @@ -1499,21 +1522,19 @@ int fat_mkdir(const char *new_dirname) goto exit; } - memset(itr->dent, 0, sizeof(*itr->dent)); - /* Check if long name is needed */ - ret = set_name(itr->dent, dirname); + ret = set_name(dirname, shortname); if (ret < 0) goto exit; if (ret > 1) { /* Set long name entries */ - ret = fill_dir_slot(itr, dirname, itr->dent->name); + ret = fill_dir_slot(itr, dirname, shortname); if (ret) goto exit; } /* Set attribute as archive for regular file */ - fill_dentry(itr->fsdata, itr->dent, dirname, 0, 0, + fill_dentry(itr->fsdata, itr->dent, shortname, 0, 0, ATTR_DIR | ATTR_ARCH); retdent = itr->dent; -- cgit v1.2.3