mirror of
https://github.com/u-boot/u-boot.git
synced 2025-04-20 20:04:46 +00:00
fs: fat: create correct short names
The current function set_name() used to create short names has the
following deficiencies resolved by this patch:
* Long names (e.g. FOO.TXT) are stored even if a short name is enough.
* Short names with spaces are created, e.g. "A ~1.TXT".
* Short names with illegal characters are created, e.g. "FOO++BAR".
* Debug output does not not consider that the short file name has no
concluding '\0'.
The solution for the following bug is split of into a separate patch:
* Short file names must be unique.
This patch only provides the loop over possible short file names.
Fixes: c30a15e590
("FAT: Add FAT write feature")
Signed-off-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
This commit is contained in:
parent
d236e825a2
commit
28cef9ca2e
2 changed files with 140 additions and 77 deletions
|
@ -8,25 +8,140 @@
|
||||||
#include <common.h>
|
#include <common.h>
|
||||||
#include <command.h>
|
#include <command.h>
|
||||||
#include <config.h>
|
#include <config.h>
|
||||||
|
#include <div64.h>
|
||||||
#include <fat.h>
|
#include <fat.h>
|
||||||
#include <log.h>
|
#include <log.h>
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
#include <asm/byteorder.h>
|
|
||||||
#include <part.h>
|
#include <part.h>
|
||||||
|
#include <rand.h>
|
||||||
|
#include <asm/byteorder.h>
|
||||||
#include <asm/cache.h>
|
#include <asm/cache.h>
|
||||||
#include <linux/ctype.h>
|
#include <linux/ctype.h>
|
||||||
#include <div64.h>
|
|
||||||
#include <linux/math64.h>
|
#include <linux/math64.h>
|
||||||
#include "fat.c"
|
#include "fat.c"
|
||||||
|
|
||||||
static void uppercase(char *str, int len)
|
/* Characters that may only be used in long file names */
|
||||||
|
static const char LONG_ONLY_CHARS[] = "+,;=[]";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* str2fat() - convert string to valid FAT name characters
|
||||||
|
*
|
||||||
|
* Stop when reaching end of @src or a period.
|
||||||
|
* Ignore spaces.
|
||||||
|
* Replace characters that may only be used in long names by underscores.
|
||||||
|
* Convert lower case characters to upper case.
|
||||||
|
*
|
||||||
|
* To avoid assumptions about the code page we do not use characters
|
||||||
|
* above 0x7f for the short name.
|
||||||
|
*
|
||||||
|
* @dest: destination buffer
|
||||||
|
* @src: source buffer
|
||||||
|
* @length: size of destination buffer
|
||||||
|
* Return: number of bytes in destination buffer
|
||||||
|
*/
|
||||||
|
static int str2fat(char *dest, char *src, int length)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < len; i++) {
|
for (i = 0; i < length; ++src) {
|
||||||
*str = toupper(*str);
|
char c = *src;
|
||||||
str++;
|
|
||||||
|
if (!c || c == '.')
|
||||||
|
break;
|
||||||
|
if (c == ' ')
|
||||||
|
continue;
|
||||||
|
if (strchr(LONG_ONLY_CHARS, c) || c > 0x7f)
|
||||||
|
c = '_';
|
||||||
|
else if (c >= 'a' && c <= 'z')
|
||||||
|
c &= 0xdf;
|
||||||
|
dest[i] = c;
|
||||||
|
++i;
|
||||||
}
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set_name() - set short name in directory entry
|
||||||
|
*
|
||||||
|
* The function determines if the @filename is a valid short name.
|
||||||
|
* In this case no long name is needed.
|
||||||
|
*
|
||||||
|
* If a long name is needed, a short name is constructed.
|
||||||
|
*
|
||||||
|
* @dirent: directory entry
|
||||||
|
* @filename: long file name
|
||||||
|
* Return: number of directory entries needed, negative on error
|
||||||
|
*/
|
||||||
|
static int set_name(dir_entry *dirent, const char *filename)
|
||||||
|
{
|
||||||
|
char *period;
|
||||||
|
char *pos;
|
||||||
|
int period_location;
|
||||||
|
char buf[13];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!filename)
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
/* Initialize buffers */
|
||||||
|
memset(dirent->name, ' ', sizeof(dirent->name));
|
||||||
|
memset(dirent->ext, ' ', sizeof(dirent->ext));
|
||||||
|
|
||||||
|
/* Convert filename to upper case short name */
|
||||||
|
period = strrchr(filename, '.');
|
||||||
|
pos = (char *)filename;
|
||||||
|
if (*pos == '.') {
|
||||||
|
pos = period + 1;
|
||||||
|
period = 0;
|
||||||
|
}
|
||||||
|
if (period)
|
||||||
|
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 = '_';
|
||||||
|
/* 0xe5 signals a deleted directory entry. Replace it by 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;
|
||||||
|
|
||||||
|
/* Construct an indexed short name */
|
||||||
|
for (i = 1; i < 0x200000; ++i) {
|
||||||
|
int suffix_len;
|
||||||
|
int suffix_start;
|
||||||
|
int j;
|
||||||
|
|
||||||
|
/* To speed up the search use random numbers */
|
||||||
|
if (i < 10) {
|
||||||
|
j = i;
|
||||||
|
} else {
|
||||||
|
j = 30 - fls(i);
|
||||||
|
j = 10 + (rand() >> j);
|
||||||
|
}
|
||||||
|
sprintf(buf, "~%d", j);
|
||||||
|
suffix_len = strlen(buf);
|
||||||
|
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 != ' ')
|
||||||
|
sprintf(buf, "%.*s.%.3s", suffix_start + suffix_len,
|
||||||
|
dirent->name, dirent->ext);
|
||||||
|
else
|
||||||
|
sprintf(buf, "%.*s", suffix_start + suffix_len,
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int total_sector;
|
static int total_sector;
|
||||||
|
@ -50,67 +165,6 @@ static int disk_write(__u32 block, __u32 nr_blocks, void *buf)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* set_name() - set short name in directory entry
|
|
||||||
*
|
|
||||||
* @dirent: directory entry
|
|
||||||
* @filename: long file name
|
|
||||||
*/
|
|
||||||
static void set_name(dir_entry *dirent, const char *filename)
|
|
||||||
{
|
|
||||||
char s_name[VFAT_MAXLEN_BYTES];
|
|
||||||
char *period;
|
|
||||||
int period_location, len, i, ext_num;
|
|
||||||
|
|
||||||
if (filename == NULL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
len = strlen(filename);
|
|
||||||
if (len == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
strncpy(s_name, filename, VFAT_MAXLEN_BYTES - 1);
|
|
||||||
s_name[VFAT_MAXLEN_BYTES - 1] = '\0';
|
|
||||||
uppercase(s_name, len);
|
|
||||||
|
|
||||||
period = strchr(s_name, '.');
|
|
||||||
if (period == NULL) {
|
|
||||||
period_location = len;
|
|
||||||
ext_num = 0;
|
|
||||||
} else {
|
|
||||||
period_location = period - s_name;
|
|
||||||
ext_num = len - period_location - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Pad spaces when the length of file name is shorter than eight */
|
|
||||||
if (period_location < 8) {
|
|
||||||
memcpy(dirent->name, s_name, period_location);
|
|
||||||
for (i = period_location; i < 8; i++)
|
|
||||||
dirent->name[i] = ' ';
|
|
||||||
} else if (period_location == 8) {
|
|
||||||
memcpy(dirent->name, s_name, period_location);
|
|
||||||
} else {
|
|
||||||
memcpy(dirent->name, s_name, 6);
|
|
||||||
/*
|
|
||||||
* TODO: Translating two long names with the same first six
|
|
||||||
* characters to the same short name is utterly wrong.
|
|
||||||
* Short names must be unique.
|
|
||||||
*/
|
|
||||||
dirent->name[6] = '~';
|
|
||||||
dirent->name[7] = '1';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ext_num < 3) {
|
|
||||||
memcpy(dirent->ext, s_name + period_location + 1, ext_num);
|
|
||||||
for (i = ext_num; i < 3; i++)
|
|
||||||
dirent->ext[i] = ' ';
|
|
||||||
} else
|
|
||||||
memcpy(dirent->ext, s_name + period_location + 1, 3);
|
|
||||||
|
|
||||||
debug("name : %s\n", dirent->name);
|
|
||||||
debug("ext : %s\n", dirent->ext);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Write fat buffer into block device
|
* Write fat buffer into block device
|
||||||
*/
|
*/
|
||||||
|
@ -1181,12 +1235,14 @@ int file_fat_write_at(const char *filename, loff_t pos, void *buffer,
|
||||||
|
|
||||||
memset(itr->dent, 0, sizeof(*itr->dent));
|
memset(itr->dent, 0, sizeof(*itr->dent));
|
||||||
|
|
||||||
/* Calculate checksum for short name */
|
/* Check if long name is needed */
|
||||||
set_name(itr->dent, filename);
|
ret = set_name(itr->dent, filename);
|
||||||
|
if (ret < 0)
|
||||||
|
goto exit;
|
||||||
|
if (ret > 1) {
|
||||||
/* Set long name entries */
|
/* Set long name entries */
|
||||||
if (fill_dir_slot(itr, filename)) {
|
ret = fill_dir_slot(itr, filename);
|
||||||
ret = -EIO;
|
if (ret)
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1441,9 +1497,16 @@ int fat_mkdir(const char *new_dirname)
|
||||||
|
|
||||||
memset(itr->dent, 0, sizeof(*itr->dent));
|
memset(itr->dent, 0, sizeof(*itr->dent));
|
||||||
|
|
||||||
/* Set short name to set alias checksum field in dir_slot */
|
/* Check if long name is needed */
|
||||||
set_name(itr->dent, dirname);
|
ret = set_name(itr->dent, dirname);
|
||||||
fill_dir_slot(itr, dirname);
|
if (ret < 0)
|
||||||
|
goto exit;
|
||||||
|
if (ret > 1) {
|
||||||
|
/* Set long name entries */
|
||||||
|
ret = fill_dir_slot(itr, dirname);
|
||||||
|
if (ret)
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
/* Set attribute as archive for regular file */
|
/* Set attribute as archive for regular file */
|
||||||
fill_dentry(itr->fsdata, itr->dent, dirname, 0, 0,
|
fill_dentry(itr->fsdata, itr->dent, dirname, 0, 0,
|
||||||
|
|
|
@ -168,7 +168,7 @@ config REGEX
|
||||||
choice
|
choice
|
||||||
prompt "Pseudo-random library support type"
|
prompt "Pseudo-random library support type"
|
||||||
depends on NET_RANDOM_ETHADDR || RANDOM_UUID || CMD_UUID || \
|
depends on NET_RANDOM_ETHADDR || RANDOM_UUID || CMD_UUID || \
|
||||||
RNG_SANDBOX || UT_LIB && AES
|
RNG_SANDBOX || UT_LIB && AES || FAT_WRITE
|
||||||
default LIB_RAND
|
default LIB_RAND
|
||||||
help
|
help
|
||||||
Select the library to provide pseudo-random number generator
|
Select the library to provide pseudo-random number generator
|
||||||
|
|
Loading…
Add table
Reference in a new issue