From patchwork Sun Nov 29 02:02:04 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Heinrich Schuchardt X-Patchwork-Id: 1407769 X-Patchwork-Delegate: trini@ti.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=2a01:238:438b:c500:173d:9f52:ddab:ee01; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=gmx.de Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; secure) header.d=gmx.net header.i=@gmx.net header.a=rsa-sha256 header.s=badeba3b8450 header.b=iSpVFKEL; dkim-atps=neutral Received: from phobos.denx.de (phobos.denx.de [IPv6:2a01:238:438b:c500:173d:9f52:ddab:ee01]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4CkBTy2bbCz9sSf for ; Sun, 29 Nov 2020 13:03:18 +1100 (AEDT) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id 34AC682783; Sun, 29 Nov 2020 03:02:58 +0100 (CET) Authentication-Results: phobos.denx.de; dmarc=none (p=none dis=none) header.from=gmx.de Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (1024-bit key; secure) header.d=gmx.net header.i=@gmx.net header.b="iSpVFKEL"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id DDF1982762; Sun, 29 Nov 2020 03:02:45 +0100 (CET) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-2.6 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,FREEMAIL_FROM,RCVD_IN_DNSWL_LOW,RCVD_IN_MSPIKE_H2, SPF_HELO_NONE autolearn=ham autolearn_force=no version=3.4.2 Received: from mout.gmx.net (mout.gmx.net [212.227.15.18]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id E189C82738 for ; Sun, 29 Nov 2020 03:02:41 +0100 (CET) Authentication-Results: phobos.denx.de; dmarc=none (p=none dis=none) header.from=gmx.de Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=xypron.glpk@gmx.de DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=gmx.net; s=badeba3b8450; t=1606615351; bh=B9bjVajdq2dL0FYLbOHU5ny+QlNA/mrhIEEWqQL+1Tw=; h=X-UI-Sender-Class:From:To:Cc:Subject:Date:In-Reply-To:References; b=iSpVFKELYjev/fZVd6IAvoM6xsG3At/ZBpoSR8PJPwHBsikB+dMfKSR9GrJ0DknHo ijZ6O2IYFPI5C3CLSZp5+MTpkFsIerbSB5kib1aL1RvkO2LYe/dxqhOppJZX+shWEf hgJ+kXjwRCnqFGU2dVYkrUH4nrvzB5Yq4SV8YxU0= X-UI-Sender-Class: 01bb95c1-4bf8-414a-932a-4f6e2808ef9c Received: from mcbin.fritz.box ([62.143.246.89]) by mail.gmx.com (mrgmx005 [212.227.17.184]) with ESMTPSA (Nemesis) id 1MpUZ4-1kQhGF1xSG-00pxlR; Sun, 29 Nov 2020 03:02:31 +0100 From: Heinrich Schuchardt To: Tom Rini Cc: Simon Glass , Christian Gmeiner , Jason Wessel , AKASHI Takahiro , Marek Szyprowski , Bin Meng , Reuben Dowle , Marcin Juszkiewicz , Thomas Hebb , Samer El-Haj-Mahmoud , u-boot@lists.denx.de, Heinrich Schuchardt Subject: [PATCH 06/18] fs: fat: create correct short names Date: Sun, 29 Nov 2020 03:02:04 +0100 Message-Id: <20201129020216.4865-7-xypron.glpk@gmx.de> X-Mailer: git-send-email 2.29.2 In-Reply-To: <20201129020216.4865-1-xypron.glpk@gmx.de> References: <20201129020216.4865-1-xypron.glpk@gmx.de> MIME-Version: 1.0 X-Provags-ID: V03:K1:yyNGjlBcRgs2g6eC+HLuzNIIN/juN1G7BwY9kgoGO1noFNHdBCp tqCGw7duHR0pxg0889BoaRkPFCTY6RBEjxuFFEtHB13l47YEeQ7X6by6QcyzbC4uI4k92EA VkRNYjZuSoKmIJWeo9L1nQSmorMqI9SCSjfvhkx3skhz6IO2vOxcPv2F+nhQmtWkcP83TV1 63iIGEVrJL55nBB4wrezQ== X-UI-Out-Filterresults: notjunk:1;V03:K0:3uO66j5Xgpc=:/nTXUHvinyH37X4oQzy5Dn ooLOPkyoRofhldhox2k3P/Pcp+q+xKIxG4z5s2B1QRIAkz2qXcW5xyImkzjwMIsH8t0njNBkL EHyjiyaqaAoH2pvYxlV0BLGIqlmrT1drvHwxtLFDaDzb59QN323Vty2T2nvLPCLaRHEjFJeYw RnwfXWGkYxallSxPsciLszAgw/Ht5M3cTqlxnW57ryccrjfbrXUTG/93epQ+cEN2lCb9AdTGB 7EnV6FGBTWcnJ2/NZljtThAn8FuYKNg9mFg+DtpBvK6a77ZdOBQ5Iqy7C7/C3Iyc04lwTGvBb iuWLBP04p3ANt1TwWTTjQ1RbIUUQxlc9ecVDeTKyX+VD4uGgZkIlc3JkRtSjrcbE2lwEenYex Saou8bY9pGm0ABVNHdRNSexfO4kXhk6co1vkhUaOWihOU0r5Gt503hWroaK3d79HZ1eGzDQ26 TaBELFxKMjPOa53HMW2xkY2sksA0mHLiwkreWHTy3sjGc6B3+Ukwvzb95Az4YNpIRh1SMdZcw 4yw0z8PPwLc7C5IZCGYM1D09E8vXyxll277rAG4XUCpBG1t26yjIiQbh0HBbr7Ibu1F2nRZAS NlF8ztTmOI7V/Syuzx2cbPRtWsCCCFJOtwVupoKbm1K+axsBLCKc2yHAMeCleo0w93L5R0wpS xaSa7EQRjjg9BFw+iFpumGZjmG3uRWgEQ2KGc+kRm9J8E+tmF2y3Dd9KNlYJc++SgztMsATw5 sXR86/iRlJO8cKYoNlOgpick6m9ZuB6XsFV9xGMCsywoiJP7oPe2yep8yV4hILXBQR74EMyl7 BPboN08a2aFH2AaEHE95koRR+3KX27QNzxxYwK536X5p87SJbfbeEYwDhwSx0osU/xDsOTImQ vnlJEPQiVqrm6IsfcAdw== X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.34 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.102.3 at phobos.denx.de X-Virus-Status: Clean 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: c30a15e590c ("FAT: Add FAT write feature") Signed-off-by: Heinrich Schuchardt --- fs/fat/fat_write.c | 215 +++++++++++++++++++++++++++++---------------- lib/Kconfig | 2 +- 2 files changed, 140 insertions(+), 77 deletions(-) -- 2.29.2 diff --git a/fs/fat/fat_write.c b/fs/fat/fat_write.c index 4da342f9c9..058b566629 100644 --- a/fs/fat/fat_write.c +++ b/fs/fat/fat_write.c @@ -8,25 +8,140 @@ #include #include #include +#include #include #include #include -#include #include +#include +#include #include #include -#include #include #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; + + for (i = 0; i < length; ++src) { + char c = *src; + + 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; - for (i = 0; i < len; i++) { - *str = toupper(*str); - str++; + 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; @@ -50,67 +165,6 @@ static int disk_write(__u32 block, __u32 nr_blocks, void *buf) 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 */ @@ -1181,13 +1235,15 @@ int file_fat_write_at(const char *filename, loff_t pos, void *buffer, memset(itr->dent, 0, sizeof(*itr->dent)); - /* Calculate checksum for short name */ - set_name(itr->dent, filename); - - /* Set long name entries */ - if (fill_dir_slot(itr, filename)) { - ret = -EIO; + /* Check if long name is needed */ + ret = set_name(itr->dent, filename); + if (ret < 0) goto exit; + if (ret > 1) { + /* Set long name entries */ + ret = fill_dir_slot(itr, filename); + if (ret) + goto exit; } /* Set short name entry */ @@ -1441,9 +1497,16 @@ int fat_mkdir(const char *new_dirname) memset(itr->dent, 0, sizeof(*itr->dent)); - /* Set short name to set alias checksum field in dir_slot */ - set_name(itr->dent, dirname); - fill_dir_slot(itr, dirname); + /* Check if long name is needed */ + ret = set_name(itr->dent, 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 */ fill_dentry(itr->fsdata, itr->dent, dirname, 0, 0, diff --git a/lib/Kconfig b/lib/Kconfig index 7673d2e4e0..06eb8d07dc 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -168,7 +168,7 @@ config REGEX choice prompt "Pseudo-random library support type" depends on NET_RANDOM_ETHADDR || RANDOM_UUID || CMD_UUID || \ - RNG_SANDBOX || UT_LIB && AES + RNG_SANDBOX || UT_LIB && AES || FAT_WRITE default LIB_RAND help Select the library to provide pseudo-random number generator