Commit fde44225 authored by Robert Sprowson's avatar Robert Sprowson
Browse files

Revise filename shortening rules

The rules DOSFS used to create the short name version of a long filename were quite wide of the recommendations from Microsoft. This causes problems in particular with bootloaders that are a little more simplistic and use the short name form, only to find "BOOTCODE.BIN" has been replaced by "BOOTCO~1.BIN" by DOSFS.
Also, the long filename entried created for new long filenames left the reserved fields unset. Some of these seem to have special meanings to Windows which faults them ("extended attribute handle invalid") when running chkdsk.

DOSnaming.c: replaced the shorten_lfn function, sprinkled in a few token consts, set the reserved fields in long file names to 0
DOSclusters.c/OpsFunc.c/OpsFile.c: remove shorttemp[14] no longer needed, simplify sprintf/memcpy/memcpy into a single sprintf
DOSFS.h: familiar MIN/MAX maxros added

Tested in isolation in harness "test/shorten" and comparing floppy discs generated with "test/shortgen" from Windows and DOSFS.
Fixes ticket #313.

Version 1.06. Tagged as 'DOSFS-1_06'
parent 05e22692
/* (1.05)
/* (1.06)
*
* This file is automatically maintained by srccommit, do not edit manually.
* Last processed by srccommit version: 1.1.
*
*/
#define Module_MajorVersion_CMHG 1.05
#define Module_MajorVersion_CMHG 1.06
#define Module_MinorVersion_CMHG
#define Module_Date_CMHG 24 Jun 2014
#define Module_Date_CMHG 25 Jun 2014
#define Module_MajorVersion "1.05"
#define Module_Version 105
#define Module_MajorVersion "1.06"
#define Module_Version 106
#define Module_MinorVersion ""
#define Module_Date "24 Jun 2014"
#define Module_Date "25 Jun 2014"
#define Module_ApplicationDate "24-Jun-14"
#define Module_ApplicationDate "25-Jun-14"
#define Module_ComponentName "DOSFS"
#define Module_ComponentPath "castle/RiscOS/Sources/FileSys/ImageFS/DOSFS"
#define Module_FullVersion "1.05"
#define Module_HelpVersion "1.05 (24 Jun 2014)"
#define Module_LibraryVersionInfo "1:5"
#define Module_FullVersion "1.06"
#define Module_HelpVersion "1.06 (25 Jun 2014)"
#define Module_LibraryVersionInfo "1:6"
......@@ -667,7 +667,6 @@ int saveFILE(char *fname,char *leafname,word ld,word ex,char *bs,word len,DIR_in
int not_sfn;
char *longfileholder = NULL;
char shortname[13];
char shorttemp[14];
get_RISCOS_TIME(&saveTIME) ;
......@@ -725,28 +724,14 @@ int saveFILE(char *fname,char *leafname,word ld,word ex,char *bs,word len,DIR_in
}
/* Create 8.3 filename from leafname */
not_sfn = shorten_lfn(leafname, shortname, shorttemp, *cdir);
not_sfn = shorten_lfn(leafname, shortname, *cdir);
(*dentry) = not_sfn ? lfn[numreq - 1] : lfn[0];
dprintf(("","saveFILE: long filename = %s\n",leafname));
dprintf(("","saveFILE: short filename = \'%c%c%c%c%c%c%c%c\' ext=\'%c%c%c\'\n",
shortname[0],shortname[1],shortname[2],shortname[3],shortname[4],shortname[5],shortname[6],shortname[7],
shortname[8],shortname[9],shortname[10]));
if (not_sfn) MakeLFNEntries(&lfn[0],numreq,leafname,shortname);
/* write the information into the directory entry
*
* This requires the filename to be split into name and extension fields
* We fill the name with SPACEs first (the string terminator (NULL) is
* placed in the attributes field)
*
* NOTE: these should be done in this order due to the fact that the
* text building function terminates each string with a NUL
*/
sprintf((char *)&((*dentry)->FILE_status)," ") ;
memcpy((char *)&((*dentry)->FILE_status), &shortname[0], 8);
memcpy((char *)&((*dentry)->FILE_extension), &shortname[8], 3);
dprintf(("","saveFILE: long filename = '%s'\n",leafname));
dprintf(("","saveFILE: short filename = '%s'\n",shortname));
if (not_sfn) MakeLFNEntries(lfn,numreq,leafname,shortname);
sprintf((char *)&((*dentry)->FILE_status), "%-8.8s%-3s", shortname, &shortname[8]);
/* mark the file as suitable for archiving */
(*dentry)->FILE_attribute = FILE_archive ;
......
......@@ -19,6 +19,8 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
#include <ctype.h>
#include "kernel.h"
#include "DebugLib/DebugLib.h"
......@@ -335,7 +337,7 @@ char *buildFILEname(DOS_direntry * dentry,char *name)
* \param filename The filename to use
* \return The checksum
*/
byte lfnchecksum(char *filename)
byte lfnchecksum(const char *filename)
{
byte checksum = 0;
byte lsb;
......@@ -354,160 +356,117 @@ byte lfnchecksum(char *filename)
}
/*!
* \brief Formulate a DOS short filename from a long one
* \param longfname The filename to start with
* \param shortname Pointer to a buffer for the short result
* \param shortnamebuff A temporary buffer
* \param cdir The current working directory, used to check for duplicates of the shortened name
* \return Zero if the short name ends up being the same as the long one
* \brief Shorten part of a DOS filename
* \param longfname The part of filename to start with (ie. base name, or extension)
* \param buff Pointer to a buffer for the short result
* \param limit Maximum number of characters to output
* \return TRUE if copying was not an identity transform
*/
int shorten_lfn(char * longfname, char * shortname, char * shortnamebuff, DIR_info * cdir)
static int shorten_part(const char *longfname, char *buff, size_t limit)
{
// char * longfname : pointer to a null terminated file name
// char * shortnamebuff : pointer to a 13 byte buffer to store newly created short file name
// DIR_info * cdir : pointer the dir info structure for the target directory
int i, j, o, retval, dotseen=0;
char * extension,c;
/* check if already a valid short file name */
memset(shortnamebuff,' ',12);shortnamebuff[12]=0;
dprintf(("","shorten_lfn: start with -%s-\n",longfname));
for(i=0,j=0;(i<=12)&&(j<=12);i++,j++)
size_t i, j;
int identity = TRUE;
for (i = 0, j = 0; j < limit; i++)
{
c=longfname[i];
if(c=='.')
if ((longfname[i] == '.') || (longfname[i] == '\0'))
{
if(!dotseen)
{
dotseen=i+1;
while(j<8)
{
shortnamebuff[j++]=' '; // spacefill to 8
}
}
if(i>8) break; // too many chars
/* Reached the end of the base name */
break;
}
dprintf(("","shorten_lfn: check -%x-%c- toupper(c) -%c-\n",c,c,toupper(c)));
if(c && ((c=='.')||((c==toupper(c)) && (c!=' '))))
{ /* copy over */
shortnamebuff[j]=c;
dprintf(("","shorten_lfn: copied -%c-\n",c));
if (strchr("+,;=[]", longfname[i]) != NULL)
{
/* Substitute forbidden short name characters */
buff[j++] = '_';
identity = FALSE;
continue;
}
else
if (longfname[i] == ' ')
{
dprintf(("","shorten_lfn: %d %d %d\n",c,strlen(shortnamebuff),strlen(longfname)));
if(!c && /*strlen(shortnamebuff)==strlen(longfname) && */((!dotseen && (i<9)) || dotseen))
{ /* end of string .. and identical */
dprintf(("","shorten_lfn: its a valid DOS SFN\n"));
retval= 0;
goto slnfn1;
}
break;
/* Swallow spaces */
identity = FALSE;
continue;
}
buff[j++] = toupper(longfname[i]);
}
buff[j] = '\0';
return !identity;
}
int shorten_lfn(char *longfname, char *shortname, DIR_info *cdir)
{
size_t i;
int retval, nondot, tilde;
char *extension;
char basebuff[8+1];
char extbuff[1+3+1];
char compbuff[8+1+3+1];
/* Build the base name up to 8 long starting at the first non dot */
for (i = 0; longfname[i] == '.'; i++)
{
}
nondot = i;
tilde = shorten_part(&longfname[i], basebuff, 8);
memset(shortnamebuff,'_',8);
memset(&shortnamebuff[8],' ',4);
shortnamebuff[12]=NULL;
for(i=0,o=0;o<6;i++)
/* Locate the outermost extension and truncate to 3 letters */
extension = strrchr(longfname, '.');
if ((extension == NULL) || (&extension[1] == &longfname[nondot]))
{
if(longfname[i]==NULL)
{
o=6;
continue;
}
if(longfname[i]==' ') continue;
if(longfname[i]=='.')
{
if( &(longfname[i]) == strrchr(longfname,'.') )
{//If we are currently at the last 'dot' terminator in the filename...
o=6; //Ensuring we
continue; //Drop out of the loop
}else
{
i++;
continue;
}
}
if(strchr("+,;=[]", longfname[i]) != NULL)
{
shortnamebuff[o] = '_';
}else
{
shortnamebuff[o] = toupper(longfname[i]);
}
o++;
/* No extension, or found dot was at the start before the base name */
extension = ".";
}
shorten_part(&extension[1], &extbuff[1], 3);
extbuff[0] = '.';
dprintf(("", "shorten_lfn '%s' => base '%s' & ext '%s'\n", longfname, basebuff, extbuff));
/* If a tilde was needed, inject it */
if (tilde)
{
int index, length;
char fmt[] = "%0.?s~%u%s";
dprintf(("","shorten_lfn: strchr(longfname,'.') = %p, c='%c'\n",strchr(longfname,'.'),*strchr(longfname,'.')));
//We now have collected the first 6 characters of the short file name.
if(extension=strrchr(longfname,'.'), extension != NULL) //There is a dot extension
{//char * 'extension' points to the dot extension
for(i=0,o=8;o<12;i++)
{
if(extension[i]==NULL)
if (strlen(basebuff) <= 2)
{
o=12; //Break out of loop
continue;
/* Bit short, add a few digits */
sprintf(compbuff, "%04X", time(NULL));
strcat(basebuff, compbuff);
}
if(extension[i]==' ') continue;
if(strchr("+,;=[]", extension[i]) != NULL)
{
shortnamebuff[o] = '_';
}else
length = MIN(strlen(basebuff), 6);
fmt[3] = '0' + length;
for (i = 1; i <= 99; i++)
{
shortnamebuff[o] = toupper(extension[i]);
if ((i == 10) && (length == 6)) fmt[3]--; /* Budge an extra digit */
sprintf(compbuff, fmt, basebuff, i, extbuff);
index = 0;
if (findDIRentry(compbuff, cdir, cdir->dir_size, &index) == NULL)
{
/* No match, so we can use that tilde suffix */
break;
}
}
o++;
}
dprintf(("", " tilde needed, clear to use '%s'\n", compbuff));
}
else
{
strcpy(compbuff, basebuff);
strcat(compbuff, extbuff);
dprintf(("", " composite '%s'\n", compbuff));
}
shortnamebuff[6]='~';
shortnamebuff[7]='1';
shortnamebuff[12]=0;
if(shortnamebuff[9]==' ') shortnamebuff[8]='\0';
dprintf(("","Here file name is '%s'\n",shortnamebuff));
DOS_direntry * fentry;
int index;
for(i=1;i<99;i++)
/* See if the short candidate is the same as the long one */
retval = strcmp(longfname, compbuff);
if (retval == 0)
{
shortnamebuff[7]='0'+(i%10);
if((i / 10) > 0)
{
shortnamebuff[6]='0'+(i/10);
shortnamebuff[5]='~';
}
index=0;
fentry=findDIRentry(shortnamebuff,cdir,cdir->dir_size,&index);
if(fentry==NULL)
{
i=99;
continue;
}
dprintf(("", " long will do as short\n"));
}
/* Rejig the short name into its dotless form ready to put into a dir entry */
*strchr(compbuff, '.') = '\0';
sprintf(shortname, "%-8.8s%-3s", compbuff, &extbuff[1]);
dprintf(("", " dir entry '%s'\n", shortname));
dprintf(("","shorten_lfn: Started with lfn \"%s\"\n",longfname));
dprintf(("","shorten_lfn: Created short name \"%s\", char[8]= %x, char[9]= %x, char[10]= %x, char[11]= %x, char[12]= %x, char[13]= %x\n",shortnamebuff,shortnamebuff[8],shortnamebuff[9],shortnamebuff[10],shortnamebuff[11],shortnamebuff[12],shortnamebuff[13]));
retval=1;
slnfn1:
memset(shortname,' ',11);
memcpy(shortname,shortnamebuff,8);
memcpy(&shortname[8],&shortnamebuff[9],3);
if(shortnamebuff[8]=='.') i=8;
else i=0;
for(;i<11;i++)
{
if(shortnamebuff[i] == ' ')
{
shortnamebuff[i]=0;
i=11;
continue;
}
}
return retval;
}
......@@ -532,6 +491,7 @@ void MakeLFNEntries(DOS_direntry * lfn[],int numreq,char* leafname,char* shortna
lfnentry->FILE_Ordinal = ((lfnnum & 0x3F) + 1);
if (i == 0) lfnentry->FILE_Ordinal |= 0x40; /* Last entry */
lfnentry->FILE_attribute = FILE_win95;
lfnentry->reserved1 = lfnentry->reserved2 = lfnentry->reserved3 = 0;
/* Weave the name */
lfnentry->FILE_uchar0_hi = (nullreached)?0xFF:0;
......
......@@ -466,7 +466,6 @@ int DOSFS_create_dir(char *fname, word ld, word ex, word size, DOSdisc *ihand)
DOS_direntry *lfn[(MaxString + 12) / 13]; /* enough dir entries for the longest long filename */
char *longfileholder; /* for long name */
char shortname[14]; /* for short name equivalent */
char shorttemp[14]; /* for short name equivalent */
dprintf(("","\n\nDOSFS_create_dir: \"%s\"\n",fname));
......@@ -569,28 +568,14 @@ int DOSFS_create_dir(char *fname, word ld, word ex, word size, DOSdisc *ihand)
}
/* Create 8.3 filename from leafname */
not_sfn = shorten_lfn(leafname, shortname, shorttemp, cdir);
not_sfn = shorten_lfn(leafname, shortname, cdir);
dentry = not_sfn ? lfn[numreq - 1] : lfn[0];
dprintf(("","saveDIR: long filename = %s\n",leafname));
dprintf(("","saveDIR: short filename = %c%c%c%c%c%c%c%c, ext = %c%c%c\n",
shortname[0],shortname[1],shortname[2],shortname[3],shortname[4],shortname[5],shortname[6],shortname[7],
shortname[8],shortname[9],shortname[10]));
dprintf(("","saveDIR: long filename = '%s'\n",leafname));
dprintf(("","saveDIR: short filename = '%s'\n",shortname));
if (not_sfn) MakeLFNEntries(lfn, numreq, leafname, shortname);
/* write the information into the directory entry
*
* This requires the filename to be split into name and extension fields
* We fill the name with SPACEs first (the string terminator (NULL) is
* placed in the attributes field)
*
* NOTE: these should be done in this order due to the fact that the
* text building function terminates each string with a NULL
*/
sprintf((char *)&(dentry->FILE_status)," ") ;
memcpy((char *)&((dentry)->FILE_status), &shortname[0], 8);
memcpy((char *)&((dentry)->FILE_extension), &shortname[8], 3);
sprintf((char *)&dentry->FILE_status, "%-8.8s%-3s", shortname, &shortname[8]);
/* mark the object as a directory */
dentry->FILE_attribute = FILE_subdir; /* JRS removed (| FILE_archive) here 6/3/92 */
......
......@@ -59,7 +59,6 @@ int DOSFS_rename(char *oldname,char *newname,DOSdisc *ihand)
DOS_direntry *lfn[(MaxString + 12) / 13]; /* enough dir entries for the longest long filename */
char *longfileholder; /* for long name */
char shortname[14]; /* for short name equivalent */
char shorttemp[14]; /* for short name equivalent */
DIR_info *dummy; /* parent pointer (not used) */
dprintf(("","\n\nDOSFS_rename: \"%s\" --> \"%s\"\n",oldname,newname));
......@@ -160,21 +159,18 @@ int DOSFS_rename(char *oldname,char *newname,DOSdisc *ihand)
return -1;
}
not_sfn = shorten_lfn(leafname, shortname, shorttemp, ndir);
/* Create 8.3 filename from leafname */
not_sfn = shorten_lfn(leafname, shortname, ndir);
dentry = not_sfn ? lfn[numreq-1] : lfn[0];
dprintf(("","DOSFS_rename: long filename = %s\n",leafname));
dprintf(("","DOSFS_rename: short tempory filename = %s\n",shorttemp));
dprintf(("","DOSFS_rename: short filename = \'%c%c%c%c%c%c%c%c\' ext=\'%c%c%c\'\n",
shortname[0],shortname[1],shortname[2],shortname[3],shortname[4],shortname[5],shortname[6],shortname[7],
shortname[8],shortname[9],shortname[10]));
dprintf(("","DOSFS_rename: long filename = '%s'\n",leafname));
dprintf(("","DOSFS_rename: short temporary filename = '%s'\n",shorttemp));
dprintf(("","DOSFS_rename: short filename = '%s'",shortname));
dprintf(("","DOSFS_rename: into dentry = %p\n",dentry));
if (not_sfn) MakeLFNEntries(lfn,numreq,leafname,shortname);
sprintf((char *)&dentry->FILE_status, "%-8.8s%-3s", shortname, &shortname[8]);
sprintf((char *)&(dentry->FILE_status)," ") ;
memcpy((char *)&(dentry->FILE_status),&shortname[0],8);
memcpy((char *)&(dentry->FILE_extension),&shortname[8],3);
/* copy spare bytes (either JGS info or DRDOS5.0 info) */
for (loop = 0; (loop < spare1); loop++)
{
......
......@@ -25,6 +25,8 @@
typedef unsigned char byte ;
typedef unsigned int word ;
#define MAX(a,b) ((a)>(b)?(a):(b))
#define MIN(a,b) ((a)<(b)?(a):(b))
#define WORDALIGN(v) (((v) + 3) & ~3)
#define UNUSED(x) ((x) = (x)) /* deal with unused parameters */
#ifndef FALSE
......
......@@ -40,9 +40,9 @@ char *convertRISCOStoDOS(char *source,char *dest) ;
char *convertRISCOStoLFN(char *source,char *dest) ;
char *convertDOStoRISCOS(char *source,char *dest) ;
char mapchar(char cchr,char *fromlist,char *tolist) ;
int shorten_lfn(char * longfname, char * shortname, char * shortnamebuff, DIR_info * cdir);
int shorten_lfn(char * longfname, char * shortname, DIR_info * cdir);
void MakeLFNEntries(DOS_direntry * lfn[],int numreq,char* leafname,char* shortname);
byte lfnchecksum(char * filename);
byte lfnchecksum(const char *filename);
char *buildFILEname(DOS_direntry * dentry,char *name);
/*---------------------------------------------------------------------------*/
......
......@@ -46,7 +46,6 @@
#define ROmapping "?#+=;<>"
#define namsize (8) /* maximum size of a filename */
#define namsizestr "8"
#define extsize (3) /* maximum size of a filename extension */
#define spare1 (8) /* number of free bytes in a directory entry */
......@@ -133,7 +132,7 @@ typedef struct {
byte FILE_uchar4;
byte FILE_uchar4_hi;
byte FILE_attribute; // attribute byte
byte reserved;
byte reserved1;
byte FILE_checksum; // Checksum of 8.3 filename of owner entry
byte FILE_uchar5; // Unicode chars...
byte FILE_uchar5_hi;
......
/* Copyright 2014 Castle Technology Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <ctype.h>
#include <time.h>
typedef struct
{
int dir_size;
/* And lots of other stuff */
} DIR_info;
#define FALSE 0
#define TRUE (!FALSE)
#define MIN(a,b) ((a)>(b)?(b):(a))
static int my_dprintf(const char *dunno, const char *fmt, ...)
{
#define dprintf(a) my_dprintf a
int ret;
va_list args;
va_start(args, fmt);
ret = vprintf(fmt, args);
va_end(args);
return ret;
}
static void *findDIRentry(char *name, DIR_info *cdir, int size, int *restart)
{
if ((rand() & 0xF) == 0) return NULL; /* 1:16 odds of a success */
return "Try again";
}
static int shorten_part(const char *longfname, char *buff, size_t limit)
{
size_t i, j;
int identity = TRUE;
for (i = 0, j = 0; j < limit; i++)
{
if ((longfname[i] == '.') || (longfname[i] == '\0'))
{
/* Reached the end of the base name */
break;
}
if (strchr("+,;=[]", longfname[i]) != NULL)
{
/* Substitute forbidden short name characters */
buff[j++] = '_';
identity = FALSE;
continue;
}
if (longfname[i] == ' ')
{
/* Swallow spaces */
identity = FALSE;
continue;
}
buff[j++] = toupper(longfname[i]);
}
buff[j] = '\0';
return !identity;
}
int shorten_lfn(char *longfname, char *shortname, DIR_info *cdir)
{
size_t i;
int retval, nondot, tilde;
char *extension;
char basebuff[8+1];
char extbuff[1+3+1];
char compbuff[8+1+3+1];
/* Build the base name up to 8 long starting at the first non dot */
for (i = 0; longfname[i] == '.'; i++)
{
}
nondot = i;
tilde = shorten_part(&longfname[i], basebuff, 8);
/* Locate the outermost extension and truncate to 3 letters */
extension = strrchr(longfname, '.');
if ((extension == NULL) || (&extension[1] == &longfname[nondot]))
{
/* No extension, or found dot was at the start before the base name */
extension = "";
}
shorten_part(&extension[1], &extbuff[1], 3);
extbuff[0] = '.';
dprintf(("", "shorten_lfn '%s' => base '%s' & ext '%s'\n", longfname, basebuff, extbuff));
/* If a tilde was needed, inject it */
if (tilde)
{
int index, length;
char fmt[] = "%0.?s~%u%s";
if (strlen(basebuff) <= 2)
{
/* Bit short, add a few digits */
sprintf(compbuff, "%04X", time(NULL));
strcat(basebuff, compbuff);
}
length = MIN(strlen(basebuff), 6);
fmt[3] = '0' + length;
for (i = 1; i <= 99; i++)
{
if ((i == 10) && (length == 6)) fmt[3]--; /* Budge an extra digit */
sprintf(compbuff, fmt, basebuff, i, extbuff);
index = 0;
if (findDIRentry(compbuff, cdir, cdir->dir_size, &index) == NULL)
{
/* No match, so we can use that tilde suffix */
break;
}
}
dprintf(("", " tilde needed, clear to use '%s'\n", compbuff));
}
else
{
strcpy(compbuff, basebuff);
strcat(compbuff, extbuff);
dprintf(("", " composite '%s'\n", compbuff));
}
/* See if the short candidate is the same as the long one */
retval = strcmp(longfname, compbuff);
if (retval == 0)
{
dprintf(("", " long will do as short\n"));
}
/* Rejig the short name into its dotless form ready to put into a dir entry */
*strchr(compbuff, '.') = '\0';
sprintf(shortname, "%-8.8s%-3s", compbuff, &extbuff[1]);
dprintf(("", " dir entry '%s'\n", shortname));
return retval;
}