Commit b7d80d6c authored by Stewart Brodie's avatar Stewart Brodie
Browse files

First cut at long filenames in LanManFS. *Cat and *Ex work.

  Pretty much nothing else does.
Detail:
  Now uses long filenames if available, and switches to the advanced
    SMB protocol and Transact2 commands to do work with long filenames.
Admin:
  Work in progress.  End of week's work checkin point only.  Do not use.

Version 1.87, 1.1.1.1.2.2. Tagged as 'LanManFS-1_87-1_1_1_1_2_2'
parent 45acaef2
......@@ -45,7 +45,7 @@ CD = dir
CPFLAGS = ~cfr~v
WFLAGS = ~c~v
DFLAGS = -UDEBUG -UTML -DCOMPAT_INET4
DFLAGS = -UTML -DCOMPAT_INET4 -DLONGNAMES
AFLAGS = -depend !Depend ${THROWBACK} -Stamp -quit
CFLAGS = -depend !Depend ${THROWBACK} -c -Wpc -ff -zps1 -zM ${INCLUDES},. ${DFLAGS}
CMHGFLAGS = -depend !Depend ${THROWBACK} -p
......@@ -67,27 +67,31 @@ UNIXLIB = TCPIPLibs:o.unixlibzm
OBJS = LanMan.o Omni.o Logon.o CoreFn.o Printers.o \
Xlate.o Interface.o RMInfo.o buflib.o \
Xlate.o Interface.o RMInfo.o buflib.o Transact.o \
LLC.o NetBIOS.o SMB.o Errors.o Attr.o RPC.o NBIP.o Stats.o LanMan_MH.o
ROM_OBJS = or.LanMan or.Omni or.Logon or.CoreFn or.Printers \
or.Xlate or.buflib Interface.o RMInfo.o Errors.o \
or.Xlate or.buflib Interface.o RMInfo.o Errors.o or.Transact \
or.LLC or.NetBIOS or.SMB or.Attr or.RPC or.NBIP or.Stats LanMan_MH.o
DBG_OBJS = od.LanMan od.Omni od.Logon od.CoreFn od.Printers \
od.Xlate od.buflib Interface.o RMInfo.o Errors.o \
od.LLC od.NetBIOS od.SMB od.Attr od.RPC od.NBIP od.Stats LanMan_MH.o
#DBG_OBJS = od.LanMan od.Omni od.Logon od.CoreFn od.Printers \
# od.Xlate od.buflib Interface.o RMInfo.o Errors.o o.Transact \
# od.LLC od.NetBIOS od.SMB od.Attr od.RPC od.NBIP od.Stats LanMan_MH.o
DBG_OBJS = od.LanMan o.Omni od.Logon od.CoreFn od.Printers \
od.Xlate od.buflib Interface.o RMInfo.o Errors.o od.Transact \
od.LLC o.NetBIOS od.SMB o.Attr od.RPC o.NBIP od.Stats LanMan_MH.o
OBJSI = i.LanMan i.Omni i.Logon i.CoreFn i.Printers \
i.Xlate i.buflib \
i.Xlate i.buflib i.Transact \
i.LLC i.NetBIOS i.SMB i.Attr i.RPC i.NBIP i.Stats
OBJSINST = LanMan_MH.o inst.LanMan inst.Omni inst.Logon inst.CoreFn inst.Printers \
inst.Xlate inst.buflib Interface.o RMInfo.o Errors.o \
inst.Xlate inst.buflib Interface.o RMInfo.o Errors.o inst.Transact\
inst.LLC inst.NetBIOS inst.SMB inst.Attr inst.RPC inst.NBIP inst.Stats
LanMan_MH.h: LanMan_MH.o
@|
${CMHG} ${CMHGFLAGS} cmhg.$* -d $@
#
# Rule patterns
......@@ -95,7 +99,7 @@ LanMan_MH.h: LanMan_MH.o
.SUFFIXES: .o .od .or .s .c .i .h .cmhg .inst
.c.o:; ${CC} ${CFLAGS} -o $@ $<
.c.or:; ${CC} ${CFLAGS} -DROM -o $@ $<
.c.od:; ${CC} ${CFLAGS} -DTRACE -o $@ $<
.c.od:; ${CC} ${CFLAGS} -DDEBUG -DTRACE -o $@ $<
.c.i:; $(CC) $(CFLAGS) -c -C -E $< >> $@
.i.inst:; $(CC) $(CFLAGS) -C++ -o $@ $<
.cmhg.o:; ${CMHG} ${CMHGFLAGS} -o $@ $< -d $*.h
......@@ -162,7 +166,7 @@ ${RAM_MODULE}: ${OBJS} o.dirs
${DBG_MODULE}: ${DBG_OBJS} o.dirs
${MKDIR} rm
${LD} -o $@ -rmf $DBG_{OBJS} ${UNIXLIB} ${INETLIB} ${SOCKLIB} ${CLIB}
${LD} -o $@ -rmf ${DBG_OBJS} ${UNIXLIB} ${INETLIB} ${SOCKLIB} ${CLIB}
${MODSQZ} $@
#
......@@ -177,7 +181,6 @@ ${ROM_MODULE}: ${ROM_OBJS} ${UNIXLIB} ${INETLIB} ${SOCKLIB} o.dirs
#
rom_link:
${MKDIR} linked
${MKDIR} map
${LD} -o linked.${COMPONENT} -rmf -base ${ADDRESS} ${ROM_MODULE} ${ABSSYM}
${CP} linked.${COMPONENT} ${LINKDIR}.${COMPONENT} ${CPFLAGS}
@echo ${COMPONENT}: rom_link complete
......
......@@ -4,11 +4,11 @@
*
*/
#define Module_MajorVersion_CMHG 1.87
#define Module_MinorVersion_CMHG 1.1.1.1.2.1
#define Module_Date_CMHG 04 Dec 1998
#define Module_MinorVersion_CMHG 1.1.1.1.2.2
#define Module_Date_CMHG 11 Dec 1998
#define Module_MajorVersion "1.87"
#define Module_Version 187
#define Module_MinorVersion "1.1.1.1.2.1"
#define Module_Date "04 Dec 1998"
#define Module_MinorVersion "1.1.1.1.2.2"
#define Module_Date "11 Dec 1998"
......@@ -69,6 +69,7 @@ static char DOSnamebuf [NAMEBUF_LEN];
static char RD_CurrentPath[DOS_NAME_LEN];
static int RD_Reason;
static char *RD_BufPtr;
static int RD_BufLen;
static int RD_ReqCount; /* No. of entries required */
static int RD_ReqOffset; /* Offset of currently required dir entry */
......@@ -77,77 +78,119 @@ static int RD_CurOffset; /* Offset of next entry to be given to
/* ---------------- */
static char *WriteEntry ( int reason, char *ptr, BYTE *entry )
/* SNB: the blocks being written are those required by:
* NAMES_ONLY => OS_GBPB 9 => (FSEntry_Func 14)
* MOST_INFO => OS_GBPB 10 => (FSEntry_Func 15)
* ALL_INFO => OS_GBPB 11 => (FSEntry_Func 19)
*/
static char *WriteEntry ( int reason, char *ptr, BYTE *entry, int format )
{
static char namebuf[DOS_NAME_LEN];
int name_len;
int rec_len;
DOS_ATTRIBS da;
RISCOS_ATTRIBS ra;
int *IntPtr;
if (format == 1) {
Xlt_ExpandSearchEntryX2(entry, DOSnamebuf, namebuf, &da, &ra);
}
else {
Xlt_ExpandSearchEntry(entry, DOSnamebuf, namebuf, &da, &ra);
}
name_len = strlen(namebuf) + 1;
switch ( reason )
{
case NAMES_ONLY:
Xlt_ExpandSearchEntry ( entry, DOSnamebuf, ptr, NULL, NULL );
rec_len = strlen(ptr)+1;
rec_len = name_len;
if (rec_len <= RD_BufLen) {
memcpy(ptr, namebuf, name_len);
}
break;
case MOST_INFO:
Xlt_ExpandSearchEntry (entry, DOSnamebuf, ptr+20, &da, &ra );
IntPtr = (int *)ptr;
IntPtr[0] = ra.loadaddr;
IntPtr[1] = ra.execaddr;
IntPtr[2] = da.length;
IntPtr[3] = ra.flags;
IntPtr[4] = (da.attr & ATTR_DIR) ? 2 : 1;
rec_len = 20+strlen(ptr+20)+1;
rec_len = (rec_len+3) & ~3; /* Round up to whole word */
rec_len = (20+name_len+3) & ~3; /* Round up to whole word */
if (rec_len <= RD_BufLen) {
IntPtr = (int *)ptr;
IntPtr[0] = ra.loadaddr;
IntPtr[1] = ra.execaddr;
IntPtr[2] = da.length;
IntPtr[3] = ra.flags;
IntPtr[4] = (da.attr & ATTR_DIR) ? 2 : 1;
memcpy(ptr+20, namebuf, name_len);
}
break;
case ALL_INFO:
Xlt_ExpandSearchEntry (entry, DOSnamebuf, ptr+29, &da, &ra );
IntPtr = (int *)ptr;
IntPtr[0] = ra.loadaddr;
IntPtr[1] = ra.execaddr;
IntPtr[2] = da.length;
IntPtr[3] = ra.flags;
IntPtr[4] = (da.attr & ATTR_DIR) ? 2 : 1;
IntPtr[5] = 0;
IntPtr[6] = ra.execaddr;
ptr[28] = ra.loadaddr & 0xFF;
rec_len = 29+strlen(ptr+29)+1;
rec_len = (rec_len+3) & ~3; /* Round up to whole word */
rec_len = (29+name_len)+3 & ~3; /* Round up to whole word */
if (rec_len <= RD_BufLen) {
IntPtr = (int *)ptr;
IntPtr[0] = ra.loadaddr;
IntPtr[1] = ra.execaddr;
IntPtr[2] = da.length;
IntPtr[3] = ra.flags;
IntPtr[4] = (da.attr & ATTR_DIR) ? 2 : 1;
IntPtr[5] = 0;
IntPtr[6] = ra.execaddr;
ptr[28] = ra.loadaddr & 0xFF;
memcpy(ptr+29, namebuf, name_len);
}
break;
default: /* Shouldn't get here */
return ptr;
}
if (rec_len > RD_BufLen) {
debug2("WriteEntry detects a buffer overflow (%d > %d)\n",
rec_len, RD_BufLen);
RD_BufLen = -1;
return NULL;
}
RD_BufLen -= rec_len;
return (ptr+rec_len);
}
/* ------------------------------ */
static void Dir_CallbackFn ( BYTE *entry, void *pw )
static err_t Dir_CallbackFn ( BYTE *entry, int format, void *pw )
{
(void) pw;
#ifdef LONGNAMES
/* Insert check for . and .. here */
if (format == 1) {
if (entry[23] == '.') return OK;
}
else
#endif
if ( entry[9] == '.' ) /* Ignore '.' and '..' directories */
return;
return OK;
debug3 ("Entry: current %d req %d count %d\n",
RD_CurOffset, RD_ReqOffset, RD_ReqCount );
// debug3 ("Entry: current %d req %d count %d\n",
// RD_CurOffset, RD_ReqOffset, RD_ReqCount );
if ( RD_ReqOffset == RD_CurOffset )
{
if ( RD_ReqCount > 0 )
{
RD_BufPtr = WriteEntry ( RD_Reason, RD_BufPtr, entry );
RD_ReqCount--;
RD_ReqOffset++;
char *w_result = WriteEntry ( RD_Reason, RD_BufPtr, entry, format );
if (w_result != NULL) {
RD_BufPtr = w_result;
RD_ReqCount--;
RD_ReqOffset++;
}
else {
debug0("Oh dear - client ran out of buffer space!\n");
RD_CurOffset++;
return EOUTOFMEM;
}
}
}
RD_CurOffset++;
return OK;
}
/* ----------------- */
......@@ -159,7 +202,7 @@ static err_t Func_ReadDir ( int reason, char *path_name, char *buffer,
int tmp;
err_t res;
debug2 ( "\nReadDir reason %d path %s", reason, path_name );
debug2 ( "\n\n>> ReadDir reason %d path %s", reason, path_name );
debug2 ( " offset %d count %d\n", dir_offset, n_names );
/* Make sure the number of names we read will fit into buffer ---- */
......@@ -167,16 +210,35 @@ static err_t Func_ReadDir ( int reason, char *path_name, char *buffer,
tmp = (reason == NAMES_ONLY) ? 16 :
(reason == MOST_INFO) ? 36 : 44;
#ifdef LONGNAMES
/* Of course, the above calculation assumes that the filename is at most
* 16 characters long (8 + '.' + 3 + \0 + 3 bytes pad). This is
* useless for long filename shares and so the n_names parameter is
* accepted in place, and the buffer writer updated to be able to
* avoid and flag buffer overflow. In the worst case, this may mean
* multiple searches are required, however, as long as the directory
* reading function SMB_ReadDirEntriesX2 is intelligent, it can remember
* the resume keys and last successful filename data for the search and
* attempt to keep our caller in step with the remote server.
*
* The limit is still performed as it is still useful for avoiding
* unnecessary buffer overruns - especially when there are not very
* many files with long names or even no files with long names.
*/
/* There is no code inside this #ifdef - it just marks my comment. */
#else
if ( n_names*tmp > buflen ) n_names = buflen/tmp;
#endif
if ( n_names <= 0 ) return EBADPARAM;
RD_Reason = reason;
RD_BufPtr = buffer;
RD_BufLen = buflen;
RD_ReqCount = n_names;
RD_ReqOffset = dir_offset;
debug1("Actual count=%d", n_names );
debug1("Actual count=%d\n", n_names );
/* Convert path adding wildcard search spec */
......@@ -184,10 +246,17 @@ static err_t Func_ReadDir ( int reason, char *path_name, char *buffer,
if ( res != OK ) return res;
tmp = strlen(DOSnamebuf);
#ifdef LONGNAMES
if ( DOSnamebuf[tmp-1] == '\\' )
strcpy( DOSnamebuf+tmp, "*" );
else
strcpy( DOSnamebuf+tmp, "\\*" );
#else
if ( DOSnamebuf[tmp-1] == '\\' )
strcpy( DOSnamebuf+tmp, "????????.???" );
else
strcpy( DOSnamebuf+tmp, "\\????????.???" );
#endif
/* Start a new search? ----------------------------------- */
......@@ -196,7 +265,7 @@ static err_t Func_ReadDir ( int reason, char *path_name, char *buffer,
RD_ReqOffset < RD_CurOffset || /* Going back in search */
strcmp(DOSnamebuf, RD_CurrentPath) != 0 ) /* New directory */
{
debug0("Start search\n");
debug0("\n>> Start search\n");
strcpy ( RD_CurrentPath, DOSnamebuf);
RD_CurOffset = 0;
res = SMB_ReadDirEntries ( DOSnamebuf, RD_ReqCount+RD_ReqOffset,
......@@ -210,14 +279,20 @@ static err_t Func_ReadDir ( int reason, char *path_name, char *buffer,
while ( res == OK && /* More names available */
RD_ReqCount > 0 ) /* More names wanted */
{
debug0("Continue search\n");
debug0("\n>> Continue search\n");
res = SMB_ReadDirEntries ( NULL, RD_ReqCount+RD_ReqOffset-RD_CurOffset,
Dir_CallbackFn, NULL );
}
/* Process results */
debug2("End search, res=%d count=%d\n", res, RD_ReqCount);
if (res == EOUTOFMEM && RD_BufLen == -1) {
/* Special result when client buffer space exhausted */
debug0("Client buffer space was exhausted\n");
res = OK;
}
debug2("End search, res=%d RD_ReqCount=%d\n", res, RD_ReqCount);
if ( res != OK ) /* Ran out of files, or error */
{
......@@ -226,7 +301,15 @@ static err_t Func_ReadDir ( int reason, char *path_name, char *buffer,
}
else
{
*pOutNextOffset = RD_CurOffset;
if (RD_BufLen < 0) {
/* OK - was out of buffer space, force restart */
*pOutNextOffset = RD_ReqOffset;
//RD_CurOffset = 0;
//RD_CurrentPath[0] = 0;
}
else {
*pOutNextOffset = RD_ReqOffset;
}
}
*pOutNread = RD_ReqOffset-dir_offset;
......
......@@ -28,6 +28,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <ctype.h>
#include "kernel.h"
......@@ -471,6 +472,7 @@ err_t RPC_Transact ( char *servername, char *pipename, void *pvParmBlk )
{
char drv;
err_t res;
struct TransactParms t;
/* Connect to IPC share using default user ID/password */
......@@ -480,7 +482,12 @@ err_t RPC_Transact ( char *servername, char *pipename, void *pvParmBlk )
if ( res != OK )
return res;
res = SMB_Transact ( drv, pipename, (struct TransactParms *) pvParmBlk );
memcpy(&t, pvParmBlk, sizeof_TransactParms_external);
#ifdef LONGNAMES
t.setup_in_len = 0;
t.setup_out_maxlen = 0;
#endif
res = SMB_Transact ( drv, pipename, &t );
SMB_DeleteShare ( drv );
return res;
}
......
This diff is collapsed.
/* Copyright 1998 Acorn Computers 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.
*/
/*
*
* Transact.C -- SMB Transact and Transact2 related support routines
*
* 08-12-98 sbrodie Original
*
*/
/* Standard includes */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "kernel.h"
/* Our includes */
#include "stdtypes.h"
#include "buflib.h"
#include "NetBIOS.h"
#include "smb.h"
#include "Transact.h"
/*
* These routines are provided for the benefit of SMB.c:SMB_Transact2()
*
*/
void Transact_addword(struct TransactParms *TP, int value)
{
BYTE *p = TP->parms_in + TP->parms_in_len;
p[0] = (value & 0xFF);
p[1] = (value >> 8 );
TP->parms_in_len+=2;
}
/* ---------- */
void Transact_addlong(struct TransactParms *TP, long value)
{
BYTE *p = TP->parms_in + TP->parms_in_len;
p[0] = (BYTE) (value & 0xFF);
p[1] = (BYTE) (value >> 8 );
p[2] = (BYTE) (value >> 16 );
p[3] = (BYTE) (value >> 24 );
TP->parms_in_len+=4;
}
/* ---------- */
void Transact_addstring(struct TransactParms *TP, const char *str)
{
BYTE *p = TP->parms_in + TP->parms_in_len;
int l = strlen(str)+1;
//*p++ = 4; /* ASCII string */
memcpy ( p, str, l );
TP->parms_in_len += l;// + 1;
}
WORD Transact_getword(BYTE *p)
{
return ( p[0] + (p[1] << 8));
}
DWORD Transact_getlong(BYTE *p)
{
return ( p[0] + (p[1] << 8) + (p[2] << 16)+ (p[3] << 24));
}
BYTE *Transact_getpointer(struct TransactParms *TP, BYTE *p)
{
int ptrval;
ptrval = Transact_getword(p) + TP->data_out_len - TP->data_out_maxlen;
if ( ptrval <= 0 || ptrval >= TP->data_out_len )
return NULL;
return TP->data_out_buf + ptrval;
}
/* ---------- */
#ifdef LONGNAMES
void Transact_addsetupword(struct TransactParms *TP, int value)
{
if (TP->setup_in_len < MAX_SETUPWORDS)
TP->setup_in[TP->setup_in_len++] = value;
else
debug0("**** Transact_addsetupword = TOO MANY SETUP WORDS\n");
}
void Transact_addroutepath(struct TransactParms *TP, const char *path)
{
int len = strlen(path), wds;
if (len % 2) ++len;
wds = len / 2;
if ((wds + TP->setup_in_len) < MAX_SETUPWORDS) {
/* NOTE - we do not put the terminator in if the string was an even
* number of bytes long. If it was an odd number of bytes long we
* need to zero pad, hence we can just round len up to a multiple
* of 2 and allow the source string terminator to be the destination
* buffer pad byte.
*/
memcpy(&TP->setup_in[TP->setup_in_len], path, len);
TP->setup_in_len += wds;
}
else
debug0("**** Transact_addroutepath => too long!!\n");
}
#endif
void Transact_init(struct TransactParms *TP, int ret_param_len)
{
TP->parms_in = SMB_WorkBuf;
TP->parms_in_len = 0;
TP->data_in = NULL;
TP->data_in_len = 0;
TP->parms_out_buf = SMB_WorkBuf;
TP->parms_out_maxlen = min(ret_param_len, SMBWORKBUF_SIZE);
TP->data_out_maxlen = SMBWORKBUF_SIZE - TP->parms_out_maxlen;
TP->data_out_buf = SMB_WorkBuf + TP->parms_out_maxlen;
#ifdef LONGNAMES
TP->setup_out_maxlen = MAX_SETUPWORDS;
TP->setup_in_len = 0;
#endif
}
......@@ -453,7 +453,7 @@ static char xlt_DOS2RO[256] =
'`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
'c', 'y', 'z', '{', CH_DUD, '}', '~', CH_DUD,
'x', 'y', 'z', '{', CH_DUD, '}', '~', CH_DUD,
CH_DUD, CH_DUD, CH_DUD, CH_DUD, CH_DUD, CH_DUD, CH_DUD, CH_DUD,
CH_DUD, CH_DUD, CH_DUD, CH_DUD, CH_DUD, CH_DUD, CH_DUD, CH_DUD,
......@@ -523,6 +523,47 @@ void Xlt_NameDOStoRO ( char *dst, char *src )
*dst = 0;
}
#ifdef LONGNAMES
/* Originally, this came from NFS(pathmunge.c):move_nfsname_to_riscos_name */
static const char nfs_lookup_table[257]=
"________________________________"
"!\"_<__'()_+,-//0123456789_;<=>?"
"_ABCDEFGHIJKLMNOPQRSTUVWXYZ[_]__"
"`abcdefghijklmnopqrstuvwxyz{|}~_"
""
""
""
"";
void Xlt_NameDOStoROX2 ( char *dst, char *src )
{
int i;
for ( i=0; i<255; i++ ) /* No limit on length, except 255 chars */
{
int c = src[i] & 0xFF;
if (c == 0) break;
*dst++ = nfs_lookup_table[c];
}
*dst = 0;
}
//void Xlt_NameROtoDOSX2 ( char *dst, char *src )
//{
// int i, c;
//
// for ( i=0; i<255; i++ ) /* No limit on length, except 255 chars */
// {
// c = src[i] & 0xFF;
// if (c == 0) break;
// *dst++ = nfs_lookup_inverse_table[c];
// }
//
// *dst = 0;
//}
#endif
/* --------------------------- */
......@@ -600,6 +641,18 @@ err_t Xlt_ExpandSearchEntry ( BYTE *entry, char *path_base,
DOS_ATTRIBS *da_out,
RISCOS_ATTRIBS *ra_out )
{
/* Old SMBsearch format. For reference, entry points to the following
* structure (treat strictly as byte array with no padding except where
* stated:
* BYTE find_buf_attr;
* WORD find_buf_time;
* WORD find_buf_date;
* WORD find_buf_size_l;
* WORD find_buf_size_h;
* BYTE find_buf_pname[13]; ASCII - NUL terminated
* =====
* 22 bytes.
*/
DOS_ATTRIBS da;
if ( entry == NULL )
......@@ -631,6 +684,62 @@ err_t Xlt_ExpandSearchEntry ( BYTE *entry, char *path_base,
return OK;
}
#ifdef LONGNAMES
err_t Xlt_ExpandSearchEntryX2 ( BYTE *entry, char *path_base,
char *name_out,
DOS_ATTRIBS *da_out,
RISCOS_ATTRIBS *ra_out )
{
/* New TRANSACT2/FINDFIRST format. For reference, entry points to the
* following structure (treat strictly as byte array with no padding
* except where stated: (SMB_DATE and SMB_TIME are actually WORD)
*
* WORD CreationDate
* WORD CreationTime
* WORD LastAccessDate
* WORD LastAccessTime
* WORD LastWriteDate
* WORD LastWriteTime
* DWORD DataSize
* DWORD AllocationSize
* WORD Attributes
* BYTE FilenameLength
* STRING FileName
* =====