mirror of
https://github.com/obarthel/amiga-smbfs.git
synced 2025-12-08 14:58:35 +00:00
smbfs will not work properly with the standard 4K stack size. Testing revealed that 20K are safer, which is why the program startup code now provides as much if needed.
6934 lines
144 KiB
C
6934 lines
144 KiB
C
/*
|
|
* :ts=4
|
|
*
|
|
* SMB file system wrapper for AmigaOS, using the AmiTCP V3 API
|
|
*
|
|
* Copyright (C) 2000-2016 by Olaf `Olsen' Barthel <obarthel -at- gmx -dot- net>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
|
|
/*
|
|
* cpr smbfs.debug domain=workgroup user=olsen password=bazong volume=olsen //felix/olsen
|
|
* cpr smbfs.debug dumpsmb user=guest volume=amiga //windows7-amiga/Users/Public
|
|
* copy "amiga:Public/Documents/Amiga Files/Shared/dir/Windows-Export/LP2NRFP.h" ram:
|
|
* break smba_start
|
|
* break smba_connect
|
|
* break smb_receive_raw
|
|
*
|
|
* smbfs.debug user=guest volume=sicherung //192.168.1.76/sicherung-smb
|
|
*/
|
|
|
|
#include "smbfs.h"
|
|
|
|
/****************************************************************************/
|
|
|
|
#include "smb_abstraction.h"
|
|
#include "utf-8-iso-8859-1-conversion.h"
|
|
#include "dump_smb.h"
|
|
|
|
/****************************************************************************/
|
|
|
|
#include <smb/smb.h>
|
|
|
|
/****************************************************************************/
|
|
|
|
#if defined(__amigaos4__) && !defined(Flush)
|
|
#define Flush(fh) FFlush(fh)
|
|
#endif /* __amigaos4__ && !Flush */
|
|
|
|
/****************************************************************************/
|
|
|
|
/* A quick workaround for the timeval/timerequest->TimeVal/TimeRequest
|
|
change in the recent OS4 header files. */
|
|
#if defined(__NEW_TIMEVAL_DEFINITION_USED__)
|
|
|
|
#define timeval TimeVal
|
|
#define tv_secs Seconds
|
|
#define tv_micro Microseconds
|
|
|
|
#define timerequest TimeRequest
|
|
#define tr_node Request
|
|
#define tr_time Time
|
|
|
|
#endif /* __NEW_TIMEVAL_DEFINITION_USED__ */
|
|
|
|
/****************************************************************************/
|
|
|
|
/* This is for backwards compatibility only. */
|
|
#if defined(__amigaos4__)
|
|
#define fib_EntryType fib_Obsolete
|
|
#endif /* __amigaos4__ */
|
|
|
|
/****************************************************************************/
|
|
|
|
#include "smbfs_rev.h"
|
|
STRPTR Version = VERSTAG;
|
|
|
|
/****************************************************************************/
|
|
|
|
#define UNIX_TIME_OFFSET 252460800
|
|
#define MAX_FILENAME_LEN 256
|
|
|
|
/****************************************************************************/
|
|
|
|
#define SMB_ROOT_DIR_NAME "\\"
|
|
#define SMB_PATH_SEPARATOR '\\'
|
|
|
|
/****************************************************************************/
|
|
|
|
typedef STRPTR KEY;
|
|
typedef LONG * NUMBER;
|
|
typedef LONG SWITCH;
|
|
|
|
/****************************************************************************/
|
|
|
|
struct FileNode
|
|
{
|
|
struct MinNode fn_MinNode;
|
|
struct FileHandle * fn_Handle;
|
|
LONG fn_Offset;
|
|
LONG fn_Mode;
|
|
smba_file_t * fn_File;
|
|
STRPTR fn_FullName;
|
|
};
|
|
|
|
struct LockNode
|
|
{
|
|
struct MinNode ln_MinNode;
|
|
struct FileLock ln_FileLock;
|
|
smba_file_t * ln_File;
|
|
BOOL ln_RestartExamine;
|
|
UWORD ln_Pad;
|
|
STRPTR ln_FullName;
|
|
};
|
|
|
|
/****************************************************************************/
|
|
|
|
/* The minimum operating system version we require to work. */
|
|
#define MINIMUM_OS_VERSION 37 /* Kickstart 2.04 or better */
|
|
/*#define MINIMUM_OS_VERSION 39*/ /* Kickstart 3.0 or better */
|
|
|
|
/****************************************************************************/
|
|
|
|
/* Careful: the memory pool routines in amiga.lib are available only to
|
|
* SAS/C and similar compilers (not necessarily to GCC).
|
|
*/
|
|
#if defined(__GNUC__) && (MINIMUM_OS_VERSION < 39)
|
|
|
|
#undef MINIMUM_OS_VERSION
|
|
#define MINIMUM_OS_VERSION 39
|
|
|
|
#endif /* __GNUC__ */
|
|
|
|
/****************************************************************************/
|
|
|
|
#if (MINIMUM_OS_VERSION < 39)
|
|
|
|
/* These are in amiga.lib */
|
|
APTR ASM AsmCreatePool(REG(d0,ULONG memFlags),REG(d1,ULONG puddleSize),REG(d2,ULONG threshSize),REG(a6,struct Library * SysBase));
|
|
VOID ASM AsmDeletePool(REG(a0,APTR poolHeader),REG(a6,struct Library * SysBase));
|
|
APTR ASM AsmAllocPooled(REG(a0,APTR poolHeader),REG(d0,ULONG memSize),REG(a6,struct Library * SysBase));
|
|
VOID ASM AsmFreePooled(REG(a0,APTR poolHeader),REG(a1,APTR memory),REG(d0,ULONG memSize),REG(a6,struct Library * SysBase));
|
|
|
|
#define CreatePool(memFlags,puddleSize,threshSize) AsmCreatePool((memFlags),(puddleSize),(threshSize),SysBase)
|
|
#define DeletePool(poolHeader) AsmDeletePool((poolHeader),SysBase)
|
|
#define AllocPooled(poolHeader,memSize) AsmAllocPooled((poolHeader),(memSize),SysBase)
|
|
#define FreePooled(poolHeader,memory,memSize) AsmFreePooled((poolHeader),(memory),(memSize),SysBase)
|
|
|
|
#endif /* MINIMUM_OS_VERSION */
|
|
|
|
/****************************************************************************/
|
|
|
|
/* Forward declarations for local routines. */
|
|
LONG _start(VOID);
|
|
LONG VARARGS68K LocalPrintf(STRPTR format, ...);
|
|
STRPTR amitcp_strerror(int error);
|
|
STRPTR host_strerror(int error);
|
|
LONG CompareNames(STRPTR a, STRPTR b);
|
|
VOID StringToUpper(STRPTR s);
|
|
VOID VARARGS68K ReportError(STRPTR fmt, ...);
|
|
VOID FreeMemory(APTR address);
|
|
APTR AllocateMemory(ULONG size);
|
|
LONG GetTimeZoneDelta(VOID);
|
|
ULONG GetCurrentTime(VOID);
|
|
VOID GMTime(time_t seconds, struct tm *tm);
|
|
time_t MakeTime(const struct tm *const tm);
|
|
VOID VARARGS68K SPrintf(STRPTR buffer, STRPTR formatString, ...);
|
|
int BroadcastNameQuery(char *name, char *scope, UBYTE *address);
|
|
|
|
/****************************************************************************/
|
|
|
|
STATIC LONG main(VOID);
|
|
STATIC ULONG get_stack_size(VOID);
|
|
STATIC VOID stack_usage_init(struct StackSwapStruct * stk);
|
|
STATIC ULONG stack_usage_exit(struct StackSwapStruct * stk);
|
|
|
|
/****************************************************************************/
|
|
|
|
INLINE STATIC BOOL ReallyRemoveDosEntry(struct DosList *entry);
|
|
INLINE STATIC LONG BuildFullName(STRPTR parent_name, STRPTR name, STRPTR *result_ptr, LONG *result_size_ptr);
|
|
INLINE STATIC VOID TranslateCName(UBYTE *name, UBYTE *map);
|
|
INLINE STATIC VOID ConvertCString(APTR bstring, LONG max_len, STRPTR cstring, LONG len);
|
|
STATIC VOID DisplayErrorList(VOID);
|
|
STATIC VOID AddError(STRPTR fmt, APTR args);
|
|
STATIC LONG CVSPrintf(STRPTR format_string, APTR args);
|
|
STATIC VOID VSPrintf(STRPTR buffer, STRPTR formatString, APTR args);
|
|
STATIC VOID SendDiskChange(ULONG class);
|
|
STATIC struct FileNode *FindFileNode(STRPTR name, struct FileNode *skip);
|
|
STATIC struct LockNode *FindLockNode(STRPTR name, struct LockNode *skip);
|
|
STATIC LONG CheckAccessModeCollision(STRPTR name, LONG mode);
|
|
STATIC LONG NameAlreadyInUse(STRPTR name);
|
|
STATIC BOOL IsReservedName(STRPTR name);
|
|
STATIC LONG MapErrnoToIoErr(int error);
|
|
STATIC VOID TranslateBName(UBYTE *name, UBYTE *map);
|
|
STATIC VOID Cleanup(VOID);
|
|
STATIC BOOL Setup(STRPTR program_name, STRPTR service, STRPTR workgroup, STRPTR username, STRPTR opt_password, BOOL opt_changecase, STRPTR opt_clientname, STRPTR opt_servername, int opt_cachesize, int opt_max_transmit, LONG *opt_time_zone_offset, LONG *opt_dst_offset, BOOL opt_raw_smb, STRPTR device_name, STRPTR volume_name, STRPTR translation_file);
|
|
STATIC VOID ConvertBString(LONG max_len, STRPTR cstring, APTR bstring);
|
|
STATIC BPTR Action_Parent(struct FileLock *parent, LONG *error_ptr);
|
|
STATIC LONG Action_DeleteObject(struct FileLock *parent, APTR bcpl_name, LONG *error_ptr);
|
|
STATIC BPTR Action_CreateDir(struct FileLock *parent, APTR bcpl_name, LONG *error_ptr);
|
|
STATIC BPTR Action_LocateObject(struct FileLock *parent, APTR bcpl_name, LONG mode, LONG *error_ptr);
|
|
STATIC BPTR Action_CopyDir(struct FileLock *lock, LONG *error_ptr);
|
|
STATIC LONG Action_FreeLock(struct FileLock *lock, LONG *error_ptr);
|
|
STATIC LONG Action_SameLock(struct FileLock *lock1, struct FileLock *lock2, LONG *error_ptr);
|
|
STATIC LONG Action_SetProtect(struct FileLock *parent, APTR bcpl_name, LONG mask, LONG *error_ptr);
|
|
STATIC LONG Action_RenameObject(struct FileLock *source_lock, APTR source_bcpl_name, struct FileLock *destination_lock, APTR destination_bcpl_name, LONG *error_ptr);
|
|
STATIC LONG Action_DiskInfo(struct InfoData *id, LONG *error_ptr);
|
|
STATIC LONG Action_Info(struct FileLock *lock, struct InfoData *id, LONG *error_ptr);
|
|
STATIC LONG Action_ExamineObject(struct FileLock *lock, struct FileInfoBlock *fib, LONG *error_ptr);
|
|
STATIC BOOL NameIsAcceptable(STRPTR name, LONG max_len);
|
|
STATIC LONG Action_ExamineNext(struct FileLock *lock, struct FileInfoBlock *fib, LONG *error_ptr);
|
|
STATIC LONG Action_ExamineAll(struct FileLock *lock, struct ExAllData *ed, ULONG size, ULONG type, struct ExAllControl *eac, LONG *error_ptr);
|
|
STATIC LONG Action_Find(LONG action, struct FileHandle *fh, struct FileLock *parent, APTR bcpl_name, LONG *error_ptr);
|
|
STATIC LONG Action_Read(struct FileNode *fn, APTR mem, LONG length, LONG *error_ptr);
|
|
STATIC LONG Action_Write(struct FileNode *fn, APTR mem, LONG length, LONG *error_ptr);
|
|
STATIC LONG Action_End(struct FileNode *fn, LONG *error_ptr);
|
|
STATIC LONG Action_Seek(struct FileNode *fn, LONG position, LONG mode, LONG *error_ptr);
|
|
STATIC LONG Action_SetFileSize(struct FileNode *fn, LONG position, LONG mode, LONG *error_ptr);
|
|
STATIC LONG Action_SetDate(struct FileLock *parent, APTR bcpl_name, struct DateStamp *ds, LONG *error_ptr);
|
|
STATIC LONG Action_ExamineFH(struct FileNode *fn, struct FileInfoBlock *fib, LONG *error_ptr);
|
|
STATIC BPTR Action_ParentFH(struct FileNode *fn, LONG *error_ptr);
|
|
STATIC BPTR Action_CopyDirFH(struct FileNode *fn, LONG *error_ptr);
|
|
STATIC LONG Action_FHFromLock(struct FileHandle *fh, struct FileLock *fl, LONG *error_ptr);
|
|
STATIC LONG Action_RenameDisk(APTR bcpl_name, LONG *error_ptr);
|
|
STATIC LONG Action_ChangeMode(LONG type, APTR object, LONG new_mode, LONG *error_ptr);
|
|
STATIC LONG Action_WriteProtect(LONG flag, ULONG key, LONG *error_ptr);
|
|
STATIC LONG Action_MoreCache(LONG buffer_delta, LONG *error_ptr);
|
|
STATIC LONG Action_SetComment(struct FileLock *parent, APTR bcpl_name, APTR bcpl_comment, LONG *error_ptr);
|
|
STATIC LONG Action_LockRecord(struct FileNode *fn, LONG offset, LONG length, LONG mode, ULONG timeout, LONG *error_ptr);
|
|
STATIC LONG Action_FreeRecord(struct FileNode *fn, LONG offset, LONG length, LONG *error_ptr);
|
|
STATIC VOID HandleFileSystem(STRPTR device_name, STRPTR volume_name, STRPTR service_name);
|
|
|
|
/****************************************************************************/
|
|
|
|
/* This is in sock.c and controls whether SMB packets sent/received are
|
|
* decoded and printed in the shell.
|
|
*/
|
|
void control_smb_dump(int enable);
|
|
|
|
/****************************************************************************/
|
|
|
|
struct Library * SysBase;
|
|
struct Library * DOSBase;
|
|
struct Library * UtilityBase;
|
|
struct Library * IntuitionBase;
|
|
struct Library * SocketBase;
|
|
struct Library * LocaleBase;
|
|
struct Library * TimerBase;
|
|
struct Library * IconBase;
|
|
|
|
/****************************************************************************/
|
|
|
|
#if defined(__amigaos4__)
|
|
|
|
/****************************************************************************/
|
|
|
|
struct ExecIFace * IExec;
|
|
struct DOSIFace * IDOS;
|
|
struct UtilityIFace * IUtility;
|
|
struct IntuitionIFace * IIntuition;
|
|
struct SocketIFace * ISocket;
|
|
struct LocaleIFace * ILocale;
|
|
struct TimerIFace * ITimer;
|
|
struct IconIFace * IIcon;
|
|
|
|
/****************************************************************************/
|
|
|
|
#endif /* __amigaos4__ */
|
|
|
|
/****************************************************************************/
|
|
|
|
struct timerequest TimerRequest;
|
|
|
|
/****************************************************************************/
|
|
|
|
struct Locale * Locale;
|
|
|
|
/****************************************************************************/
|
|
|
|
int errno;
|
|
int h_errno;
|
|
|
|
/****************************************************************************/
|
|
|
|
STATIC struct DosList * DeviceNode;
|
|
STATIC BOOL DeviceNodeAdded;
|
|
STATIC struct DosList * VolumeNode;
|
|
STATIC BOOL VolumeNodeAdded;
|
|
STATIC struct MsgPort * FileSystemPort;
|
|
|
|
STATIC smba_server_t * ServerData;
|
|
|
|
STATIC BOOL Quit;
|
|
STATIC BOOL Quiet;
|
|
STATIC BOOL CaseSensitive;
|
|
STATIC BOOL OmitHidden;
|
|
STATIC BOOL TranslateUTF8;
|
|
|
|
STATIC LONG DSTOffset;
|
|
STATIC LONG TimeZoneOffset;
|
|
STATIC BOOL OverrideLocaleTimeZone;
|
|
|
|
STATIC BOOL WriteProtected;
|
|
STATIC ULONG WriteProtectKey;
|
|
|
|
STATIC struct MinList FileList;
|
|
STATIC struct MinList LockList;
|
|
|
|
STATIC APTR MemoryPool;
|
|
|
|
STATIC struct RDArgs * Parameters;
|
|
STATIC struct DiskObject * Icon;
|
|
|
|
STATIC struct WBStartup * WBStartup;
|
|
|
|
STATIC struct MinList ErrorList;
|
|
|
|
STATIC STRPTR NewProgramName;
|
|
|
|
STATIC BOOL TranslateNames;
|
|
STATIC UBYTE A2M[256];
|
|
STATIC UBYTE M2A[256];
|
|
|
|
/****************************************************************************/
|
|
|
|
extern int STDARGS swap_stack_and_call(struct StackSwapStruct * stk,APTR function);
|
|
|
|
/****************************************************************************/
|
|
|
|
LONG
|
|
_start(VOID)
|
|
{
|
|
struct StackSwapStruct * stk = NULL;
|
|
APTR new_stack = NULL;
|
|
LONG new_stack_size = 20000;
|
|
struct Process * this_process;
|
|
LONG result = RETURN_FAIL;
|
|
|
|
SysBase = (struct Library *)AbsExecBase;
|
|
|
|
#if defined(__amigaos4__)
|
|
{
|
|
IExec = (struct ExecIFace *)((struct ExecBase *)SysBase)->MainInterface;
|
|
}
|
|
#endif /* __amigaos4__ */
|
|
|
|
/* Pick up the Workbench startup message, if
|
|
* there is one.
|
|
*/
|
|
this_process = (struct Process *)FindTask(NULL);
|
|
if(this_process->pr_CLI == ZERO)
|
|
{
|
|
WaitPort(&this_process->pr_MsgPort);
|
|
WBStartup = (struct WBStartup *)GetMsg(&this_process->pr_MsgPort);
|
|
}
|
|
else
|
|
{
|
|
WBStartup = NULL;
|
|
}
|
|
|
|
/* Open the libraries we need and check
|
|
* whether we could get them.
|
|
*/
|
|
DOSBase = OpenLibrary("dos.library",0);
|
|
|
|
#if defined(__amigaos4__)
|
|
{
|
|
if(DOSBase != NULL)
|
|
{
|
|
IDOS = (struct DOSIFace *)GetInterface(DOSBase, "main", 1, 0);
|
|
if(IDOS == NULL)
|
|
{
|
|
CloseLibrary(DOSBase);
|
|
DOSBase = NULL;
|
|
}
|
|
}
|
|
}
|
|
#endif /* __amigaos4__ */
|
|
|
|
UtilityBase = OpenLibrary("utility.library",37);
|
|
|
|
#if defined(__amigaos4__)
|
|
{
|
|
if(UtilityBase != NULL)
|
|
{
|
|
IUtility = (struct UtilityIFace *)GetInterface(UtilityBase, "main", 1, 0);
|
|
if(IUtility == NULL)
|
|
{
|
|
CloseLibrary(UtilityBase);
|
|
UtilityBase = NULL;
|
|
}
|
|
}
|
|
}
|
|
#endif /* __amigaos4__ */
|
|
|
|
if(UtilityBase == NULL || DOSBase == NULL || DOSBase->lib_Version < MINIMUM_OS_VERSION)
|
|
{
|
|
/* Complain loudly if this is not the operating
|
|
* system version we expected.
|
|
*/
|
|
if(DOSBase != NULL && this_process->pr_CLI != ZERO)
|
|
{
|
|
STRPTR msg;
|
|
|
|
#if (MINIMUM_OS_VERSION < 39)
|
|
{
|
|
msg = "AmigaOS 2.04 or higher required.\n";
|
|
}
|
|
#else
|
|
{
|
|
msg = "AmigaOS 3.0 or higher required.\n";
|
|
}
|
|
#endif /* MINIMUM_OS_VERSION */
|
|
|
|
Write(Output(),msg,strlen(msg));
|
|
}
|
|
|
|
goto out;
|
|
}
|
|
|
|
if(get_stack_size() < new_stack_size)
|
|
{
|
|
/* Make the stack size a multiple of 32 bytes. */
|
|
new_stack_size = 32 + ((new_stack_size + 31UL) & ~31UL);
|
|
|
|
/* Allocate the stack swapping data structure
|
|
and the stack space separately. */
|
|
stk = AllocVec(sizeof(*stk),MEMF_PUBLIC|MEMF_ANY);
|
|
if(stk == NULL)
|
|
goto out;
|
|
|
|
new_stack = AllocMem(new_stack_size,MEMF_PUBLIC|MEMF_ANY);
|
|
if(new_stack == NULL)
|
|
goto out;
|
|
|
|
/* Fill in the lower and upper bounds, then take care of
|
|
the stack pointer itself. */
|
|
stk->stk_Lower = new_stack;
|
|
stk->stk_Upper = ((ULONG)new_stack) + new_stack_size;
|
|
stk->stk_Pointer = (APTR)(stk->stk_Upper - 32);
|
|
|
|
/* stack_usage_init(stk); */
|
|
|
|
result = swap_stack_and_call(stk,(APTR)main);
|
|
|
|
/* Printf("stack size used = %ld\n",stack_usage_exit(stk)); */
|
|
}
|
|
else
|
|
{
|
|
result = main();
|
|
}
|
|
|
|
out:
|
|
|
|
if(stk != NULL)
|
|
FreeVec(stk);
|
|
|
|
if(new_stack != NULL)
|
|
FreeMem(new_stack,new_stack_size);
|
|
|
|
#if defined(__amigaos4__)
|
|
{
|
|
if(IUtility != NULL)
|
|
{
|
|
DropInterface((struct Interface *)IUtility);
|
|
IUtility = NULL;
|
|
}
|
|
}
|
|
#endif /* __amigaos4__ */
|
|
|
|
if(UtilityBase != NULL)
|
|
{
|
|
CloseLibrary(UtilityBase);
|
|
UtilityBase = NULL;
|
|
}
|
|
|
|
#if defined(__amigaos4__)
|
|
{
|
|
if(IDOS != NULL)
|
|
{
|
|
DropInterface((struct Interface *)IDOS);
|
|
IDOS = NULL;
|
|
}
|
|
}
|
|
#endif /* __amigaos4__ */
|
|
|
|
if(DOSBase != NULL)
|
|
{
|
|
CloseLibrary(DOSBase);
|
|
DOSBase = NULL;
|
|
}
|
|
|
|
if(WBStartup != NULL)
|
|
{
|
|
Forbid();
|
|
ReplyMsg((struct Message *)WBStartup);
|
|
}
|
|
|
|
return(result);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
STATIC ULONG
|
|
get_stack_size(VOID)
|
|
{
|
|
struct Task * tc = FindTask(NULL);
|
|
ULONG upper,lower;
|
|
ULONG result;
|
|
|
|
/* How much stack size was provided? */
|
|
upper = (ULONG)tc->tc_SPUpper;
|
|
lower = (ULONG)tc->tc_SPLower;
|
|
|
|
result = upper - lower;
|
|
|
|
return(result);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
#define STACK_FILL_COOKIE 0xA1
|
|
|
|
/****************************************************************************/
|
|
|
|
STATIC VOID
|
|
stack_usage_init(struct StackSwapStruct * stk)
|
|
{
|
|
size_t stack_size = ((ULONG)stk->stk_Upper - (ULONG)stk->stk_Lower);
|
|
|
|
memset(stk->stk_Lower,STACK_FILL_COOKIE,stack_size);
|
|
}
|
|
|
|
STATIC ULONG
|
|
stack_usage_exit(struct StackSwapStruct * stk)
|
|
{
|
|
const UBYTE * m = (const UBYTE *)stk->stk_Lower;
|
|
size_t stack_size = ((ULONG)stk->stk_Upper - (ULONG)stk->stk_Lower);
|
|
size_t total,i;
|
|
|
|
total = 0;
|
|
|
|
/* Figure out how much of the stack was used by checking
|
|
if the fill pattern was overwritten. */
|
|
for(i = 0 ; i < stack_size ; i++)
|
|
{
|
|
/* Strangely, the first long word is always trashed,
|
|
even if the stack doesn't grow down this far... */
|
|
if(i > sizeof(LONG) && m[i] != STACK_FILL_COOKIE)
|
|
break;
|
|
|
|
total++;
|
|
}
|
|
|
|
return(total);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
STATIC LONG
|
|
main(VOID)
|
|
{
|
|
struct
|
|
{
|
|
KEY Workgroup;
|
|
KEY UserName;
|
|
KEY Password;
|
|
SWITCH ChangeCase;
|
|
SWITCH CaseSensitive;
|
|
SWITCH OmitHidden;
|
|
SWITCH Quiet;
|
|
KEY ClientName;
|
|
KEY ServerName;
|
|
KEY DeviceName;
|
|
KEY VolumeName;
|
|
NUMBER CacheSize;
|
|
NUMBER MaxTransmit;
|
|
NUMBER DebugLevel;
|
|
NUMBER TimeZoneOffset;
|
|
NUMBER DSTOffset;
|
|
SWITCH NetBIOSTransport;
|
|
SWITCH DumpSMB;
|
|
SWITCH UTF8;
|
|
KEY TranslationFile;
|
|
KEY Service;
|
|
} args;
|
|
|
|
STRPTR cmd_template =
|
|
"DOMAIN=WORKGROUP/K,"
|
|
"USER=USERNAME/K,"
|
|
"PASSWORD/K,"
|
|
"CHANGECASE/S,"
|
|
"CASE=CASESENSITIVE/S,"
|
|
"OMITHIDDEN/S,"
|
|
"QUIET/S,"
|
|
"CLIENT=CLIENTNAME/K,"
|
|
"SERVER=SERVERNAME/K,"
|
|
"DEVICE=DEVICENAME/K,"
|
|
"VOLUME=VOLUMENAME/K,"
|
|
"CACHE=CACHESIZE/N/K,"
|
|
"MAXTRANSMIT/N/K,"
|
|
"DEBUGLEVEL=DEBUG/N/K,"
|
|
"TZ=TIMEZONEOFFSET/N/K,"
|
|
"DST=DSTOFFSET/N/K,"
|
|
"NETBIOS/S,"
|
|
"DUMPSMB/S,"
|
|
"UTF8/S,"
|
|
"TRANSLATE=TRANSLATIONFILE/K,"
|
|
"SERVICE/A";
|
|
|
|
UBYTE program_name[MAX_FILENAME_LEN];
|
|
LONG result = RETURN_FAIL;
|
|
LONG number;
|
|
LONG other_number;
|
|
LONG cache_size = 0;
|
|
LONG max_transmit = -1;
|
|
char env_workgroup_name[17];
|
|
char env_user_name[64];
|
|
char env_password[64];
|
|
|
|
/* Don't emit any debugging output before we are ready. */
|
|
SETDEBUGLEVEL(0);
|
|
|
|
/* This needs to be set up properly for ReportError()
|
|
* to work.
|
|
*/
|
|
NewList((struct List *)&ErrorList);
|
|
|
|
memset(&args,0,sizeof(args));
|
|
|
|
/* If this program was launched from Workbench,
|
|
* parameter passing will have to be handled
|
|
* differently.
|
|
*/
|
|
if(WBStartup != NULL)
|
|
{
|
|
STRPTR str;
|
|
BPTR old_dir;
|
|
LONG n;
|
|
|
|
if(WBStartup->sm_NumArgs > 1)
|
|
n = 1;
|
|
else
|
|
n = 0;
|
|
|
|
/* Get the name of the program, as it was launched
|
|
* from Workbench. We actually prefer the name of
|
|
* the first project file, if there is one.
|
|
*/
|
|
strlcpy(program_name,WBStartup->sm_ArgList[n].wa_Name,sizeof(program_name));
|
|
|
|
/* Now open icon.library and read that icon. */
|
|
IconBase = OpenLibrary("icon.library",0);
|
|
|
|
#if defined(__amigaos4__)
|
|
{
|
|
if(IconBase != NULL)
|
|
{
|
|
IIcon = (struct IconIFace *)GetInterface(IconBase, "main", 1, 0);
|
|
if(IIcon == NULL)
|
|
{
|
|
CloseLibrary(IconBase);
|
|
IconBase = NULL;
|
|
}
|
|
}
|
|
}
|
|
#endif /* __amigaos4__ */
|
|
|
|
if(IconBase == NULL)
|
|
{
|
|
ReportError("Could not open 'icon.library'.");
|
|
goto out;
|
|
}
|
|
|
|
old_dir = CurrentDir(WBStartup->sm_ArgList[n].wa_Lock);
|
|
Icon = GetDiskObject(WBStartup->sm_ArgList[n].wa_Name);
|
|
CurrentDir(old_dir);
|
|
|
|
if(Icon == NULL)
|
|
{
|
|
ReportError("Icon not found.");
|
|
goto out;
|
|
}
|
|
|
|
/* Only input validation errors are reported below. */
|
|
result = RETURN_ERROR;
|
|
|
|
/* Examine the icon's tool types and use the
|
|
* information to fill the startup parameter
|
|
* data structure.
|
|
*/
|
|
str = FindToolType(Icon->do_ToolTypes,"DOMAIN");
|
|
if(str == NULL)
|
|
str = FindToolType(Icon->do_ToolTypes,"WORKGROUP");
|
|
|
|
if(str == NULL)
|
|
{
|
|
if(GetVar("smbfs_domain",env_workgroup_name,sizeof(env_workgroup_name),0) > 0 ||
|
|
GetVar("smbfs_workgroup",env_workgroup_name,sizeof(env_workgroup_name),0) > 0)
|
|
{
|
|
str = env_workgroup_name;
|
|
}
|
|
else
|
|
{
|
|
ReportError("Required 'WORKGROUP' parameter was not provided.");
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
args.Workgroup = str;
|
|
|
|
str = FindToolType(Icon->do_ToolTypes,"USER");
|
|
if(str == NULL)
|
|
str = FindToolType(Icon->do_ToolTypes,"USERNAME");
|
|
|
|
if(str == NULL)
|
|
{
|
|
if(GetVar("smbfs_user",env_user_name,sizeof(env_user_name),0) > 0 ||
|
|
GetVar("smbfs_username",env_user_name,sizeof(env_user_name),0) > 0)
|
|
{
|
|
str = env_user_name;
|
|
}
|
|
}
|
|
|
|
args.UserName = str;
|
|
|
|
str = FindToolType(Icon->do_ToolTypes,"PASSWORD");
|
|
if(str == NULL)
|
|
{
|
|
if(GetVar("smbfs_password",env_password,sizeof(env_password),0) > 0)
|
|
str = env_password;
|
|
}
|
|
|
|
args.Password = str;
|
|
|
|
if(FindToolType(Icon->do_ToolTypes,"CHANGECASE") != NULL)
|
|
args.ChangeCase = TRUE;
|
|
|
|
if(FindToolType(Icon->do_ToolTypes,"OMITHIDDEN") != NULL)
|
|
args.OmitHidden = TRUE;
|
|
|
|
if(FindToolType(Icon->do_ToolTypes,"QUIET") != NULL)
|
|
args.Quiet = TRUE;
|
|
|
|
if(FindToolType(Icon->do_ToolTypes,"CASE") != NULL ||
|
|
FindToolType(Icon->do_ToolTypes,"CASESENSITIVE") != NULL)
|
|
{
|
|
args.CaseSensitive = TRUE;
|
|
}
|
|
|
|
str = FindToolType(Icon->do_ToolTypes,"CLIENT");
|
|
if(str == NULL)
|
|
str = FindToolType(Icon->do_ToolTypes,"CLIENTNAME");
|
|
|
|
args.ClientName = str;
|
|
|
|
str = FindToolType(Icon->do_ToolTypes,"SERVER");
|
|
if(str == NULL)
|
|
str = FindToolType(Icon->do_ToolTypes,"SERVERNAME");
|
|
|
|
args.ServerName = str;
|
|
|
|
str = FindToolType(Icon->do_ToolTypes,"DEVICE");
|
|
if(str == NULL)
|
|
str = FindToolType(Icon->do_ToolTypes,"DEVICENAME");
|
|
|
|
args.DeviceName = str;
|
|
|
|
str = FindToolType(Icon->do_ToolTypes,"VOLUME");
|
|
if(str == NULL)
|
|
str = FindToolType(Icon->do_ToolTypes,"VOLUMENAME");
|
|
|
|
args.VolumeName = str;
|
|
|
|
str = FindToolType(Icon->do_ToolTypes,"TRANSLATE");
|
|
if(str == NULL)
|
|
str = FindToolType(Icon->do_ToolTypes,"TRANSLATIONFILE");
|
|
|
|
args.TranslationFile = str;
|
|
|
|
str = FindToolType(Icon->do_ToolTypes,"SERVICE");
|
|
args.Service = str;
|
|
|
|
if(str != NULL)
|
|
{
|
|
/* Set up the name of the program, as it will be
|
|
* displayed in error requesters.
|
|
*/
|
|
NewProgramName = AllocVec(strlen(WBStartup->sm_ArgList[0].wa_Name) + strlen(" ''") + strlen(str)+1,MEMF_ANY|MEMF_PUBLIC);
|
|
if(NewProgramName != NULL)
|
|
SPrintf(NewProgramName,"%s '%s'",WBStartup->sm_ArgList[0].wa_Name,str);
|
|
}
|
|
|
|
str = FindToolType(Icon->do_ToolTypes,"DEBUG");
|
|
if(str == NULL)
|
|
str = FindToolType(Icon->do_ToolTypes,"DEBUGLEVEL");
|
|
|
|
if(str != NULL)
|
|
{
|
|
if(StrToLong(str,&number) == -1)
|
|
{
|
|
ReportError("Invalid number '%s'�for 'DEBUG' parameter.",str);
|
|
goto out;
|
|
}
|
|
|
|
args.DebugLevel = &number;
|
|
}
|
|
|
|
str = FindToolType(Icon->do_ToolTypes,"TZ");
|
|
if(str == NULL)
|
|
str = FindToolType(Icon->do_ToolTypes,"TIMEZONEOFFSET");
|
|
|
|
if(str != NULL)
|
|
{
|
|
if(StrToLong(str,&other_number) == -1)
|
|
{
|
|
ReportError("Invalid number '%s'�for 'TIMEZONEOFFSET' parameter.",str);
|
|
goto out;
|
|
}
|
|
|
|
args.TimeZoneOffset = &other_number;
|
|
}
|
|
|
|
if(FindToolType(Icon->do_ToolTypes,"NETBIOS") != NULL)
|
|
args.NetBIOSTransport = TRUE;
|
|
|
|
if(FindToolType(Icon->do_ToolTypes,"UTF8") != NULL)
|
|
args.UTF8 = TRUE;
|
|
|
|
str = FindToolType(Icon->do_ToolTypes,"DST");
|
|
if(str == NULL)
|
|
str = FindToolType(Icon->do_ToolTypes,"DSTOFFSET");
|
|
|
|
if(str != NULL)
|
|
{
|
|
if(StrToLong(str,&other_number) == -1)
|
|
{
|
|
ReportError("Invalid number '%s'�for 'DSTOFFSET' parameter.",str);
|
|
goto out;
|
|
}
|
|
|
|
args.DSTOffset = &other_number;
|
|
}
|
|
|
|
str = FindToolType(Icon->do_ToolTypes,"CACHE");
|
|
if(str == NULL)
|
|
str = FindToolType(Icon->do_ToolTypes,"CACHESIZE");
|
|
|
|
if(str != NULL)
|
|
{
|
|
if(StrToLong(str,&number) == -1)
|
|
{
|
|
ReportError("Invalid number '%s'�for 'CACHE' parameter.",str);
|
|
goto out;
|
|
}
|
|
|
|
cache_size = number;
|
|
}
|
|
|
|
str = FindToolType(Icon->do_ToolTypes,"MAXTRANSMIT");
|
|
if(str != NULL)
|
|
{
|
|
if(StrToLong(str,&number) == -1)
|
|
{
|
|
ReportError("Invalid number '%s'�for 'MAXTRANSMIT' parameter.",str);
|
|
goto out;
|
|
}
|
|
|
|
max_transmit = number;
|
|
}
|
|
|
|
if(args.Workgroup == NULL)
|
|
{
|
|
ReportError("Required 'WORKGROUP' parameter was not provided.");
|
|
goto out;
|
|
}
|
|
|
|
if(args.Service == NULL)
|
|
{
|
|
ReportError("'SERVICE' parameter needs an argument.");
|
|
goto out;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Only input validation errors are reported below. */
|
|
result = RETURN_ERROR;
|
|
|
|
GetProgramName(program_name,sizeof(program_name));
|
|
|
|
Parameters = ReadArgs(cmd_template,(LONG *)&args,NULL);
|
|
if(Parameters == NULL)
|
|
{
|
|
PrintFault(IoErr(),FilePart(program_name));
|
|
goto out;
|
|
}
|
|
|
|
if(args.Workgroup == NULL)
|
|
{
|
|
if(GetVar("smbfs_domain",env_workgroup_name,sizeof(env_workgroup_name),0) > 0 ||
|
|
GetVar("smbfs_workgroup",env_workgroup_name,sizeof(env_workgroup_name),0) > 0)
|
|
{
|
|
args.Workgroup = env_workgroup_name;
|
|
}
|
|
else
|
|
{
|
|
ReportError("Required 'WORKGROUP' parameter was not provided.");
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
if(args.UserName == NULL)
|
|
{
|
|
if(GetVar("smbfs_user",env_user_name,sizeof(env_user_name),0) > 0 ||
|
|
GetVar("smbfs_username",env_user_name,sizeof(env_user_name),0) > 0)
|
|
{
|
|
args.UserName = env_user_name;
|
|
}
|
|
}
|
|
|
|
if(args.Password == NULL)
|
|
{
|
|
if(GetVar("smbfs_password",env_password,sizeof(env_password),0) > 0)
|
|
args.Password = env_password;
|
|
}
|
|
|
|
if(args.Service != NULL)
|
|
{
|
|
STRPTR name = FilePart(program_name);
|
|
|
|
/* Set up the name of the program, as it will be
|
|
* displayed in the proces status list.
|
|
*/
|
|
NewProgramName = AllocVec(strlen(name) + strlen(" ''") + strlen(args.Service)+1,MEMF_ANY|MEMF_PUBLIC);
|
|
if(NewProgramName != NULL)
|
|
SPrintf(NewProgramName,"%s '%s'",name,args.Service);
|
|
}
|
|
|
|
if(args.CacheSize != NULL)
|
|
cache_size = (*args.CacheSize);
|
|
|
|
if(args.MaxTransmit != NULL)
|
|
max_transmit = (*args.MaxTransmit);
|
|
}
|
|
|
|
/* Use the default if no user name is given. */
|
|
if(args.UserName == NULL)
|
|
args.UserName = "GUEST";
|
|
|
|
/* Use the default if no device or volume name is given. */
|
|
if(args.DeviceName == NULL && args.VolumeName == NULL)
|
|
args.DeviceName = "SMBFS";
|
|
|
|
/* UTF8 file name translation disables code-page based translation. */
|
|
if(args.UTF8)
|
|
args.TranslationFile = NULL;
|
|
|
|
CaseSensitive = (BOOL)args.CaseSensitive;
|
|
OmitHidden = (BOOL)args.OmitHidden;
|
|
TranslateUTF8 = (BOOL)args.UTF8;
|
|
|
|
/* Configure the debugging options. */
|
|
SETPROGRAMNAME(FilePart(program_name));
|
|
|
|
if(args.DebugLevel != NULL)
|
|
SETDEBUGLEVEL(*args.DebugLevel);
|
|
else
|
|
SETDEBUGLEVEL(0);
|
|
|
|
/* Enable SMB packet decoding, but only if not started from Workbench. */
|
|
#if defined(DUMP_SMB)
|
|
{
|
|
if(args.DumpSMB && WBStartup == NULL)
|
|
control_smb_dump(TRUE);
|
|
}
|
|
#endif /* DUMP_SMB */
|
|
|
|
D(("%s (%s)",VERS,DATE));
|
|
|
|
if(Setup(
|
|
FilePart(program_name),
|
|
args.Service,
|
|
args.Workgroup,
|
|
args.UserName,
|
|
args.Password,
|
|
args.ChangeCase,
|
|
args.ClientName,
|
|
args.ServerName,
|
|
cache_size,
|
|
max_transmit,
|
|
args.TimeZoneOffset,
|
|
args.DSTOffset,
|
|
!args.NetBIOSTransport, /* Use raw SMB transport instead of NetBIOS transport? */
|
|
args.DeviceName,
|
|
args.VolumeName,
|
|
args.TranslationFile))
|
|
{
|
|
Quiet = args.Quiet;
|
|
|
|
if(Locale != NULL)
|
|
SHOWVALUE(Locale->loc_GMTOffset);
|
|
|
|
HandleFileSystem(args.DeviceName,args.VolumeName,args.Service);
|
|
|
|
result = RETURN_WARN;
|
|
}
|
|
else
|
|
{
|
|
result = RETURN_ERROR;
|
|
}
|
|
|
|
out:
|
|
|
|
Cleanup();
|
|
|
|
return(result);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
LONG VARARGS68K
|
|
LocalPrintf(STRPTR format, ...)
|
|
{
|
|
va_list args;
|
|
LONG result;
|
|
|
|
#if defined(__amigaos4__)
|
|
{
|
|
va_startlinear(args,format);
|
|
result = VPrintf(format,va_getlinearva(args,APTR));
|
|
va_end(args);
|
|
}
|
|
#else
|
|
{
|
|
va_start(args,format);
|
|
result = VPrintf(format,args);
|
|
va_end(args);
|
|
}
|
|
#endif /* __amigaos4__ */
|
|
|
|
return(result);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
/* Obtain the descriptive text corresponding to an error number
|
|
* that may have been generated by the TCP/IP stack.
|
|
*/
|
|
STRPTR
|
|
amitcp_strerror(int error)
|
|
{
|
|
struct TagItem tags[2];
|
|
STRPTR result;
|
|
|
|
ENTER();
|
|
|
|
tags[0].ti_Tag = SBTM_GETVAL(SBTC_ERRNOSTRPTR);
|
|
tags[0].ti_Data = error;
|
|
tags[1].ti_Tag = TAG_END;
|
|
|
|
SocketBaseTagList(tags);
|
|
|
|
result = (STRPTR)tags[0].ti_Data;
|
|
|
|
RETURN(result);
|
|
return(result);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
/* Return the descriptive text associated with a host lookup failure code. */
|
|
STRPTR
|
|
host_strerror(int error)
|
|
{
|
|
struct TagItem tags[2];
|
|
STRPTR result;
|
|
|
|
ENTER();
|
|
|
|
tags[0].ti_Tag = SBTM_GETVAL(SBTC_HERRNOSTRPTR);
|
|
tags[0].ti_Data = error;
|
|
tags[1].ti_Tag = TAG_END;
|
|
|
|
SocketBaseTagList(tags);
|
|
|
|
result = (STRPTR)tags[0].ti_Data;
|
|
|
|
RETURN(result);
|
|
return(result);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
/* Compare two strings, either case sensitive or not
|
|
* sensitive to the case of the letters. How this is
|
|
* to be done is controlled by a global option. This
|
|
* routine is called whenever two SMB file names are
|
|
* to be compared.
|
|
*/
|
|
LONG
|
|
CompareNames(STRPTR a,STRPTR b)
|
|
{
|
|
LONG result;
|
|
|
|
if(CaseSensitive)
|
|
result = strcmp(a,b);
|
|
else
|
|
result = Stricmp(a,b);
|
|
|
|
return(result);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
/* Translate a string into all upper case characters. */
|
|
VOID
|
|
StringToUpper(STRPTR s)
|
|
{
|
|
UBYTE c;
|
|
|
|
while((c = (*s)) != '\0')
|
|
(*s++) = ToUpper(c);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
/* Prepare the accumulated list of error messages for display
|
|
* and purge the contents of that list.
|
|
*/
|
|
STATIC VOID
|
|
DisplayErrorList(VOID)
|
|
{
|
|
struct MinNode * last = NULL;
|
|
struct MinNode * mn;
|
|
STRPTR str = NULL;
|
|
STRPTR msg;
|
|
LONG len;
|
|
|
|
/* Determine how much memory will have to be
|
|
* allocated to hold all the accumulated
|
|
* error messages.
|
|
*/
|
|
len = 0;
|
|
|
|
for(mn = ErrorList.mlh_Head ;
|
|
mn->mln_Succ != NULL ;
|
|
mn = mn->mln_Succ)
|
|
{
|
|
last = mn;
|
|
|
|
msg = (STRPTR)(mn + 1);
|
|
|
|
len += strlen(msg)+1;
|
|
}
|
|
|
|
/* Allocate the memory for the messages, then
|
|
* copy them there.
|
|
*/
|
|
if(len > 0)
|
|
{
|
|
str = AllocVec(len,MEMF_ANY);
|
|
if(str != NULL)
|
|
{
|
|
str[0] = '\0';
|
|
|
|
for(mn = ErrorList.mlh_Head ;
|
|
mn->mln_Succ != NULL ;
|
|
mn = mn->mln_Succ)
|
|
{
|
|
msg = (STRPTR)(mn + 1);
|
|
|
|
strcat(str,msg);
|
|
if(mn != last)
|
|
strcat(str,"\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Purge the list. */
|
|
while((mn = (struct MinNode *)RemHead((struct List *)&ErrorList)) != NULL)
|
|
FreeVec(mn);
|
|
|
|
/* Display the error messages. */
|
|
if(str != NULL)
|
|
{
|
|
IntuitionBase = OpenLibrary("intuition.library",37);
|
|
|
|
#if defined(__amigaos4__)
|
|
{
|
|
if(IntuitionBase != NULL)
|
|
{
|
|
IIntuition = (struct IntuitionIFace *)GetInterface(IntuitionBase, "main", 1, 0);
|
|
if(IIntuition == NULL)
|
|
{
|
|
CloseLibrary(IntuitionBase);
|
|
IntuitionBase = NULL;
|
|
}
|
|
}
|
|
}
|
|
#endif /* __amigaos4__ */
|
|
|
|
if(IntuitionBase != NULL)
|
|
{
|
|
struct EasyStruct es;
|
|
STRPTR title;
|
|
|
|
memset(&es,0,sizeof(es));
|
|
|
|
if(NewProgramName == NULL)
|
|
title = WBStartup->sm_ArgList[0].wa_Name;
|
|
else
|
|
title = NewProgramName;
|
|
|
|
es.es_StructSize = sizeof(es);
|
|
es.es_Title = title;
|
|
es.es_TextFormat = str;
|
|
es.es_GadgetFormat = "Ok";
|
|
|
|
EasyRequestArgs(NULL,&es,NULL,NULL);
|
|
}
|
|
|
|
FreeVec(str);
|
|
}
|
|
|
|
#if defined(__amigaos4__)
|
|
{
|
|
if(IIntuition != NULL)
|
|
{
|
|
DropInterface((struct Interface *)IIntuition);
|
|
IIntuition = NULL;
|
|
}
|
|
}
|
|
#endif /* __amigaos4__ */
|
|
|
|
CloseLibrary(IntuitionBase);
|
|
IntuitionBase = NULL;
|
|
}
|
|
|
|
/* Add another error message to the list; the messages are
|
|
* collected so that they may be displayed together when
|
|
* necessary.
|
|
*/
|
|
STATIC VOID
|
|
AddError(STRPTR fmt,APTR args)
|
|
{
|
|
LONG len;
|
|
|
|
len = CVSPrintf(fmt,args);
|
|
if(len > 0)
|
|
{
|
|
struct MinNode * mn;
|
|
|
|
mn = AllocVec(sizeof(*mn) + len,MEMF_ANY|MEMF_PUBLIC);
|
|
if(mn != NULL)
|
|
{
|
|
STRPTR msg = (STRPTR)(mn + 1);
|
|
|
|
VSPrintf(msg,fmt,args);
|
|
|
|
AddTail((struct List *)&ErrorList,(struct Node *)mn);
|
|
}
|
|
}
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
/* Report an error that has occured; if the program was not launched
|
|
* from Shell, error messages will be accumulated for later display.
|
|
*/
|
|
VOID VARARGS68K
|
|
ReportError(STRPTR fmt,...)
|
|
{
|
|
if(NOT Quiet)
|
|
{
|
|
va_list args;
|
|
|
|
if(WBStartup != NULL)
|
|
{
|
|
#if defined(__amigaos4__)
|
|
{
|
|
va_startlinear(args,fmt);
|
|
AddError(fmt,va_getlinearva(args,APTR));
|
|
va_end(args);
|
|
}
|
|
#else
|
|
{
|
|
va_start(args,fmt);
|
|
AddError(fmt,args);
|
|
va_end(args);
|
|
}
|
|
#endif /* __amigaos4__ */
|
|
}
|
|
else
|
|
{
|
|
UBYTE program_name[MAX_FILENAME_LEN];
|
|
|
|
GetProgramName(program_name,sizeof(program_name));
|
|
|
|
LocalPrintf("%s: ",FilePart(program_name));
|
|
|
|
#if defined(__amigaos4__)
|
|
{
|
|
va_startlinear(args,fmt);
|
|
VPrintf(fmt,va_getlinearva(args,APTR));
|
|
va_end(args);
|
|
}
|
|
#else
|
|
{
|
|
va_start(args,fmt);
|
|
VPrintf(fmt,args);
|
|
va_end(args);
|
|
}
|
|
#endif /* __amigaos4__ */
|
|
|
|
LocalPrintf("\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
/* Release memory allocated from the global pool. */
|
|
VOID
|
|
FreeMemory(APTR address)
|
|
{
|
|
if(address != NULL)
|
|
{
|
|
ULONG * mem = address;
|
|
|
|
#if DEBUG
|
|
{
|
|
if(GETDEBUGLEVEL() > 0)
|
|
memset(address,0xA3,mem[-1] - sizeof(*mem));
|
|
}
|
|
#endif /* DEBUG */
|
|
|
|
FreePooled(MemoryPool,&mem[-1],mem[-1]);
|
|
}
|
|
}
|
|
|
|
/* Allocate memory from the global pool. */
|
|
APTR
|
|
AllocateMemory(ULONG size)
|
|
{
|
|
APTR result = NULL;
|
|
|
|
if(size > 0)
|
|
{
|
|
ULONG * mem;
|
|
|
|
size = (sizeof(*mem) + size + 7) & ~7UL;
|
|
|
|
mem = AllocPooled(MemoryPool,size);
|
|
if(mem != NULL)
|
|
{
|
|
(*mem++) = size;
|
|
|
|
#if DEBUG
|
|
{
|
|
if(GETDEBUGLEVEL() > 0)
|
|
memset(mem,0xA5,mem[-1] - sizeof(*mem));
|
|
}
|
|
#endif /* DEBUG */
|
|
|
|
result = mem;
|
|
}
|
|
}
|
|
|
|
return(result);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
/* Obtain the number of seconds to add to the current time
|
|
* to translate local time into UTC.
|
|
*/
|
|
LONG
|
|
GetTimeZoneDelta(VOID)
|
|
{
|
|
LONG seconds;
|
|
|
|
if(OverrideLocaleTimeZone)
|
|
{
|
|
seconds = 60 * TimeZoneOffset;
|
|
}
|
|
else if (Locale != NULL)
|
|
{
|
|
/* The GMT offset actually is the number of minutes to add to
|
|
* the local time to yield Greenwich Mean Time. It is negative
|
|
* for all time zones east of the Greenwich meridian and
|
|
* positive for all time zones west of it.
|
|
*/
|
|
seconds = 60 * Locale->loc_GMTOffset;
|
|
}
|
|
else
|
|
{
|
|
seconds = 0;
|
|
}
|
|
|
|
return(seconds + DSTOffset);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
/* Obtain the current time, in standard Unix format, adjusted for the
|
|
* local time zone.
|
|
*/
|
|
ULONG
|
|
GetCurrentTime(VOID)
|
|
{
|
|
struct timeval tv;
|
|
ULONG result;
|
|
|
|
GetSysTime((APTR)&tv);
|
|
|
|
result = UNIX_TIME_OFFSET + GetTimeZoneDelta() + tv.tv_secs;
|
|
|
|
return(result);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
/* Fill in a 'tm' type time specification with time information
|
|
* corresponding to the number of seconds provided. Input is
|
|
* in Unix format.
|
|
*/
|
|
VOID
|
|
GMTime(time_t seconds,struct tm * tm)
|
|
{
|
|
struct ClockData clock_data;
|
|
|
|
if(seconds < UNIX_TIME_OFFSET)
|
|
seconds = 0;
|
|
else
|
|
seconds -= UNIX_TIME_OFFSET;
|
|
|
|
Amiga2Date(seconds,&clock_data);
|
|
|
|
memset(tm,0,sizeof(*tm));
|
|
|
|
tm->tm_sec = clock_data.sec;
|
|
tm->tm_min = clock_data.min;
|
|
tm->tm_hour = clock_data.hour;
|
|
tm->tm_mday = clock_data.mday;
|
|
tm->tm_mon = clock_data.month - 1;
|
|
tm->tm_year = clock_data.year - 1900;
|
|
}
|
|
|
|
/* Calculate the number of seconds that have passed since January 1st 1970
|
|
* based upon the time specification provided. Output is in Unix format.
|
|
*/
|
|
time_t
|
|
MakeTime(const struct tm * const tm)
|
|
{
|
|
struct ClockData clock_data;
|
|
time_t seconds;
|
|
|
|
clock_data.sec = tm->tm_sec;
|
|
clock_data.min = tm->tm_min;
|
|
clock_data.hour = tm->tm_hour;
|
|
clock_data.mday = tm->tm_mday;
|
|
clock_data.month = tm->tm_mon + 1;
|
|
clock_data.year = tm->tm_year + 1900;
|
|
|
|
seconds = Date2Amiga(&clock_data) + UNIX_TIME_OFFSET;
|
|
|
|
return(seconds);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
struct FormatContext
|
|
{
|
|
UBYTE * fc_Buffer;
|
|
LONG fc_Size;
|
|
};
|
|
|
|
/****************************************************************************/
|
|
|
|
STATIC VOID ASM
|
|
CountChar(REG(a3,struct FormatContext * fc))
|
|
{
|
|
fc->fc_Size++;
|
|
}
|
|
|
|
/* Count the number of characters SPrintf() would put into a string. */
|
|
STATIC LONG
|
|
CVSPrintf(STRPTR format_string,APTR args)
|
|
{
|
|
struct FormatContext fc;
|
|
|
|
fc.fc_Size = 0;
|
|
|
|
RawDoFmt((STRPTR)format_string,args,(VOID (*)())CountChar,&fc);
|
|
|
|
return(fc.fc_Size);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
STATIC VOID ASM
|
|
StuffChar(REG(d0,UBYTE c),REG(a3,struct FormatContext * fc))
|
|
{
|
|
(*fc->fc_Buffer++) = c;
|
|
}
|
|
|
|
STATIC VOID
|
|
VSPrintf(STRPTR buffer, STRPTR formatString, APTR args)
|
|
{
|
|
struct FormatContext fc;
|
|
|
|
fc.fc_Buffer = buffer;
|
|
|
|
RawDoFmt(formatString,args,(VOID (*)())StuffChar,&fc);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
/* Format a string for output. */
|
|
VOID VARARGS68K
|
|
SPrintf(STRPTR buffer, STRPTR formatString,...)
|
|
{
|
|
va_list varArgs;
|
|
|
|
#if defined(__amigaos4__)
|
|
{
|
|
va_startlinear(varArgs,formatString);
|
|
VSPrintf(buffer,formatString,va_getlinearva(varArgs,APTR));
|
|
va_end(varArgs);
|
|
}
|
|
#else
|
|
{
|
|
va_start(varArgs,formatString);
|
|
VSPrintf(buffer,formatString,varArgs);
|
|
va_end(varArgs);
|
|
}
|
|
#endif /* __amigaos4__ */
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
/* NetBIOS broadcast name query code courtesy of Christopher R. Hertel.
|
|
* Thanks very much, Chris!
|
|
*/
|
|
struct addr_entry
|
|
{
|
|
unsigned short flags;
|
|
unsigned char address[4];
|
|
};
|
|
|
|
struct nmb_header
|
|
{
|
|
unsigned short name_trn_id;
|
|
unsigned short flags;
|
|
unsigned short qdcount;
|
|
unsigned short ancount;
|
|
unsigned short nscount;
|
|
unsigned short arcount;
|
|
};
|
|
|
|
static UBYTE *
|
|
L1_Encode(UBYTE * dst, const UBYTE * name, const UBYTE pad, const UBYTE sfx)
|
|
{
|
|
int i = 0;
|
|
int j = 0;
|
|
int k;
|
|
|
|
while(('\0' != name[i]) && (i < 15))
|
|
{
|
|
k = ToUpper(name[i]);
|
|
i++;
|
|
dst[j++] = 'A' + ((k & 0xF0) >> 4);
|
|
dst[j++] = 'A' + (k & 0x0F);
|
|
}
|
|
|
|
i = 'A' + ((pad & 0xF0) >> 4);
|
|
k = 'A' + (pad & 0x0F);
|
|
|
|
while(j < 30)
|
|
{
|
|
dst[j++] = i;
|
|
dst[j++] = k;
|
|
}
|
|
|
|
dst[30] = 'A' + ((sfx & 0xF0) >> 4);
|
|
dst[31] = 'A' + (sfx & 0x0F);
|
|
dst[32] = '\0';
|
|
|
|
return(dst);
|
|
}
|
|
|
|
static int
|
|
L2_Encode(UBYTE * dst, const UBYTE * name, const UBYTE pad, const UBYTE sfx, const UBYTE * scope)
|
|
{
|
|
int lenpos;
|
|
int i;
|
|
int j;
|
|
|
|
if(NULL == L1_Encode(&dst[1], name, pad, sfx))
|
|
return(-1);
|
|
|
|
dst[0] = 0x20;
|
|
lenpos = 33;
|
|
|
|
if('\0' != (*scope))
|
|
{
|
|
do
|
|
{
|
|
for(i = 0, j = (lenpos + 1);
|
|
('.' != scope[i]) && ('\0' != scope[i]);
|
|
i++, j++)
|
|
{
|
|
dst[j] = ToUpper(scope[i]);
|
|
}
|
|
|
|
dst[lenpos] = (UBYTE)i;
|
|
lenpos += i + 1;
|
|
scope += i;
|
|
}
|
|
while('.' == (*scope++));
|
|
|
|
dst[lenpos] = '\0';
|
|
}
|
|
|
|
return(lenpos + 1);
|
|
}
|
|
|
|
int
|
|
BroadcastNameQuery(char *name, char *scope, UBYTE *address)
|
|
{
|
|
static const UBYTE header[12] =
|
|
{
|
|
0x07, 0xB0, /* 1964 == 0x07B0. */
|
|
0x01, 0x10, /* Binary 0 0000 0010001 0000 */
|
|
0x00, 0x01, /* One name query. */
|
|
0x00, 0x00, /* Zero answers. */
|
|
0x00, 0x00, /* Zero authorities. */
|
|
0x00, 0x00 /* Zero additional. */
|
|
};
|
|
|
|
static const UBYTE query_tail[4] =
|
|
{
|
|
0x00, 0x20,
|
|
0x00, 0x01
|
|
};
|
|
|
|
struct timeval tv;
|
|
fd_set read_fds;
|
|
int sock_fd;
|
|
int option_true = 1;
|
|
struct sockaddr_in sox;
|
|
struct nmb_header nmb_header;
|
|
UBYTE buffer[512];
|
|
int total_len;
|
|
int i,n;
|
|
int result;
|
|
struct servent * s;
|
|
|
|
ENTER();
|
|
|
|
sock_fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
|
if(sock_fd < 0)
|
|
{
|
|
SHOWMSG("couldn't get the socket");
|
|
result = (-errno);
|
|
goto out;
|
|
}
|
|
|
|
if(setsockopt(sock_fd, SOL_SOCKET, SO_BROADCAST, &option_true, sizeof(option_true)) < 0)
|
|
{
|
|
SHOWMSG("couldn't enable the broadcast option");
|
|
result = (-errno);
|
|
goto out;
|
|
}
|
|
|
|
sox.sin_family = AF_INET;
|
|
sox.sin_addr.s_addr = htonl(0xFFFFFFFF);
|
|
|
|
s = getservbyname("netbios-ns","udp");
|
|
if(s != NULL)
|
|
sox.sin_port = s->s_port;
|
|
else
|
|
sox.sin_port = htons(137);
|
|
|
|
memcpy(buffer, header, (total_len = sizeof(header)));
|
|
|
|
n = L2_Encode(&buffer[total_len], name, ' ', '\0', scope);
|
|
if(n < 0)
|
|
{
|
|
SHOWMSG("name encoding failed");
|
|
result = (-EINVAL);
|
|
goto out;
|
|
}
|
|
|
|
total_len += n;
|
|
memcpy(&buffer[total_len], query_tail, sizeof(query_tail));
|
|
total_len += sizeof(query_tail);
|
|
|
|
result = (-ENOENT);
|
|
n = 0;
|
|
|
|
/* Send the query packet; retry five times with a one second
|
|
* delay in between.
|
|
*/
|
|
for(i = 0 ; i < 5 ; i++)
|
|
{
|
|
if(sendto(sock_fd, (void *) buffer, total_len, 0, (struct sockaddr *)&sox, sizeof(struct sockaddr_in)) < 0)
|
|
{
|
|
SHOWMSG("could not send the packet");
|
|
result = (-errno);
|
|
goto out;
|
|
}
|
|
|
|
/* Wait for a response to arrive. */
|
|
tv.tv_secs = 1;
|
|
tv.tv_micro = 0;
|
|
|
|
FD_ZERO(&read_fds);
|
|
FD_SET(sock_fd,&read_fds);
|
|
|
|
if(WaitSelect(sock_fd+1, &read_fds, NULL, NULL, &tv, NULL) > 0)
|
|
{
|
|
n = recvfrom(sock_fd, buffer, sizeof(buffer), 0, NULL, NULL);
|
|
if(n < 0)
|
|
{
|
|
SHOWMSG("could not pick up the response packet");
|
|
result = (-errno);
|
|
goto out;
|
|
}
|
|
else if (n > 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Did we get anything at all? */
|
|
if(n > (int)sizeof(nmb_header))
|
|
{
|
|
/* Check whether the query was successful. */
|
|
memcpy(&nmb_header, buffer, sizeof(nmb_header));
|
|
if((nmb_header.flags & 0xF) == OK)
|
|
{
|
|
/* Find the NB/IP fields which directly follow
|
|
* the name.
|
|
*/
|
|
for(i = sizeof(header) + strlen(&buffer[sizeof(header)])+1 ; i < n - (int)sizeof(query_tail) ; i++)
|
|
{
|
|
if(memcmp(&buffer[i], query_tail, sizeof(query_tail)) == SAME)
|
|
{
|
|
int start;
|
|
|
|
/* This should be the start of the interesting bits;
|
|
* we skip the NB/IP fields and the TTL field.
|
|
*/
|
|
start = i + sizeof(query_tail) + sizeof(long);
|
|
if(start < n)
|
|
{
|
|
unsigned short read_len;
|
|
struct addr_entry addr_entry;
|
|
|
|
/* This should be the read length. */
|
|
memcpy(&read_len, &buffer[start], 2);
|
|
|
|
/* Is there any useful and readable data attached? */
|
|
if(read_len >= sizeof(addr_entry) &&
|
|
start + (int)sizeof(read_len) + (int)sizeof(addr_entry) <= n)
|
|
{
|
|
/* Copy a single address entry; this should be
|
|
* just the one we need.
|
|
*/
|
|
memcpy(&addr_entry, &buffer[start + sizeof(read_len)], sizeof(addr_entry));
|
|
|
|
/* Copy the address field (IPv4 only). */
|
|
memcpy(address, addr_entry.address, 4);
|
|
|
|
result = 0;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
out:
|
|
|
|
if(sock_fd >= 0)
|
|
CloseSocket(sock_fd);
|
|
|
|
RETURN(result);
|
|
return(result);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
/* Send a disk change notification message which will be picked up
|
|
* by all applications that listen for this kind of event, e.g.
|
|
* Workbench.
|
|
*/
|
|
STATIC VOID
|
|
SendDiskChange(ULONG class)
|
|
{
|
|
struct IOStdReq * input_request = NULL;
|
|
struct MsgPort * input_port;
|
|
struct InputEvent ie;
|
|
|
|
ENTER();
|
|
|
|
input_port = CreateMsgPort();
|
|
if(input_port == NULL)
|
|
goto out;
|
|
|
|
input_request = (struct IOStdReq *)CreateIORequest(input_port,sizeof(*input_request));
|
|
if(input_request == NULL)
|
|
goto out;
|
|
|
|
if(OpenDevice("input.device",0,(struct IORequest *)input_request,0) != OK)
|
|
goto out;
|
|
|
|
memset(&ie,0,sizeof(ie));
|
|
|
|
ie.ie_Class = class;
|
|
ie.ie_Qualifier = IEQUALIFIER_MULTIBROADCAST;
|
|
|
|
GetSysTime(&ie.ie_TimeStamp);
|
|
|
|
input_request->io_Command = IND_WRITEEVENT;
|
|
input_request->io_Data = &ie;
|
|
input_request->io_Length = sizeof(ie);
|
|
|
|
DoIO((struct IORequest *)input_request);
|
|
|
|
out:
|
|
|
|
if(input_request != NULL)
|
|
{
|
|
if(input_request->io_Device != NULL)
|
|
CloseDevice((struct IORequest *)input_request);
|
|
|
|
DeleteIORequest((struct IORequest *)input_request);
|
|
}
|
|
|
|
DeleteMsgPort(input_port);
|
|
|
|
LEAVE();
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
/* Find the file node corresponding to a given name,
|
|
* skipping a particular entry if necessary.
|
|
*/
|
|
STATIC struct FileNode *
|
|
FindFileNode(STRPTR name,struct FileNode * skip)
|
|
{
|
|
struct FileNode * result = NULL;
|
|
struct FileNode * fn;
|
|
|
|
for(fn = (struct FileNode *)FileList.mlh_Head ;
|
|
fn->fn_MinNode.mln_Succ != NULL ;
|
|
fn = (struct FileNode *)fn->fn_MinNode.mln_Succ)
|
|
{
|
|
if(fn != skip && CompareNames(name,fn->fn_FullName) == SAME)
|
|
{
|
|
result = fn;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return(result);
|
|
}
|
|
|
|
/* Find the lock node corresponding to a given name,
|
|
* skipping a particular entry if necessary.
|
|
*/
|
|
STATIC struct LockNode *
|
|
FindLockNode(STRPTR name,struct LockNode * skip)
|
|
{
|
|
struct LockNode * result = NULL;
|
|
struct LockNode * ln;
|
|
|
|
for(ln = (struct LockNode *)LockList.mlh_Head ;
|
|
ln->ln_MinNode.mln_Succ != NULL ;
|
|
ln = (struct LockNode *)ln->ln_MinNode.mln_Succ)
|
|
{
|
|
if(ln != skip && CompareNames(name,ln->ln_FullName) == SAME)
|
|
{
|
|
result = ln;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return(result);
|
|
}
|
|
|
|
/* Check whether a new reference to be made to a named
|
|
* file will cause a conflict of access modes. No two
|
|
* files and locks may refer to the same object if
|
|
* either of these references is made in exclusive
|
|
* mode. This is the case which this function is
|
|
* trying to avoid.
|
|
*/
|
|
STATIC LONG
|
|
CheckAccessModeCollision(STRPTR name,LONG mode)
|
|
{
|
|
struct LockNode * ln;
|
|
struct FileNode * fn;
|
|
LONG error = OK;
|
|
|
|
ENTER();
|
|
SHOWSTRING(name);
|
|
|
|
fn = FindFileNode(name,NULL);
|
|
if(fn != NULL)
|
|
{
|
|
if(mode != SHARED_LOCK || fn->fn_Mode != SHARED_LOCK)
|
|
{
|
|
D(("collides with '%s'",fn->fn_FullName));
|
|
error = ERROR_OBJECT_IN_USE;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
ln = FindLockNode(name,NULL);
|
|
if(ln != NULL)
|
|
{
|
|
if(mode != SHARED_LOCK || ln->ln_FileLock.fl_Access != SHARED_LOCK)
|
|
{
|
|
D(("collides with '%s'",ln->ln_FullName));
|
|
error = ERROR_OBJECT_IN_USE;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
out:
|
|
|
|
RETURN(error);
|
|
return(error);
|
|
}
|
|
|
|
/* Find out whether there already exists a reference to a
|
|
* certain file or directory.
|
|
*/
|
|
STATIC LONG
|
|
NameAlreadyInUse(STRPTR name)
|
|
{
|
|
LONG error = OK;
|
|
|
|
ENTER();
|
|
|
|
if(FindFileNode(name,NULL) != NULL)
|
|
{
|
|
error = ERROR_OBJECT_IN_USE;
|
|
goto out;
|
|
}
|
|
|
|
if(FindLockNode(name,NULL) != NULL)
|
|
{
|
|
error = ERROR_OBJECT_IN_USE;
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
|
|
RETURN(error);
|
|
return(error);
|
|
}
|
|
|
|
/* Check whether an Amiga file name uses special characters which
|
|
* should be avoided when used with the SMB file sharing protocol.
|
|
*/
|
|
STATIC BOOL
|
|
IsReservedName(STRPTR name)
|
|
{
|
|
BOOL result = FALSE;
|
|
|
|
/* Disallow "." and "..". */
|
|
if(name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')))
|
|
{
|
|
result = TRUE;
|
|
}
|
|
else
|
|
{
|
|
UBYTE c;
|
|
|
|
/* Disallow the use of the backslash in file names. */
|
|
while((c = (*name++)) != '\0')
|
|
{
|
|
if(c == SMB_PATH_SEPARATOR)
|
|
{
|
|
result = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return(result);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
/* Convert a POSIX error code into an AmigaDOS error code. */
|
|
STATIC LONG
|
|
MapErrnoToIoErr(int error)
|
|
{
|
|
/* Not all of these mappings make good sense; bear in mind that
|
|
* POSIX covers more than a hundred different error codes
|
|
* whereas with AmigaDOS we're stranded with a measly 48...
|
|
*/
|
|
STATIC const LONG Map[][2] =
|
|
{
|
|
{ EPERM, ERROR_OBJECT_NOT_FOUND }, /* Operation not permitted */
|
|
{ ENOENT, ERROR_OBJECT_NOT_FOUND }, /* No such file or directory */
|
|
{ ESRCH, ERROR_OBJECT_NOT_FOUND }, /* No such process */
|
|
{ EINTR, ERROR_BREAK }, /* Interrupted system call */
|
|
{ EIO, ERROR_OBJECT_IN_USE }, /* Input/output error */
|
|
{ E2BIG, ERROR_TOO_MANY_ARGS }, /* Argument list too long */
|
|
{ EBADF, ERROR_INVALID_LOCK }, /* Bad file descriptor */
|
|
{ ENOMEM, ERROR_NO_FREE_STORE }, /* Cannot allocate memory */
|
|
{ EACCES, ERROR_OBJECT_NOT_FOUND}, /* Permission denied */
|
|
{ ENOTBLK, ERROR_OBJECT_WRONG_TYPE }, /* Block device required */
|
|
{ EBUSY, ERROR_OBJECT_IN_USE }, /* Device busy */
|
|
{ EEXIST, ERROR_OBJECT_EXISTS }, /* File exists */
|
|
{ EXDEV, ERROR_NOT_IMPLEMENTED }, /* Cross-device link */
|
|
{ ENOTDIR, ERROR_OBJECT_WRONG_TYPE }, /* Not a directory */
|
|
{ EISDIR, ERROR_OBJECT_WRONG_TYPE }, /* Is a directory */
|
|
{ EINVAL, ERROR_BAD_NUMBER }, /* Invalid argument */
|
|
{ EFBIG, ERROR_DISK_FULL }, /* File too large */
|
|
{ ENOSPC, ERROR_DISK_FULL }, /* No space left on device */
|
|
{ ESPIPE, ERROR_SEEK_ERROR }, /* Illegal seek */
|
|
{ EROFS, ERROR_WRITE_PROTECTED }, /* Read-only file system */
|
|
{ EMLINK, ERROR_TOO_MANY_LEVELS }, /* Too many links */
|
|
{ ENOTSOCK, ERROR_OBJECT_WRONG_TYPE }, /* Socket operation on non-socket */
|
|
{ EDESTADDRREQ, ERROR_REQUIRED_ARG_MISSING }, /* Destination address required */
|
|
{ EMSGSIZE, ERROR_LINE_TOO_LONG }, /* Message too long */
|
|
{ EPROTOTYPE, ERROR_BAD_TEMPLATE }, /* Protocol wrong type for socket */
|
|
{ ENOPROTOOPT, ERROR_NOT_IMPLEMENTED }, /* Protocol not available */
|
|
{ EPROTONOSUPPORT, ERROR_NOT_IMPLEMENTED }, /* Protocol not supported */
|
|
{ ESOCKTNOSUPPORT, ERROR_NOT_IMPLEMENTED }, /* Socket type not supported */
|
|
{ EOPNOTSUPP, ERROR_NOT_IMPLEMENTED }, /* Operation not supported */
|
|
{ EPFNOSUPPORT, ERROR_NOT_IMPLEMENTED }, /* Protocol family not supported */
|
|
{ EAFNOSUPPORT, ERROR_NOT_IMPLEMENTED }, /* Address family not supported by protocol family */
|
|
{ EADDRINUSE, ERROR_OBJECT_IN_USE }, /* Address already in use */
|
|
{ EADDRNOTAVAIL, ERROR_OBJECT_NOT_FOUND }, /* Can't assign requested address */
|
|
{ ENETDOWN, ERROR_OBJECT_NOT_FOUND }, /* Network is down */
|
|
{ ENETUNREACH, ERROR_OBJECT_NOT_FOUND }, /* Network is unreachable */
|
|
{ ENETRESET, ERROR_OBJECT_NOT_FOUND }, /* Network dropped connection on reset */
|
|
{ ECONNABORTED, ERROR_OBJECT_NOT_FOUND }, /* Software caused connection abort */
|
|
{ ECONNRESET, ERROR_OBJECT_NOT_FOUND }, /* Connection reset by peer */
|
|
{ ENOBUFS, ERROR_DISK_FULL }, /* No buffer space available */
|
|
{ EISCONN, ERROR_OBJECT_IN_USE }, /* Socket is already connected */
|
|
{ ENOTCONN, ERROR_OBJECT_WRONG_TYPE }, /* Socket is not connected */
|
|
{ ESHUTDOWN, ERROR_INVALID_LOCK }, /* Can't send after socket shutdown */
|
|
{ ECONNREFUSED, ERROR_OBJECT_IN_USE }, /* Connection refused */
|
|
{ ELOOP, ERROR_TOO_MANY_LEVELS }, /* Too many levels of symbolic links */
|
|
{ ENAMETOOLONG, ERROR_LINE_TOO_LONG }, /* File name too long */
|
|
{ EHOSTDOWN, ERROR_OBJECT_NOT_FOUND }, /* Host is down */
|
|
{ EHOSTUNREACH, ERROR_OBJECT_NOT_FOUND }, /* No route to host */
|
|
{ ENOTEMPTY, ERROR_DIRECTORY_NOT_EMPTY }, /* Directory not empty */
|
|
{ EPROCLIM, ERROR_TASK_TABLE_FULL }, /* Too many processes */
|
|
{ EUSERS, ERROR_TASK_TABLE_FULL }, /* Too many users */
|
|
{ EDQUOT, ERROR_DISK_FULL }, /* Disc quota exceeded */
|
|
{ ENOLCK, ERROR_NOT_IMPLEMENTED }, /* no locks available */
|
|
{ -1, -1 }
|
|
};
|
|
|
|
LONG result = ERROR_ACTION_NOT_KNOWN;
|
|
LONG i;
|
|
|
|
ENTER();
|
|
|
|
if(error < 0)
|
|
error = (-error);
|
|
|
|
for(i = 0 ; Map[i][0] != -1 ; i++)
|
|
{
|
|
if(Map[i][0] == error)
|
|
{
|
|
result = Map[i][1];
|
|
break;
|
|
}
|
|
}
|
|
|
|
RETURN(result);
|
|
return(result);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
/* Translate a BCPL style file name (i.e. length is in the first byte)
|
|
* via a mapping table.
|
|
*/
|
|
INLINE STATIC VOID
|
|
TranslateBName(UBYTE * name,UBYTE * map)
|
|
{
|
|
LONG len;
|
|
UBYTE c;
|
|
|
|
len = (*name++);
|
|
|
|
while(len-- > 0)
|
|
{
|
|
c = (*name);
|
|
|
|
(*name++) = map[c];
|
|
}
|
|
}
|
|
|
|
/* Translate a NUL terminated file name via a mapping table. */
|
|
INLINE STATIC VOID
|
|
TranslateCName(UBYTE * name,UBYTE * map)
|
|
{
|
|
UBYTE c;
|
|
|
|
while((c = (*name)) != '\0')
|
|
(*name++) = map[c];
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
/* Remove a DosList entry using the proper protocols. Note that
|
|
* this function can fail!
|
|
*/
|
|
STATIC BOOL
|
|
ReallyRemoveDosEntry(struct DosList * entry)
|
|
{
|
|
struct Message * mn;
|
|
struct MsgPort * port;
|
|
struct DosList * dl;
|
|
BOOL result = FALSE;
|
|
LONG kind,i;
|
|
|
|
if(entry->dol_Type == DLT_DEVICE)
|
|
kind = LDF_DEVICES;
|
|
else
|
|
kind = LDF_VOLUMES;
|
|
|
|
port = entry->dol_Task;
|
|
|
|
for(i = 0 ; i < 100 ; i++)
|
|
{
|
|
dl = AttemptLockDosList(LDF_WRITE|kind);
|
|
if(((ULONG)dl) <= 1)
|
|
dl = NULL;
|
|
|
|
if(dl != NULL)
|
|
{
|
|
RemDosEntry(entry);
|
|
|
|
UnLockDosList(LDF_WRITE|kind);
|
|
|
|
result = TRUE;
|
|
|
|
break;
|
|
}
|
|
|
|
while((mn = GetMsg(port)) != NULL)
|
|
ReplyPkt((struct DosPacket *)mn->mn_Node.ln_Name,DOSFALSE,ERROR_ACTION_NOT_KNOWN);
|
|
|
|
Delay(TICKS_PER_SECOND / 10);
|
|
}
|
|
|
|
return(result);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
/* Release all resources allocated by the Setup() routine. */
|
|
STATIC VOID
|
|
Cleanup(VOID)
|
|
{
|
|
BOOL send_disk_change = FALSE;
|
|
|
|
ENTER();
|
|
|
|
/* If any errors have cropped up, display them now before
|
|
* call it quits.
|
|
*/
|
|
DisplayErrorList();
|
|
|
|
if(NewProgramName != NULL)
|
|
{
|
|
FreeVec(NewProgramName);
|
|
NewProgramName = NULL;
|
|
}
|
|
|
|
if(Parameters != NULL)
|
|
{
|
|
FreeArgs(Parameters);
|
|
Parameters = NULL;
|
|
}
|
|
|
|
if(Icon != NULL)
|
|
{
|
|
FreeDiskObject(Icon);
|
|
Icon = NULL;
|
|
}
|
|
|
|
if(ServerData != NULL)
|
|
{
|
|
smba_disconnect(ServerData);
|
|
ServerData = NULL;
|
|
}
|
|
|
|
if(DeviceNode != NULL)
|
|
{
|
|
if(DeviceNodeAdded)
|
|
{
|
|
if(ReallyRemoveDosEntry(DeviceNode))
|
|
FreeDosEntry(DeviceNode);
|
|
}
|
|
else
|
|
{
|
|
FreeDosEntry(DeviceNode);
|
|
}
|
|
|
|
DeviceNode = NULL;
|
|
}
|
|
|
|
if(VolumeNode != NULL)
|
|
{
|
|
if(VolumeNodeAdded)
|
|
{
|
|
if(ReallyRemoveDosEntry(VolumeNode))
|
|
FreeDosEntry(VolumeNode);
|
|
|
|
send_disk_change = TRUE;
|
|
}
|
|
else
|
|
{
|
|
FreeDosEntry(VolumeNode);
|
|
}
|
|
|
|
VolumeNode = NULL;
|
|
}
|
|
|
|
if(FileSystemPort != NULL)
|
|
{
|
|
struct Message * mn;
|
|
|
|
/* Return all queued packets; there should be none, though. */
|
|
while((mn = GetMsg(FileSystemPort)) != NULL)
|
|
ReplyPkt((struct DosPacket *)mn->mn_Node.ln_Name,DOSFALSE,ERROR_ACTION_NOT_KNOWN);
|
|
|
|
DeleteMsgPort(FileSystemPort);
|
|
FileSystemPort = NULL;
|
|
}
|
|
|
|
if(WBStartup == NULL && send_disk_change)
|
|
SendDiskChange(IECLASS_DISKREMOVED);
|
|
|
|
#if defined(__amigaos4__)
|
|
{
|
|
if(ITimer != NULL)
|
|
{
|
|
DropInterface((struct Interface *)ITimer);
|
|
ITimer = NULL;
|
|
}
|
|
}
|
|
#endif /* __amigaos4__ */
|
|
|
|
if(TimerBase != NULL)
|
|
{
|
|
CloseDevice((struct IORequest *)&TimerRequest);
|
|
TimerBase = NULL;
|
|
}
|
|
|
|
#if defined(__amigaos4__)
|
|
{
|
|
if(ISocket != NULL)
|
|
{
|
|
DropInterface((struct Interface *)ISocket);
|
|
ISocket = NULL;
|
|
}
|
|
}
|
|
#endif /* __amigaos4__ */
|
|
|
|
if(SocketBase != NULL)
|
|
{
|
|
CloseLibrary(SocketBase);
|
|
SocketBase = NULL;
|
|
}
|
|
|
|
#if defined(__amigaos4__)
|
|
{
|
|
if(IIcon != NULL)
|
|
{
|
|
DropInterface((struct Interface *)IIcon);
|
|
IIcon = NULL;
|
|
}
|
|
}
|
|
#endif /* __amigaos4__ */
|
|
|
|
if(IconBase != NULL)
|
|
{
|
|
CloseLibrary(IconBase);
|
|
IconBase = NULL;
|
|
}
|
|
|
|
if(Locale != NULL)
|
|
{
|
|
CloseLocale(Locale);
|
|
Locale = NULL;
|
|
}
|
|
|
|
#if defined(__amigaos4__)
|
|
{
|
|
if(ILocale != NULL)
|
|
{
|
|
DropInterface((struct Interface *)ILocale);
|
|
ILocale = NULL;
|
|
}
|
|
}
|
|
#endif /* __amigaos4__ */
|
|
|
|
if(LocaleBase != NULL)
|
|
{
|
|
CloseLibrary(LocaleBase);
|
|
LocaleBase = NULL;
|
|
}
|
|
|
|
if(MemoryPool != NULL)
|
|
{
|
|
DeletePool(MemoryPool);
|
|
MemoryPool = NULL;
|
|
}
|
|
|
|
LEAVE();
|
|
}
|
|
|
|
/* Allocate all the necessary resources to get going. */
|
|
STATIC BOOL
|
|
Setup(
|
|
STRPTR program_name,
|
|
STRPTR service,
|
|
STRPTR workgroup,
|
|
STRPTR username,
|
|
STRPTR opt_password,
|
|
BOOL opt_changecase,
|
|
STRPTR opt_clientname,
|
|
STRPTR opt_servername,
|
|
int opt_cachesize,
|
|
int opt_max_transmit,
|
|
LONG * opt_time_zone_offset,
|
|
LONG * opt_dst_offset,
|
|
BOOL opt_raw_smb,
|
|
STRPTR device_name,
|
|
STRPTR volume_name,
|
|
STRPTR translation_file)
|
|
{
|
|
BOOL result = FALSE;
|
|
struct DosList * dl;
|
|
int error;
|
|
STRPTR actual_volume_name;
|
|
LONG actual_volume_name_len;
|
|
UBYTE name[MAX_FILENAME_LEN];
|
|
BOOL device_exists = FALSE;
|
|
LONG len,i;
|
|
|
|
ENTER();
|
|
|
|
NewList((struct List *)&FileList);
|
|
NewList((struct List *)&LockList);
|
|
|
|
MemoryPool = CreatePool(MEMF_ANY|MEMF_PUBLIC,4096,4096);
|
|
if(MemoryPool == NULL)
|
|
{
|
|
ReportError("Could not create memory pool.");
|
|
goto out;
|
|
}
|
|
|
|
LocaleBase = OpenLibrary("locale.library",38);
|
|
|
|
#if defined(__amigaos4__)
|
|
{
|
|
if(LocaleBase != NULL)
|
|
{
|
|
ILocale = (struct LocaleIFace *)GetInterface(LocaleBase, "main", 1, 0);
|
|
if(ILocale == NULL)
|
|
{
|
|
CloseLibrary(LocaleBase);
|
|
LocaleBase = NULL;
|
|
}
|
|
}
|
|
}
|
|
#endif /* __amigaos4__ */
|
|
|
|
if(LocaleBase != NULL)
|
|
Locale = OpenLocale(NULL);
|
|
|
|
if(opt_time_zone_offset != NULL)
|
|
{
|
|
TimeZoneOffset = (*opt_time_zone_offset);
|
|
OverrideLocaleTimeZone = TRUE;
|
|
}
|
|
|
|
if(opt_dst_offset != NULL)
|
|
DSTOffset = -60 * (*opt_dst_offset);
|
|
|
|
memset(&TimerRequest,0,sizeof(TimerRequest));
|
|
|
|
if(OpenDevice(TIMERNAME,UNIT_VBLANK,(struct IORequest *)&TimerRequest,0) != OK)
|
|
{
|
|
ReportError("Could not open 'timer.device'.");
|
|
goto out;
|
|
}
|
|
|
|
TimerBase = (struct Library *)TimerRequest.tr_node.io_Device;
|
|
|
|
#if defined(__amigaos4__)
|
|
{
|
|
if(TimerBase != NULL)
|
|
{
|
|
ITimer = (struct TimerIFace *)GetInterface(TimerBase, "main", 1, 0);
|
|
if(ITimer == NULL)
|
|
{
|
|
ReportError("Could not open 'timer.device'.");
|
|
goto out;
|
|
}
|
|
}
|
|
}
|
|
#endif /* __amigaos4__ */
|
|
|
|
SocketBase = OpenLibrary("bsdsocket.library",3);
|
|
|
|
#if defined(__amigaos4__)
|
|
{
|
|
if(SocketBase != NULL)
|
|
{
|
|
ISocket = (struct SocketIFace *)GetInterface(SocketBase, "main", 1, 0);
|
|
if(ISocket == NULL)
|
|
{
|
|
CloseLibrary(SocketBase);
|
|
SocketBase = NULL;
|
|
}
|
|
}
|
|
}
|
|
#endif /* __amigaos4__ */
|
|
|
|
if(SocketBase == NULL)
|
|
{
|
|
ReportError("Could not open 'bsdsocket.library' V3; TCP/IP stack not running?");
|
|
goto out;
|
|
}
|
|
|
|
error = SocketBaseTags(
|
|
SBTM_SETVAL(SBTC_ERRNOPTR(sizeof(errno))), &errno,
|
|
SBTM_SETVAL(SBTC_HERRNOLONGPTR), &h_errno,
|
|
SBTM_SETVAL(SBTC_LOGTAGPTR), program_name,
|
|
SBTM_SETVAL(SBTC_BREAKMASK), SIGBREAKF_CTRL_C,
|
|
TAG_END);
|
|
if(error != OK)
|
|
{
|
|
ReportError("Could not initialize 'bsdsocket.library' (%ld, %s).",error,amitcp_strerror(error));
|
|
goto out;
|
|
}
|
|
|
|
if(opt_changecase)
|
|
{
|
|
for(i = 0 ; i < (LONG)strlen(opt_password) ; i++)
|
|
opt_password[i] = ToUpper(opt_password[i]);
|
|
}
|
|
|
|
TranslateNames = FALSE;
|
|
|
|
/* Read the translation file, if possible. */
|
|
if(translation_file != NULL)
|
|
{
|
|
STRPTR msg = NULL;
|
|
BPTR file;
|
|
|
|
error = OK;
|
|
|
|
file = Open(translation_file,MODE_OLDFILE);
|
|
if(file != ZERO)
|
|
{
|
|
if(Read(file,A2M,256) != 256 ||
|
|
Read(file,M2A,256) != 256)
|
|
{
|
|
msg = "Could not read translation file";
|
|
error = IoErr();
|
|
}
|
|
|
|
Close(file);
|
|
}
|
|
else
|
|
{
|
|
msg = "Could not open translation file";
|
|
error = IoErr();
|
|
}
|
|
|
|
if(msg == NULL)
|
|
{
|
|
TranslateNames = TRUE;
|
|
}
|
|
else
|
|
{
|
|
UBYTE description[100];
|
|
|
|
Fault(error,NULL,description,sizeof(description));
|
|
for(i = ((int)strlen(description)) - 1 ; i >= 0 ; i--)
|
|
{
|
|
if(description[i] == '\n')
|
|
description[i] = '\0';
|
|
}
|
|
|
|
ReportError("%s '%s' (%ld, %s).",msg,translation_file,error,description);
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
error = smba_start(service,workgroup,username,opt_password,opt_clientname,opt_servername,opt_cachesize,opt_max_transmit,opt_raw_smb,&ServerData);
|
|
if(error < 0)
|
|
goto out;
|
|
|
|
FileSystemPort = CreateMsgPort();
|
|
if(FileSystemPort == NULL)
|
|
{
|
|
ReportError("Could not create filesystem port.");
|
|
goto out;
|
|
}
|
|
|
|
/* If a device name was provided, check whether it is
|
|
* well-formed.
|
|
*/
|
|
if(device_name != NULL)
|
|
{
|
|
len = strlen(device_name);
|
|
if(len > 255)
|
|
len = 255;
|
|
|
|
for(i = 0 ; i < len ; i++)
|
|
{
|
|
if(device_name[i] == '/')
|
|
{
|
|
ReportError("Device name '%s' cannot be used with AmigaDOS.",device_name);
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
/* Lose any trailing colon characters. */
|
|
for(i = len-1 ; i >= 0 ; i--)
|
|
{
|
|
if(device_name[i] == ':')
|
|
len = i;
|
|
}
|
|
|
|
if(len == 0)
|
|
{
|
|
ReportError("Device name '%s' cannot be used with AmigaDOS.",device_name);
|
|
goto out;
|
|
}
|
|
|
|
memcpy(name,device_name,len);
|
|
name[len] = '\0';
|
|
|
|
dl = LockDosList(LDF_WRITE|LDF_VOLUMES|LDF_DEVICES);
|
|
|
|
if(FindDosEntry(dl,name,LDF_DEVICES) != NULL)
|
|
device_exists = TRUE;
|
|
}
|
|
else
|
|
{
|
|
dl = LockDosList(LDF_WRITE|LDF_VOLUMES|LDF_DEVICES);
|
|
|
|
/* Find a unique device name. */
|
|
for(i = 0 ; i < 100 ; i++)
|
|
{
|
|
SPrintf(name,"SMBFS%ld",i);
|
|
|
|
device_exists = (BOOL)(FindDosEntry(dl,name,LDF_DEVICES) != NULL);
|
|
if(NOT device_exists)
|
|
{
|
|
device_name = name;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(device_exists)
|
|
{
|
|
UnLockDosList(LDF_WRITE|LDF_VOLUMES|LDF_DEVICES);
|
|
|
|
ReportError("Device name '%s:' is already taken.",device_name);
|
|
goto out;
|
|
}
|
|
|
|
/* Finally, create the device node. */
|
|
DeviceNode = MakeDosEntry(name,DLT_DEVICE);
|
|
if(DeviceNode == NULL)
|
|
{
|
|
UnLockDosList(LDF_WRITE|LDF_VOLUMES|LDF_DEVICES);
|
|
|
|
ReportError("Could not create device node.");
|
|
goto out;
|
|
}
|
|
|
|
DeviceNode->dol_Task = FileSystemPort;
|
|
|
|
/* Examine the volume name; make sure that it is
|
|
* well-formed.
|
|
*/
|
|
if(volume_name == NULL)
|
|
actual_volume_name = device_name;
|
|
else
|
|
actual_volume_name = volume_name;
|
|
|
|
actual_volume_name_len = strlen(actual_volume_name);
|
|
if(actual_volume_name_len > 255)
|
|
actual_volume_name_len = 255;
|
|
|
|
for(i = 0 ; i < actual_volume_name_len ; i++)
|
|
{
|
|
if(actual_volume_name[i] == '/')
|
|
{
|
|
UnLockDosList(LDF_WRITE|LDF_VOLUMES|LDF_DEVICES);
|
|
|
|
ReportError("Volume name '%s' cannot be used with AmigaDOS.",actual_volume_name);
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
/* Lose any trailing colon characters. */
|
|
for(i = actual_volume_name_len-1 ; i >= 0 ; i--)
|
|
{
|
|
if(actual_volume_name[i] == ':')
|
|
actual_volume_name_len = i;
|
|
}
|
|
|
|
if(actual_volume_name_len == 0)
|
|
{
|
|
UnLockDosList(LDF_WRITE|LDF_VOLUMES|LDF_DEVICES);
|
|
|
|
ReportError("Volume name '%s' cannot be used with AmigaDOS.",actual_volume_name);
|
|
goto out;
|
|
}
|
|
|
|
/* Now, finally, take care of the volume name. */
|
|
memcpy(name,actual_volume_name,actual_volume_name_len);
|
|
name[actual_volume_name_len] = '\0';
|
|
|
|
VolumeNode = MakeDosEntry(name,DLT_VOLUME);
|
|
if(VolumeNode == NULL)
|
|
{
|
|
UnLockDosList(LDF_WRITE|LDF_VOLUMES|LDF_DEVICES);
|
|
|
|
ReportError("Could not create volume node.");
|
|
goto out;
|
|
}
|
|
|
|
VolumeNode->dol_Task = FileSystemPort;
|
|
DateStamp(&VolumeNode->dol_misc.dol_volume.dol_VolumeDate);
|
|
VolumeNode->dol_misc.dol_volume.dol_DiskType = ID_DOS_DISK;
|
|
|
|
if(DeviceNode != NULL)
|
|
{
|
|
AddDosEntry(DeviceNode);
|
|
|
|
DeviceNodeAdded = TRUE;
|
|
}
|
|
|
|
/* Note: we always need the volume node to make some file
|
|
* system operations safe (e.g. Lock()), but we may
|
|
* not always need to make it visible.
|
|
*/
|
|
if(volume_name != NULL && VolumeNode != NULL)
|
|
{
|
|
AddDosEntry(VolumeNode);
|
|
|
|
VolumeNodeAdded = TRUE;
|
|
}
|
|
|
|
/* And that concludes the mounting operation. */
|
|
UnLockDosList(LDF_WRITE|LDF_VOLUMES|LDF_DEVICES);
|
|
|
|
/* Tell Workbench and friends to update their volume lists. */
|
|
if(VolumeNodeAdded)
|
|
SendDiskChange(IECLASS_DISKINSERTED);
|
|
|
|
SetProgramName(NewProgramName);
|
|
|
|
result = TRUE;
|
|
|
|
out:
|
|
|
|
RETURN(result);
|
|
return(result);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
/* Convert a BCPL string into a standard NUL terminated 'C' string. */
|
|
INLINE STATIC VOID
|
|
ConvertBString(LONG max_len,STRPTR cstring,APTR bstring)
|
|
{
|
|
STRPTR from = bstring;
|
|
LONG len = from[0];
|
|
|
|
if(len > max_len-1)
|
|
len = max_len-1;
|
|
|
|
if(len > 0)
|
|
memcpy(cstring,from+1,len);
|
|
|
|
cstring[len] = '\0';
|
|
}
|
|
|
|
/* Convert a NUL terminated 'C' string into a BCPL string. */
|
|
INLINE STATIC VOID
|
|
ConvertCString(APTR bstring,LONG max_len,STRPTR cstring,LONG len)
|
|
{
|
|
STRPTR to = bstring;
|
|
|
|
if(len > max_len-1)
|
|
len = max_len-1;
|
|
|
|
(*to++) = len;
|
|
memcpy(to,cstring,len);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
/* Build the fully qualified name of a file or directory in reference
|
|
* to the name of the parent directory. This takes care of all the
|
|
* special cases, such as the root directory. The result will be converted
|
|
* to be in a form suitable for use with the SMB file sharing service.
|
|
*/
|
|
STATIC LONG
|
|
BuildFullName(
|
|
STRPTR parent_name,
|
|
STRPTR name,
|
|
STRPTR * result_ptr,
|
|
LONG * result_size_ptr)
|
|
{
|
|
LONG error = OK;
|
|
STRPTR buffer;
|
|
LONG len,size;
|
|
LONG i;
|
|
|
|
ENTER();
|
|
|
|
SHOWSTRING(parent_name);
|
|
SHOWSTRING(name);
|
|
|
|
(*result_ptr) = NULL;
|
|
|
|
/* Throw everything left of the colon away. */
|
|
if(name != NULL)
|
|
{
|
|
for(i = 0 ; i < (LONG)strlen(name) ; i++)
|
|
{
|
|
if(name[i] == ':')
|
|
{
|
|
name = &name[i+1];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Now, how much room is needed for the complete
|
|
* path to fit into a buffer?
|
|
*/
|
|
len = 2;
|
|
|
|
if(parent_name != NULL)
|
|
len += strlen(parent_name) + 1;
|
|
|
|
if(name != NULL)
|
|
len += strlen(name) + 1;
|
|
|
|
if(len < SMB_MAXNAMELEN)
|
|
len = SMB_MAXNAMELEN;
|
|
|
|
size = len + 3;
|
|
|
|
buffer = AllocateMemory(size);
|
|
if(buffer == NULL)
|
|
{
|
|
error = ERROR_NO_FREE_STORE;
|
|
goto out;
|
|
}
|
|
|
|
/* Start by filling in the path name. */
|
|
if(parent_name != NULL)
|
|
{
|
|
/* Skip any excess separators. */
|
|
while((*parent_name) == SMB_PATH_SEPARATOR)
|
|
parent_name++;
|
|
|
|
buffer[0] = SMB_PATH_SEPARATOR;
|
|
strcpy(&buffer[1],parent_name);
|
|
}
|
|
else
|
|
{
|
|
strcpy(buffer,SMB_ROOT_DIR_NAME);
|
|
}
|
|
|
|
/* If there's a name to add, do just that. */
|
|
if(name != NULL)
|
|
{
|
|
LONG segment_start;
|
|
LONG segment_len;
|
|
LONG buffer_len;
|
|
LONG name_len;
|
|
|
|
buffer_len = strlen(buffer);
|
|
name_len = strlen(name);
|
|
|
|
segment_start = 0;
|
|
|
|
while(TRUE)
|
|
{
|
|
segment_len = 0;
|
|
|
|
/* Extract the next path name segment. */
|
|
for(i = segment_start ; i <= name_len ; i++)
|
|
{
|
|
if(i == name_len)
|
|
{
|
|
segment_len = i - segment_start;
|
|
break;
|
|
}
|
|
else if (name[i] == '/')
|
|
{
|
|
segment_len = i - segment_start + 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* We're finished if there are no further
|
|
* path name segments to take care of.
|
|
*/
|
|
if(segment_len == 0)
|
|
{
|
|
buffer[buffer_len] = '\0';
|
|
break;
|
|
}
|
|
|
|
/* A single slash indicates that we need to move up
|
|
* to the parent directory, if any.
|
|
*/
|
|
if(segment_len == 1 && name[segment_start] == '/')
|
|
{
|
|
/* Is this already the root directory name? */
|
|
if(buffer_len <= 1)
|
|
{
|
|
FreeMemory(buffer);
|
|
buffer = NULL;
|
|
|
|
goto out;
|
|
}
|
|
else
|
|
{
|
|
/* Skip the last path component. */
|
|
for(i = 1 ; i <= buffer_len ; i++)
|
|
{
|
|
if(i == buffer_len)
|
|
{
|
|
/* We just skipped the first path
|
|
* component following the root
|
|
* directory name. We preserve
|
|
* the first character since it
|
|
* refers to the root directory.
|
|
*/
|
|
buffer_len = 1;
|
|
break;
|
|
}
|
|
else if (buffer[buffer_len-i] == SMB_PATH_SEPARATOR)
|
|
{
|
|
/* This removes both the path separator and
|
|
* the name following it.
|
|
*/
|
|
buffer_len -= i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Add a proper separator character if
|
|
* necessary.
|
|
*/
|
|
if(buffer_len > 0 && buffer[buffer_len-1] != SMB_PATH_SEPARATOR)
|
|
buffer[buffer_len++] = SMB_PATH_SEPARATOR;
|
|
|
|
/* Find out how many characters are in that name; this
|
|
* excludes the terminating slash.
|
|
*/
|
|
if(name[segment_start + segment_len - 1] == '/')
|
|
len = segment_len - 1;
|
|
else
|
|
len = segment_len;
|
|
|
|
memcpy(&buffer[buffer_len],&name[segment_start],len);
|
|
buffer_len += len;
|
|
}
|
|
|
|
segment_start += segment_len;
|
|
}
|
|
}
|
|
|
|
(*result_ptr) = buffer;
|
|
(*result_size_ptr) = size;
|
|
|
|
SHOWSTRING(buffer);
|
|
|
|
out:
|
|
|
|
if(error != OK)
|
|
FreeMemory(buffer);
|
|
|
|
RETURN(error);
|
|
return(error);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
STATIC BPTR
|
|
Action_Parent(
|
|
struct FileLock * parent,
|
|
LONG * error_ptr)
|
|
{
|
|
BPTR result = ZERO;
|
|
STRPTR full_name = NULL;
|
|
LONG full_name_size;
|
|
STRPTR parent_name;
|
|
BOOL cleanup = TRUE;
|
|
struct LockNode * ln = NULL;
|
|
LONG error;
|
|
|
|
ENTER();
|
|
|
|
SHOWVALUE(parent);
|
|
|
|
if(parent != NULL)
|
|
{
|
|
struct LockNode * parent_ln = (struct LockNode *)parent->fl_Key;
|
|
|
|
parent_name = parent_ln->ln_FullName;
|
|
}
|
|
else
|
|
{
|
|
parent_name = NULL;
|
|
}
|
|
|
|
error = BuildFullName(parent_name,"/",&full_name,&full_name_size);
|
|
if(error != OK)
|
|
goto out;
|
|
|
|
/* Check if we ended up having to return the parent of
|
|
* the root directory. This is indicated by a NULL
|
|
* name pointer and a zero error code.
|
|
*/
|
|
if(full_name == NULL)
|
|
goto out;
|
|
|
|
ln = AllocateMemory(sizeof(*ln));
|
|
if(ln == NULL)
|
|
{
|
|
error = ERROR_NO_FREE_STORE;
|
|
goto out;
|
|
}
|
|
|
|
memset(ln,0,sizeof(*ln));
|
|
|
|
ln->ln_FileLock.fl_Key = (LONG)ln;
|
|
ln->ln_FileLock.fl_Access = SHARED_LOCK;
|
|
ln->ln_FileLock.fl_Task = FileSystemPort;
|
|
ln->ln_FileLock.fl_Volume = MKBADDR(VolumeNode);
|
|
ln->ln_FullName = full_name;
|
|
|
|
SHOWSTRING(full_name);
|
|
|
|
error = smba_open(ServerData,full_name,full_name_size,&ln->ln_File);
|
|
if(error < 0)
|
|
{
|
|
error = MapErrnoToIoErr(error);
|
|
goto out;
|
|
}
|
|
|
|
AddTail((struct List *)&LockList,(struct Node *)ln);
|
|
result = MKBADDR(&ln->ln_FileLock);
|
|
cleanup = FALSE;
|
|
SHOWVALUE(&ln->ln_FileLock);
|
|
|
|
out:
|
|
|
|
if(cleanup)
|
|
{
|
|
FreeMemory(full_name);
|
|
FreeMemory(ln);
|
|
}
|
|
|
|
(*error_ptr) = error;
|
|
|
|
RETURN(result);
|
|
return(result);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
/* Find the lock node corresponding to a given name,
|
|
* starting from node start. (if node, this one is skipped)
|
|
*/
|
|
STATIC struct LockNode *
|
|
FindNextLockNode(STRPTR name,struct LockNode * last_ln)
|
|
{
|
|
struct LockNode * result = NULL;
|
|
struct LockNode * ln;
|
|
struct LockNode * start;
|
|
|
|
if(last_ln != NULL)
|
|
start = (struct LockNode *)last_ln->ln_MinNode.mln_Succ;
|
|
else
|
|
start = (struct LockNode *)LockList.mlh_Head;
|
|
|
|
for(ln = start ;
|
|
ln->ln_MinNode.mln_Succ != NULL ;
|
|
ln = (struct LockNode *)ln->ln_MinNode.mln_Succ)
|
|
{
|
|
if(CompareNames(name,ln->ln_FullName) == SAME)
|
|
{
|
|
result = ln;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return(result);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
STATIC LONG
|
|
Action_DeleteObject(
|
|
struct FileLock * parent,
|
|
APTR bcpl_name,
|
|
LONG * error_ptr)
|
|
{
|
|
LONG result = DOSFALSE;
|
|
STRPTR full_name = NULL;
|
|
LONG full_name_size;
|
|
smba_file_t * file = NULL;
|
|
STRPTR parent_name;
|
|
STRPTR full_parent_name = NULL;
|
|
UBYTE name[MAX_FILENAME_LEN];
|
|
struct LockNode * ln;
|
|
smba_stat_t st;
|
|
LONG error;
|
|
|
|
ENTER();
|
|
|
|
if(WriteProtected)
|
|
{
|
|
error = ERROR_DISK_WRITE_PROTECTED;
|
|
goto out;
|
|
}
|
|
|
|
SHOWVALUE(parent);
|
|
|
|
if(parent != NULL)
|
|
{
|
|
ln = (struct LockNode *)parent->fl_Key;
|
|
|
|
parent_name = ln->ln_FullName;
|
|
}
|
|
else
|
|
{
|
|
parent_name = NULL;
|
|
}
|
|
|
|
ConvertBString(sizeof(name),name,bcpl_name);
|
|
|
|
if (TranslateUTF8)
|
|
{
|
|
UBYTE encoded_name[MAX_FILENAME_LEN];
|
|
int encoded_name_len;
|
|
LONG name_len = strlen(name);
|
|
|
|
encoded_name_len = encode_iso8859_1_as_utf8_string(name,name_len,NULL,0);
|
|
if(encoded_name_len < 0 || encoded_name_len >= sizeof(name))
|
|
{
|
|
error = ERROR_INVALID_COMPONENT_NAME;
|
|
goto out;
|
|
}
|
|
|
|
encode_iso8859_1_as_utf8_string(name,name_len,encoded_name,sizeof(encoded_name));
|
|
|
|
strlcpy(name,encoded_name,sizeof(name));
|
|
}
|
|
else if (TranslateNames)
|
|
{
|
|
TranslateCName(name,A2M);
|
|
}
|
|
|
|
error = BuildFullName(parent_name,name,&full_name,&full_name_size);
|
|
if(error != OK)
|
|
goto out;
|
|
|
|
/* Trying to delete the root directory, are you kidding? */
|
|
if(full_name == NULL)
|
|
{
|
|
error = ERROR_OBJECT_WRONG_TYPE;
|
|
goto out;
|
|
}
|
|
|
|
/* We need to find this file's parent directory, so that
|
|
* in case the directory contents are currently being
|
|
* examined, that process is restarted.
|
|
*/
|
|
full_parent_name = AllocateMemory(strlen(full_name)+3);
|
|
if(full_parent_name == NULL)
|
|
{
|
|
error = ERROR_NO_FREE_STORE;
|
|
goto out;
|
|
}
|
|
|
|
strcpy(full_parent_name,full_name);
|
|
|
|
/* Build the parent object name - Piru */
|
|
if (full_parent_name[0] != '\0')
|
|
{
|
|
int i;
|
|
|
|
i = strlen(full_parent_name) - 1;
|
|
if (full_parent_name[i] == SMB_PATH_SEPARATOR)
|
|
i--;
|
|
|
|
for ( ; i >= 0 ; i--)
|
|
{
|
|
if (full_parent_name[i] == SMB_PATH_SEPARATOR)
|
|
{
|
|
full_parent_name[i] = '\0';
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* NOTE: Mark all locks to this object as restart, not just first
|
|
one - Piru */
|
|
ln = NULL;
|
|
while ((ln = FindNextLockNode(full_parent_name, ln)) != NULL)
|
|
ln->ln_RestartExamine = TRUE;
|
|
|
|
ln = FindLockNode(full_parent_name,NULL);
|
|
if(ln != NULL)
|
|
ln->ln_RestartExamine = TRUE;
|
|
|
|
FreeMemory(full_parent_name);
|
|
full_parent_name = NULL;
|
|
|
|
SHOWSTRING(full_name);
|
|
|
|
error = smba_open(ServerData,full_name,full_name_size,&file);
|
|
if(error < 0)
|
|
{
|
|
error = MapErrnoToIoErr(error);
|
|
goto out;
|
|
}
|
|
|
|
error = smba_getattr(file,&st);
|
|
if(error < 0)
|
|
{
|
|
error = MapErrnoToIoErr(error);
|
|
goto out;
|
|
}
|
|
|
|
smba_close(file);
|
|
file = NULL;
|
|
|
|
if(st.is_dir)
|
|
{
|
|
SHOWMSG("removing a directory");
|
|
|
|
error = smba_rmdir(ServerData,full_name);
|
|
if(error < 0)
|
|
{
|
|
SHOWVALUE(error);
|
|
|
|
/* This is a little bit difficult to justify since
|
|
* the error code may indicate a different cause,
|
|
* but in practice 'EACCES' seems to be returned
|
|
* if the directory to remove is not empty.
|
|
*/
|
|
if(error == (-EACCES))
|
|
error = ERROR_DIRECTORY_NOT_EMPTY;
|
|
else
|
|
error = MapErrnoToIoErr(error);
|
|
|
|
goto out;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SHOWMSG("removing a file");
|
|
|
|
error = smba_remove(ServerData,full_name);
|
|
if(error < 0)
|
|
{
|
|
SHOWVALUE(error);
|
|
|
|
error = MapErrnoToIoErr(error);
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
SHOWMSG("done.");
|
|
|
|
result = DOSTRUE;
|
|
|
|
out:
|
|
|
|
FreeMemory(full_name);
|
|
FreeMemory(full_parent_name);
|
|
|
|
if(file != NULL)
|
|
smba_close(file);
|
|
|
|
(*error_ptr) = error;
|
|
|
|
RETURN(result);
|
|
return(result);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
STATIC BPTR
|
|
Action_CreateDir(
|
|
struct FileLock * parent,
|
|
APTR bcpl_name,
|
|
LONG * error_ptr)
|
|
{
|
|
BPTR result = ZERO;
|
|
STRPTR full_name = NULL;
|
|
LONG full_name_size;
|
|
struct LockNode * ln = NULL;
|
|
STRPTR parent_name;
|
|
STRPTR dir_name = NULL;
|
|
smba_file_t * dir = NULL;
|
|
STRPTR base_name;
|
|
UBYTE name[MAX_FILENAME_LEN];
|
|
LONG error;
|
|
LONG i;
|
|
|
|
ENTER();
|
|
|
|
if(WriteProtected)
|
|
{
|
|
error = ERROR_DISK_WRITE_PROTECTED;
|
|
goto out;
|
|
}
|
|
|
|
SHOWVALUE(parent);
|
|
|
|
if(parent != NULL)
|
|
{
|
|
struct LockNode * parent_ln = (struct LockNode *)parent->fl_Key;
|
|
|
|
parent_name = parent_ln->ln_FullName;
|
|
}
|
|
else
|
|
{
|
|
parent_name = NULL;
|
|
}
|
|
|
|
ConvertBString(sizeof(name),name,bcpl_name);
|
|
|
|
if (TranslateUTF8)
|
|
{
|
|
UBYTE encoded_name[MAX_FILENAME_LEN];
|
|
int encoded_name_len;
|
|
LONG name_len = strlen(name);
|
|
|
|
encoded_name_len = encode_iso8859_1_as_utf8_string(name,name_len,NULL,0);
|
|
if(encoded_name_len < 0 || encoded_name_len >= sizeof(name))
|
|
{
|
|
error = ERROR_INVALID_COMPONENT_NAME;
|
|
goto out;
|
|
}
|
|
|
|
encode_iso8859_1_as_utf8_string(name,name_len,encoded_name,sizeof(encoded_name));
|
|
|
|
strlcpy(name,encoded_name,sizeof(name));
|
|
}
|
|
else if (TranslateNames)
|
|
{
|
|
TranslateCName(name,A2M);
|
|
}
|
|
|
|
error = BuildFullName(parent_name,name,&full_name,&full_name_size);
|
|
if(error != OK)
|
|
goto out;
|
|
|
|
/* Trying to overwrite the root directory, are you kidding? */
|
|
if(full_name == NULL)
|
|
{
|
|
error = ERROR_OBJECT_IN_USE;
|
|
goto out;
|
|
}
|
|
|
|
dir_name = AllocateMemory(strlen(full_name)+3);
|
|
if(dir_name == NULL)
|
|
{
|
|
error = ERROR_NO_FREE_STORE;
|
|
goto out;
|
|
}
|
|
|
|
strcpy(dir_name,full_name);
|
|
base_name = NULL;
|
|
for(i = strlen(dir_name)-1 ; i >= 0 ; i--)
|
|
{
|
|
if(dir_name[i] == SMB_PATH_SEPARATOR)
|
|
{
|
|
if(i == 0)
|
|
{
|
|
memmove(&dir_name[1],&dir_name[0],strlen(dir_name)+1);
|
|
i++;
|
|
}
|
|
|
|
dir_name[i] = '\0';
|
|
|
|
base_name = &dir_name[i+1];
|
|
break;
|
|
}
|
|
}
|
|
|
|
ln = AllocateMemory(sizeof(*ln));
|
|
if(ln == NULL)
|
|
{
|
|
error = ERROR_NO_FREE_STORE;
|
|
goto out;
|
|
}
|
|
|
|
memset(ln,0,sizeof(*ln));
|
|
|
|
ln->ln_FileLock.fl_Key = (LONG)ln;
|
|
ln->ln_FileLock.fl_Access = EXCLUSIVE_LOCK;
|
|
ln->ln_FileLock.fl_Task = FileSystemPort;
|
|
ln->ln_FileLock.fl_Volume = MKBADDR(VolumeNode);
|
|
ln->ln_FullName = full_name;
|
|
|
|
error = smba_open(ServerData,dir_name,strlen(full_name)+3,&dir);
|
|
if(error < 0)
|
|
{
|
|
error = MapErrnoToIoErr(error);
|
|
goto out;
|
|
}
|
|
|
|
error = smba_mkdir(dir,base_name);
|
|
if(error < 0)
|
|
{
|
|
error = MapErrnoToIoErr(error);
|
|
goto out;
|
|
}
|
|
|
|
smba_close(dir);
|
|
dir = NULL;
|
|
|
|
SHOWSTRING(full_name);
|
|
|
|
error = smba_open(ServerData,full_name,full_name_size,&ln->ln_File);
|
|
if(error < 0)
|
|
{
|
|
error = MapErrnoToIoErr(error);
|
|
goto out;
|
|
}
|
|
|
|
AddTail((struct List *)&LockList,(struct Node *)ln);
|
|
result = MKBADDR(&ln->ln_FileLock);
|
|
SHOWVALUE(&ln->ln_FileLock);
|
|
|
|
out:
|
|
|
|
if(dir != NULL)
|
|
smba_close(dir);
|
|
|
|
FreeMemory(dir_name);
|
|
|
|
if(result == ZERO)
|
|
{
|
|
FreeMemory(full_name);
|
|
FreeMemory(ln);
|
|
}
|
|
|
|
(*error_ptr) = error;
|
|
|
|
RETURN(result);
|
|
return(result);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
STATIC BPTR
|
|
Action_LocateObject(
|
|
struct FileLock * parent,
|
|
APTR bcpl_name,
|
|
LONG mode,
|
|
LONG * error_ptr)
|
|
{
|
|
BPTR result = ZERO;
|
|
STRPTR full_name = NULL;
|
|
LONG full_name_size;
|
|
struct LockNode * ln = NULL;
|
|
STRPTR parent_name;
|
|
UBYTE name[MAX_FILENAME_LEN];
|
|
LONG error;
|
|
|
|
ENTER();
|
|
|
|
SHOWVALUE(parent);
|
|
|
|
if(parent != NULL)
|
|
{
|
|
struct LockNode * parent_ln = (struct LockNode *)parent->fl_Key;
|
|
|
|
parent_name = parent_ln->ln_FullName;
|
|
}
|
|
else
|
|
{
|
|
parent_name = NULL;
|
|
}
|
|
|
|
ConvertBString(sizeof(name),name,bcpl_name);
|
|
|
|
if (TranslateUTF8)
|
|
{
|
|
UBYTE encoded_name[MAX_FILENAME_LEN];
|
|
int encoded_name_len;
|
|
LONG name_len = strlen(name);
|
|
|
|
encoded_name_len = encode_iso8859_1_as_utf8_string(name,name_len,NULL,0);
|
|
if(encoded_name_len < 0 || encoded_name_len >= sizeof(name))
|
|
{
|
|
error = ERROR_INVALID_COMPONENT_NAME;
|
|
goto out;
|
|
}
|
|
|
|
encode_iso8859_1_as_utf8_string(name,name_len,encoded_name,sizeof(encoded_name));
|
|
|
|
strlcpy(name,encoded_name,sizeof(name));
|
|
}
|
|
else if (TranslateNames)
|
|
{
|
|
TranslateCName(name,A2M);
|
|
}
|
|
|
|
if(IsReservedName(FilePart(name)))
|
|
{
|
|
error = ERROR_OBJECT_NOT_FOUND;
|
|
goto out;
|
|
}
|
|
|
|
error = BuildFullName(parent_name,name,&full_name,&full_name_size);
|
|
if(error != OK)
|
|
goto out;
|
|
|
|
/* Trying to get a lock on the root directory's parent?
|
|
* My pleasure.
|
|
*/
|
|
if(full_name == NULL)
|
|
goto out;
|
|
|
|
ln = AllocateMemory(sizeof(*ln));
|
|
if(ln == NULL)
|
|
{
|
|
error = ERROR_NO_FREE_STORE;
|
|
goto out;
|
|
}
|
|
|
|
memset(ln,0,sizeof(*ln));
|
|
|
|
ln->ln_FileLock.fl_Key = (LONG)ln;
|
|
ln->ln_FileLock.fl_Access = (mode != EXCLUSIVE_LOCK) ? SHARED_LOCK : EXCLUSIVE_LOCK;
|
|
ln->ln_FileLock.fl_Task = FileSystemPort;
|
|
ln->ln_FileLock.fl_Volume = MKBADDR(VolumeNode);
|
|
ln->ln_FullName = full_name;
|
|
|
|
error = CheckAccessModeCollision(full_name,ln->ln_FileLock.fl_Access);
|
|
if(error != OK)
|
|
goto out;
|
|
|
|
SHOWSTRING(full_name);
|
|
|
|
error = smba_open(ServerData,full_name,full_name_size,&ln->ln_File);
|
|
if(error < 0)
|
|
{
|
|
error = MapErrnoToIoErr(error);
|
|
goto out;
|
|
}
|
|
|
|
AddTail((struct List *)&LockList,(struct Node *)ln);
|
|
result = MKBADDR(&ln->ln_FileLock);
|
|
SHOWVALUE(&ln->ln_FileLock);
|
|
|
|
out:
|
|
|
|
if(result == ZERO)
|
|
{
|
|
FreeMemory(full_name);
|
|
FreeMemory(ln);
|
|
}
|
|
|
|
(*error_ptr) = error;
|
|
|
|
RETURN(result);
|
|
return(result);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
STATIC BPTR
|
|
Action_CopyDir(
|
|
struct FileLock * lock,
|
|
LONG * error_ptr)
|
|
{
|
|
BPTR result = ZERO;
|
|
STRPTR full_name = NULL;
|
|
LONG full_name_size;
|
|
struct LockNode * ln = NULL;
|
|
STRPTR source_name;
|
|
LONG source_mode;
|
|
LONG error;
|
|
|
|
ENTER();
|
|
|
|
SHOWVALUE(lock);
|
|
|
|
if(lock != NULL && lock->fl_Access != SHARED_LOCK)
|
|
{
|
|
SHOWMSG("cannot duplicate exclusive lock");
|
|
error = ERROR_OBJECT_IN_USE;
|
|
goto out;
|
|
}
|
|
|
|
ln = AllocateMemory(sizeof(*ln));
|
|
if(ln == NULL)
|
|
{
|
|
error = ERROR_NO_FREE_STORE;
|
|
goto out;
|
|
}
|
|
|
|
memset(ln,0,sizeof(*ln));
|
|
|
|
if(lock != NULL)
|
|
{
|
|
struct LockNode * source = (struct LockNode *)lock->fl_Key;
|
|
|
|
source_name = source->ln_FullName;
|
|
source_mode = source->ln_FileLock.fl_Access;
|
|
}
|
|
else
|
|
{
|
|
source_name = SMB_ROOT_DIR_NAME;
|
|
source_mode = SHARED_LOCK;
|
|
}
|
|
|
|
full_name_size = strlen(source_name)+3;
|
|
if(full_name_size < SMB_MAXNAMELEN+1)
|
|
full_name_size = SMB_MAXNAMELEN+1;
|
|
|
|
full_name = AllocateMemory(full_name_size);
|
|
if(full_name == NULL)
|
|
{
|
|
error = ERROR_NO_FREE_STORE;
|
|
goto out;
|
|
}
|
|
|
|
strcpy(full_name,source_name);
|
|
|
|
ln->ln_FileLock.fl_Key = (LONG)ln;
|
|
ln->ln_FileLock.fl_Access = source_mode;
|
|
ln->ln_FileLock.fl_Task = FileSystemPort;
|
|
ln->ln_FileLock.fl_Volume = MKBADDR(VolumeNode);
|
|
ln->ln_FullName = full_name;
|
|
|
|
SHOWSTRING(full_name);
|
|
|
|
error = smba_open(ServerData,full_name,full_name_size,&ln->ln_File);
|
|
if(error < 0)
|
|
{
|
|
error = MapErrnoToIoErr(error);
|
|
goto out;
|
|
}
|
|
|
|
AddTail((struct List *)&LockList,(struct Node *)ln);
|
|
result = MKBADDR(&ln->ln_FileLock);
|
|
SHOWVALUE(&ln->ln_FileLock);
|
|
|
|
out:
|
|
|
|
if(result == ZERO)
|
|
{
|
|
FreeMemory(full_name);
|
|
FreeMemory(ln);
|
|
}
|
|
|
|
(*error_ptr) = error;
|
|
|
|
RETURN(result);
|
|
return(result);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
STATIC LONG
|
|
Action_FreeLock(
|
|
struct FileLock * lock,
|
|
LONG * error_ptr)
|
|
{
|
|
LONG result = DOSTRUE;
|
|
struct LockNode * ln;
|
|
LONG error = OK;
|
|
|
|
ENTER();
|
|
|
|
SHOWVALUE(lock);
|
|
|
|
if(lock == NULL)
|
|
goto out;
|
|
|
|
ln = (struct LockNode *)lock->fl_Key;
|
|
|
|
Remove((struct Node *)ln);
|
|
|
|
smba_close(ln->ln_File);
|
|
FreeMemory(ln->ln_FullName);
|
|
FreeMemory(ln);
|
|
|
|
out:
|
|
|
|
(*error_ptr) = error;
|
|
|
|
RETURN(result);
|
|
return(result);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
STATIC LONG
|
|
Action_SameLock(
|
|
struct FileLock * lock1,
|
|
struct FileLock * lock2,
|
|
LONG * error_ptr)
|
|
{
|
|
LONG result = DOSFALSE;
|
|
STRPTR name1;
|
|
STRPTR name2;
|
|
LONG error = OK;
|
|
|
|
ENTER();
|
|
|
|
SHOWVALUE(lock1);
|
|
SHOWVALUE(lock2);
|
|
|
|
if(lock1 != NULL)
|
|
{
|
|
struct LockNode * ln = (struct LockNode *)lock1->fl_Key;
|
|
|
|
name1 = ln->ln_FullName;
|
|
}
|
|
else
|
|
{
|
|
name1 = SMB_ROOT_DIR_NAME;
|
|
}
|
|
|
|
if(lock2 != NULL)
|
|
{
|
|
struct LockNode * ln = (struct LockNode *)lock2->fl_Key;
|
|
|
|
name2 = ln->ln_FullName;
|
|
}
|
|
else
|
|
{
|
|
name2 = SMB_ROOT_DIR_NAME;
|
|
}
|
|
|
|
SHOWSTRING(name1);
|
|
SHOWSTRING(name2);
|
|
|
|
if(Stricmp(name1,name2) == SAME)
|
|
result = DOSTRUE;
|
|
|
|
(*error_ptr) = error;
|
|
|
|
RETURN(result);
|
|
return(result);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
STATIC LONG
|
|
Action_SetProtect(
|
|
struct FileLock * parent,
|
|
APTR bcpl_name,
|
|
LONG mask,
|
|
LONG * error_ptr)
|
|
{
|
|
LONG result = DOSFALSE;
|
|
STRPTR full_name = NULL;
|
|
LONG full_name_size;
|
|
smba_file_t * file = NULL;
|
|
STRPTR parent_name;
|
|
UBYTE name[MAX_FILENAME_LEN];
|
|
smba_stat_t st;
|
|
LONG error;
|
|
|
|
ENTER();
|
|
|
|
if(WriteProtected)
|
|
{
|
|
error = ERROR_DISK_WRITE_PROTECTED;
|
|
goto out;
|
|
}
|
|
|
|
SHOWVALUE(parent);
|
|
|
|
if(parent != NULL)
|
|
{
|
|
struct LockNode * ln = (struct LockNode *)parent->fl_Key;
|
|
|
|
parent_name = ln->ln_FullName;
|
|
}
|
|
else
|
|
{
|
|
parent_name = NULL;
|
|
}
|
|
|
|
ConvertBString(sizeof(name),name,bcpl_name);
|
|
|
|
if (TranslateUTF8)
|
|
{
|
|
UBYTE encoded_name[MAX_FILENAME_LEN];
|
|
int encoded_name_len;
|
|
LONG name_len = strlen(name);
|
|
|
|
encoded_name_len = encode_iso8859_1_as_utf8_string(name,name_len,NULL,0);
|
|
if(encoded_name_len < 0 || encoded_name_len >= sizeof(name))
|
|
{
|
|
error = ERROR_INVALID_COMPONENT_NAME;
|
|
goto out;
|
|
}
|
|
|
|
encode_iso8859_1_as_utf8_string(name,name_len,encoded_name,sizeof(encoded_name));
|
|
|
|
strlcpy(name,encoded_name,sizeof(name));
|
|
}
|
|
else if (TranslateNames)
|
|
{
|
|
TranslateCName(name,A2M);
|
|
}
|
|
|
|
error = BuildFullName(parent_name,name,&full_name,&full_name_size);
|
|
if(error != OK)
|
|
goto out;
|
|
|
|
/* Trying to change the protection bits of the root
|
|
* directory, are you kidding?
|
|
*/
|
|
if(full_name == NULL)
|
|
{
|
|
error = ERROR_OBJECT_WRONG_TYPE;
|
|
goto out;
|
|
}
|
|
|
|
SHOWSTRING(full_name);
|
|
|
|
error = smba_open(ServerData,full_name,full_name_size,&file);
|
|
if(error < 0)
|
|
{
|
|
error = MapErrnoToIoErr(error);
|
|
goto out;
|
|
}
|
|
|
|
memset(&st,0,sizeof(st));
|
|
|
|
mask ^= FIBF_READ | FIBF_WRITE | FIBF_EXECUTE | FIBF_DELETE;
|
|
|
|
st.atime = -1;
|
|
st.ctime = -1;
|
|
st.mtime = -1;
|
|
st.size = -1;
|
|
|
|
if((mask & (FIBF_WRITE|FIBF_DELETE)) != (FIBF_WRITE|FIBF_DELETE))
|
|
{
|
|
SHOWMSG("write protection enabled");
|
|
st.is_wp = TRUE;
|
|
}
|
|
else
|
|
{
|
|
SHOWMSG("write protection disabled");
|
|
}
|
|
|
|
/* Careful: the 'archive' attribute has exactly the opposite
|
|
* meaning in the Amiga and the SMB worlds.
|
|
*/
|
|
st.is_archive = ((mask & FIBF_ARCHIVE) == 0);
|
|
|
|
/* The 'system' attribute is associated with the 'pure' bit for now. */
|
|
st.is_system = ((mask & FIBF_PURE) != 0);
|
|
|
|
error = smba_setattr(file,&st);
|
|
if(error < 0)
|
|
{
|
|
error = MapErrnoToIoErr(error);
|
|
goto out;
|
|
}
|
|
|
|
result = DOSTRUE;
|
|
|
|
out:
|
|
|
|
FreeMemory(full_name);
|
|
|
|
if(file != NULL)
|
|
smba_close(file);
|
|
|
|
(*error_ptr) = error;
|
|
|
|
RETURN(result);
|
|
return(result);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
STATIC LONG
|
|
Action_RenameObject(
|
|
struct FileLock * source_lock,
|
|
APTR source_bcpl_name,
|
|
struct FileLock * destination_lock,
|
|
APTR destination_bcpl_name,
|
|
LONG * error_ptr)
|
|
{
|
|
struct LockNode * ln;
|
|
LONG result = DOSFALSE;
|
|
STRPTR full_source_name = NULL;
|
|
LONG full_source_name_size;
|
|
STRPTR full_destination_name = NULL;
|
|
LONG full_destination_name_size;
|
|
UBYTE name[MAX_FILENAME_LEN];
|
|
STRPTR parent_name;
|
|
LONG error;
|
|
|
|
ENTER();
|
|
|
|
if(WriteProtected)
|
|
{
|
|
error = ERROR_DISK_WRITE_PROTECTED;
|
|
goto out;
|
|
}
|
|
|
|
SHOWVALUE(source_lock);
|
|
SHOWVALUE(destination_lock);
|
|
|
|
if(source_lock != NULL)
|
|
{
|
|
ln = (struct LockNode *)source_lock->fl_Key;
|
|
|
|
parent_name = ln->ln_FullName;
|
|
}
|
|
else
|
|
{
|
|
parent_name = NULL;
|
|
}
|
|
|
|
ConvertBString(sizeof(name),name,source_bcpl_name);
|
|
|
|
if (TranslateUTF8)
|
|
{
|
|
UBYTE encoded_name[MAX_FILENAME_LEN];
|
|
int encoded_name_len;
|
|
LONG name_len = strlen(name);
|
|
|
|
encoded_name_len = encode_iso8859_1_as_utf8_string(name,name_len,NULL,0);
|
|
if(encoded_name_len < 0 || encoded_name_len >= sizeof(name))
|
|
{
|
|
error = ERROR_INVALID_COMPONENT_NAME;
|
|
goto out;
|
|
}
|
|
|
|
encode_iso8859_1_as_utf8_string(name,name_len,encoded_name,sizeof(encoded_name));
|
|
|
|
strlcpy(name,encoded_name,sizeof(name));
|
|
}
|
|
else if (TranslateNames)
|
|
{
|
|
TranslateCName(name,A2M);
|
|
}
|
|
|
|
error = BuildFullName(parent_name,name,&full_source_name,&full_source_name_size);
|
|
if(error != OK)
|
|
goto out;
|
|
|
|
/* Trying to rename the root directory, are you kidding? */
|
|
if(full_source_name == NULL)
|
|
{
|
|
error = ERROR_OBJECT_IN_USE;
|
|
goto out;
|
|
}
|
|
|
|
if(destination_lock != NULL)
|
|
{
|
|
ln = (struct LockNode *)destination_lock->fl_Key;
|
|
|
|
parent_name = ln->ln_FullName;
|
|
}
|
|
else
|
|
{
|
|
parent_name = NULL;
|
|
}
|
|
|
|
ConvertBString(sizeof(name),name,destination_bcpl_name);
|
|
|
|
if (TranslateUTF8)
|
|
{
|
|
UBYTE encoded_name[MAX_FILENAME_LEN];
|
|
int encoded_name_len;
|
|
LONG name_len = strlen(name);
|
|
|
|
encoded_name_len = encode_iso8859_1_as_utf8_string(name,name_len,NULL,0);
|
|
if(encoded_name_len < 0 || encoded_name_len >= sizeof(name))
|
|
{
|
|
error = ERROR_INVALID_COMPONENT_NAME;
|
|
goto out;
|
|
}
|
|
|
|
encode_iso8859_1_as_utf8_string(name,name_len,encoded_name,sizeof(encoded_name));
|
|
|
|
strlcpy(name,encoded_name,sizeof(name));
|
|
}
|
|
else if (TranslateNames)
|
|
{
|
|
TranslateCName(name,A2M);
|
|
}
|
|
|
|
error = BuildFullName(parent_name,name,&full_destination_name,&full_destination_name_size);
|
|
if(error != OK)
|
|
goto out;
|
|
|
|
/* Trying to rename the root directory, are you kidding? */
|
|
if(full_destination_name == NULL)
|
|
{
|
|
error = ERROR_OBJECT_IN_USE;
|
|
goto out;
|
|
}
|
|
|
|
error = NameAlreadyInUse(full_source_name);
|
|
if(error != OK)
|
|
goto out;
|
|
|
|
error = NameAlreadyInUse(full_destination_name);
|
|
if(error != OK)
|
|
goto out;
|
|
|
|
SHOWSTRING(full_source_name);
|
|
SHOWSTRING(full_destination_name);
|
|
|
|
error = smba_rename(ServerData,full_source_name,full_destination_name);
|
|
if(error < 0)
|
|
{
|
|
error = MapErrnoToIoErr(error);
|
|
goto out;
|
|
}
|
|
|
|
result = DOSTRUE;
|
|
|
|
out:
|
|
|
|
FreeMemory(full_source_name);
|
|
FreeMemory(full_destination_name);
|
|
|
|
(*error_ptr) = error;
|
|
|
|
RETURN(result);
|
|
return(result);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
STATIC LONG
|
|
Action_DiskInfo(
|
|
struct InfoData * id,
|
|
LONG * error_ptr)
|
|
{
|
|
LONG result = DOSTRUE;
|
|
LONG block_size;
|
|
LONG num_blocks;
|
|
LONG num_blocks_free;
|
|
LONG error;
|
|
|
|
ENTER();
|
|
|
|
memset(id,0,sizeof(*id));
|
|
|
|
if(WriteProtected)
|
|
id->id_DiskState = ID_WRITE_PROTECTED;
|
|
else
|
|
id->id_DiskState = ID_VALIDATED;
|
|
|
|
error = smba_statfs(ServerData,&block_size,&num_blocks,&num_blocks_free);
|
|
if(error >= 0)
|
|
{
|
|
SHOWMSG("got the disk data");
|
|
SHOWVALUE(block_size);
|
|
SHOWVALUE(num_blocks);
|
|
SHOWVALUE(num_blocks_free);
|
|
|
|
if(block_size <= 0)
|
|
block_size = 512;
|
|
|
|
if(block_size < 512)
|
|
{
|
|
num_blocks /= (512 / block_size);
|
|
num_blocks_free /= (512 / block_size);
|
|
}
|
|
else if (block_size > 512)
|
|
{
|
|
num_blocks *= (block_size / 512);
|
|
num_blocks_free *= (block_size / 512);
|
|
}
|
|
|
|
id->id_NumBlocks = num_blocks;
|
|
id->id_NumBlocksUsed = num_blocks - num_blocks_free;
|
|
id->id_BytesPerBlock = 512;
|
|
id->id_DiskType = ID_DOS_DISK;
|
|
id->id_VolumeNode = MKBADDR(VolumeNode);
|
|
id->id_InUse = NOT (IsListEmpty((struct List *)&FileList) && IsListEmpty((struct List *)&LockList));
|
|
|
|
if(id->id_NumBlocks == 0)
|
|
id->id_NumBlocks = 1;
|
|
|
|
if(id->id_NumBlocksUsed == 0)
|
|
id->id_NumBlocksUsed = 1;
|
|
}
|
|
else
|
|
{
|
|
SHOWMSG("could not get any disk data");
|
|
|
|
id->id_NumBlocks = 1;
|
|
id->id_NumBlocksUsed = 1;
|
|
id->id_BytesPerBlock = 512;
|
|
id->id_DiskType = ID_NO_DISK_PRESENT;
|
|
|
|
error = MapErrnoToIoErr(error);
|
|
result = DOSFALSE;
|
|
}
|
|
|
|
SHOWVALUE(id->id_NumBlocks);
|
|
SHOWVALUE(id->id_NumBlocksUsed);
|
|
SHOWVALUE(id->id_BytesPerBlock);
|
|
SHOWVALUE(id->id_DiskType);
|
|
SHOWVALUE(id->id_VolumeNode);
|
|
SHOWVALUE(id->id_InUse);
|
|
|
|
(*error_ptr) = error;
|
|
|
|
RETURN(result);
|
|
return(result);
|
|
}
|
|
|
|
STATIC LONG
|
|
Action_Info(
|
|
struct FileLock * lock,
|
|
struct InfoData * id,
|
|
LONG * error_ptr)
|
|
{
|
|
LONG result;
|
|
|
|
ENTER();
|
|
|
|
SHOWVALUE(lock);
|
|
|
|
/* We need to check if the lock matches the volume node. However,
|
|
* a NULL lock is valid, too.
|
|
*/
|
|
if(lock != NULL && lock->fl_Volume != MKBADDR(VolumeNode))
|
|
{
|
|
SHOWMSG("volume node does not match");
|
|
|
|
result = DOSFALSE;
|
|
|
|
(*error_ptr) = ERROR_NO_DISK;
|
|
}
|
|
else
|
|
{
|
|
result = Action_DiskInfo(id,error_ptr);
|
|
}
|
|
|
|
RETURN(result);
|
|
return(result);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
STATIC LONG
|
|
Action_ExamineObject(
|
|
struct FileLock * lock,
|
|
struct FileInfoBlock * fib,
|
|
LONG * error_ptr)
|
|
{
|
|
LONG result = DOSFALSE;
|
|
LONG error = OK;
|
|
|
|
ENTER();
|
|
|
|
SHOWVALUE(lock);
|
|
|
|
memset(fib,0,sizeof(*fib));
|
|
|
|
if(lock == NULL)
|
|
{
|
|
STRPTR volume_name = BADDR(VolumeNode->dol_Name);
|
|
LONG len = volume_name[0];
|
|
|
|
SHOWMSG("ZERO root lock");
|
|
|
|
memcpy(fib->fib_FileName+1,volume_name+1,len);
|
|
fib->fib_FileName[0] = len;
|
|
|
|
fib->fib_DirEntryType = ST_ROOT;
|
|
fib->fib_EntryType = ST_ROOT;
|
|
fib->fib_NumBlocks = 1;
|
|
fib->fib_Date = VolumeNode->dol_misc.dol_volume.dol_VolumeDate;
|
|
fib->fib_DiskKey = -1;
|
|
fib->fib_Protection = FIBF_OTR_READ|FIBF_OTR_EXECUTE|FIBF_OTR_WRITE|FIBF_OTR_DELETE|
|
|
FIBF_GRP_READ|FIBF_GRP_EXECUTE|FIBF_GRP_WRITE|FIBF_GRP_DELETE;
|
|
}
|
|
else
|
|
{
|
|
struct LockNode * ln = (struct LockNode *)lock->fl_Key;
|
|
LONG seconds;
|
|
smba_stat_t st;
|
|
|
|
error = smba_getattr(ln->ln_File,&st);
|
|
if(error < 0)
|
|
{
|
|
SHOWMSG("information not available");
|
|
|
|
error = MapErrnoToIoErr(error);
|
|
goto out;
|
|
}
|
|
|
|
seconds = st.mtime - UNIX_TIME_OFFSET - GetTimeZoneDelta();
|
|
if(seconds < 0)
|
|
seconds = 0;
|
|
|
|
fib->fib_Date.ds_Days = (seconds / (24 * 60 * 60));
|
|
fib->fib_Date.ds_Minute = (seconds % (24 * 60 * 60)) / 60;
|
|
fib->fib_Date.ds_Tick = (seconds % 60) * TICKS_PER_SECOND;
|
|
|
|
SHOWSTRING(ln->ln_FullName);
|
|
|
|
if(strcmp(ln->ln_FullName,SMB_ROOT_DIR_NAME) == SAME)
|
|
{
|
|
STRPTR volume_name = BADDR(VolumeNode->dol_Name);
|
|
LONG len = volume_name[0];
|
|
|
|
SHOWMSG("root lock");
|
|
|
|
memcpy(fib->fib_FileName+1,volume_name+1,len);
|
|
fib->fib_FileName[0] = len;
|
|
|
|
fib->fib_DirEntryType = ST_ROOT;
|
|
fib->fib_EntryType = ST_ROOT;
|
|
fib->fib_NumBlocks = 1;
|
|
fib->fib_Protection = FIBF_OTR_READ|FIBF_OTR_EXECUTE|FIBF_OTR_WRITE|FIBF_OTR_DELETE|
|
|
FIBF_GRP_READ|FIBF_GRP_EXECUTE|FIBF_GRP_WRITE|FIBF_GRP_DELETE;
|
|
}
|
|
else
|
|
{
|
|
STRPTR name;
|
|
LONG name_len;
|
|
LONG i;
|
|
|
|
name = ln->ln_FullName;
|
|
name_len = strlen(name);
|
|
|
|
for(i = name_len-1 ; i >= 0 ; i--)
|
|
{
|
|
if(name[i] == SMB_PATH_SEPARATOR)
|
|
{
|
|
name = &name[i+1];
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Just checking: will the name fit? */
|
|
if(name_len >= sizeof(fib->fib_FileName))
|
|
{
|
|
error = ERROR_INVALID_COMPONENT_NAME;
|
|
goto out;
|
|
}
|
|
|
|
if(TranslateUTF8)
|
|
{
|
|
UBYTE decoded_name[MAX_FILENAME_LEN];
|
|
int decoded_name_len;
|
|
|
|
decoded_name_len = decode_utf8_as_iso8859_1_string(name,name_len,NULL,0);
|
|
if(decoded_name_len < 0 || decoded_name_len >= MAX_FILENAME_LEN)
|
|
{
|
|
error = ERROR_INVALID_COMPONENT_NAME;
|
|
goto out;
|
|
}
|
|
|
|
decoded_name_len = decode_utf8_as_iso8859_1_string(name,name_len,decoded_name,sizeof(decoded_name));
|
|
|
|
fib->fib_FileName[0] = decoded_name_len;
|
|
memcpy(&fib->fib_FileName[1],decoded_name,decoded_name_len);
|
|
}
|
|
else
|
|
{
|
|
ConvertCString(fib->fib_FileName,sizeof(fib->fib_FileName),name,name_len);
|
|
|
|
if(TranslateNames)
|
|
TranslateBName(fib->fib_FileName,M2A);
|
|
}
|
|
|
|
fib->fib_DirEntryType = st.is_dir ? ST_USERDIR : ST_FILE;
|
|
fib->fib_EntryType = fib->fib_DirEntryType;
|
|
fib->fib_NumBlocks = (st.size + 511) / 512;
|
|
fib->fib_Size = st.size;
|
|
fib->fib_Protection = FIBF_OTR_READ|FIBF_OTR_EXECUTE|FIBF_OTR_WRITE|FIBF_OTR_DELETE|
|
|
FIBF_GRP_READ|FIBF_GRP_EXECUTE|FIBF_GRP_WRITE|FIBF_GRP_DELETE;
|
|
|
|
if(st.is_wp)
|
|
fib->fib_Protection ^= (FIBF_OTR_WRITE|FIBF_OTR_DELETE|FIBF_GRP_WRITE|FIBF_GRP_DELETE|FIBF_WRITE|FIBF_DELETE);
|
|
|
|
/* Careful: the 'archive' attribute has exactly the opposite
|
|
* meaning in the Amiga and the SMB worlds.
|
|
*/
|
|
if(NOT st.is_archive)
|
|
fib->fib_Protection |= FIBF_ARCHIVE;
|
|
|
|
if(st.is_system)
|
|
fib->fib_Protection |= FIBF_PURE;
|
|
|
|
if(NOT st.is_dir)
|
|
fib->fib_DiskKey = -1;
|
|
}
|
|
}
|
|
|
|
result = DOSTRUE;
|
|
|
|
D(("fib->fib_FileName = \"%b\"",MKBADDR(fib->fib_FileName)));
|
|
SHOWVALUE(fib->fib_DirEntryType);
|
|
SHOWVALUE(fib->fib_NumBlocks);
|
|
SHOWVALUE(fib->fib_Size);
|
|
SHOWVALUE(fib->fib_Date.ds_Days);
|
|
SHOWVALUE(fib->fib_Date.ds_Minute);
|
|
SHOWVALUE(fib->fib_Date.ds_Tick);
|
|
SHOWVALUE(fib->fib_DiskKey);
|
|
|
|
out:
|
|
|
|
(*error_ptr) = error;
|
|
|
|
RETURN(result);
|
|
return(result);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
STATIC BOOL
|
|
NameIsAcceptable(STRPTR name,LONG max_len)
|
|
{
|
|
BOOL result = FALSE;
|
|
UBYTE c;
|
|
|
|
/* This takes care of "." and "..". */
|
|
if(name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')))
|
|
goto out;
|
|
|
|
/* Now for embedded '/', ':' and '\' characters and
|
|
* names that just don't want to fit.
|
|
*/
|
|
while((c = (*name++)) != '\0')
|
|
{
|
|
max_len--;
|
|
if(max_len == 0 || c == '/' || c == ':' || c == SMB_PATH_SEPARATOR)
|
|
goto out;
|
|
}
|
|
|
|
result = TRUE;
|
|
|
|
out:
|
|
|
|
return(result);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
static int
|
|
dir_scan_callback_func_exnext(
|
|
struct FileInfoBlock * fib,
|
|
int unused_fpos,
|
|
int nextpos,
|
|
char * name,
|
|
int eof,
|
|
smba_stat_t * st)
|
|
{
|
|
int result = 0;
|
|
LONG name_len;
|
|
LONG seconds;
|
|
|
|
ENTER();
|
|
|
|
D((" '%s'",name));
|
|
D((" is_dir=%ld is_wp=%ld is_hidden=%ld size=%ld",
|
|
st->is_dir,st->is_wp,st->is_hidden,st->size));
|
|
D((" nextpos=%ld eof=%ld",nextpos,eof));
|
|
|
|
/* Skip file and drawer names that we wouldn't be
|
|
* able to handle in the first place.
|
|
*/
|
|
if(!NameIsAcceptable((STRPTR)name,sizeof(fib->fib_FileName)) || (st->is_hidden && OmitHidden))
|
|
goto out;
|
|
|
|
name_len = strlen(name);
|
|
|
|
if(TranslateUTF8)
|
|
{
|
|
UBYTE decoded_name[MAX_FILENAME_LEN];
|
|
int decoded_name_len;
|
|
|
|
decoded_name_len = decode_utf8_as_iso8859_1_string(name,name_len,NULL,0);
|
|
if(decoded_name_len < 0 || decoded_name_len >= MAX_FILENAME_LEN)
|
|
goto out;
|
|
|
|
decoded_name_len = decode_utf8_as_iso8859_1_string(name,name_len,decoded_name,sizeof(decoded_name));
|
|
|
|
fib->fib_FileName[0] = decoded_name_len;
|
|
memcpy(&fib->fib_FileName[1],decoded_name,decoded_name_len);
|
|
}
|
|
else
|
|
{
|
|
ConvertCString(fib->fib_FileName,sizeof(fib->fib_FileName),name,name_len);
|
|
|
|
if(TranslateNames)
|
|
TranslateBName(fib->fib_FileName,M2A);
|
|
}
|
|
|
|
fib->fib_DirEntryType = st->is_dir ? ST_USERDIR : ST_FILE;
|
|
fib->fib_EntryType = fib->fib_DirEntryType;
|
|
fib->fib_NumBlocks = (st->size + 511) / 512;
|
|
fib->fib_Size = st->size;
|
|
fib->fib_Protection = FIBF_OTR_READ|FIBF_OTR_EXECUTE|FIBF_OTR_WRITE|FIBF_OTR_DELETE|
|
|
FIBF_GRP_READ|FIBF_GRP_EXECUTE|FIBF_GRP_WRITE|FIBF_GRP_DELETE;
|
|
|
|
if(st->is_wp)
|
|
fib->fib_Protection ^= (FIBF_OTR_WRITE|FIBF_OTR_DELETE|FIBF_GRP_WRITE|FIBF_GRP_DELETE|FIBF_WRITE|FIBF_DELETE);
|
|
|
|
/* Careful: the 'archive' attribute has exactly the opposite
|
|
* meaning in the Amiga and the SMB worlds.
|
|
*/
|
|
if(NOT st->is_archive)
|
|
fib->fib_Protection |= FIBF_ARCHIVE;
|
|
|
|
if(st->is_system)
|
|
fib->fib_Protection |= FIBF_PURE;
|
|
|
|
/* If modification time is 0 use creation time instead (cyfm 2009-03-18). */
|
|
seconds = (st->mtime == 0 ? st->ctime : st->mtime) - UNIX_TIME_OFFSET - GetTimeZoneDelta();
|
|
if(seconds < 0)
|
|
seconds = 0;
|
|
|
|
fib->fib_Date.ds_Days = (seconds / (24 * 60 * 60));
|
|
fib->fib_Date.ds_Minute = (seconds % (24 * 60 * 60)) / 60;
|
|
fib->fib_Date.ds_Tick = (seconds % 60) * TICKS_PER_SECOND;
|
|
|
|
result = 1;
|
|
|
|
out:
|
|
|
|
fib->fib_DiskKey = eof ? -1 : nextpos;
|
|
|
|
RETURN(result);
|
|
return(result);
|
|
}
|
|
|
|
STATIC LONG
|
|
Action_ExamineNext(
|
|
struct FileLock * lock,
|
|
struct FileInfoBlock * fib,
|
|
LONG * error_ptr)
|
|
{
|
|
struct LockNode * ln;
|
|
LONG result = DOSFALSE;
|
|
LONG error = OK;
|
|
long offset;
|
|
int count;
|
|
|
|
ENTER();
|
|
|
|
SHOWVALUE(lock);
|
|
|
|
if(fib->fib_DiskKey == -1)
|
|
{
|
|
SHOWMSG("scanning finished.");
|
|
error = ERROR_NO_MORE_ENTRIES;
|
|
goto out;
|
|
}
|
|
|
|
if(lock == NULL)
|
|
{
|
|
SHOWMSG("invalid lock");
|
|
error = ERROR_INVALID_LOCK;
|
|
goto out;
|
|
}
|
|
|
|
offset = fib->fib_DiskKey;
|
|
|
|
ln = (struct LockNode *)lock->fl_Key;
|
|
|
|
/* Check if we should restart scanning the directory
|
|
* contents. This is tricky at best and may produce
|
|
* irritating results :(
|
|
*/
|
|
if(ln->ln_RestartExamine)
|
|
{
|
|
offset = 0;
|
|
|
|
ln->ln_RestartExamine = FALSE;
|
|
}
|
|
|
|
memset(fib,0,sizeof(*fib));
|
|
|
|
SHOWMSG("calling 'smba_readdir'");
|
|
SHOWVALUE(offset);
|
|
|
|
count = smba_readdir(ln->ln_File,offset,fib,(smba_callback_t)dir_scan_callback_func_exnext);
|
|
|
|
SHOWVALUE(count);
|
|
|
|
if(count == 0 || fib->fib_FileName[0] == '\0')
|
|
{
|
|
SHOWMSG("nothing to be read");
|
|
fib->fib_DiskKey = -1;
|
|
|
|
error = ERROR_NO_MORE_ENTRIES;
|
|
goto out;
|
|
}
|
|
else if (count == (-EIO))
|
|
{
|
|
SHOWMSG("ouch! directory read error");
|
|
fib->fib_DiskKey = -1;
|
|
|
|
error = ERROR_NO_DEFAULT_DIR;
|
|
goto out;
|
|
}
|
|
else if (count < 0)
|
|
{
|
|
SHOWMSG("error whilst scanning");
|
|
SHOWVALUE(count);
|
|
fib->fib_DiskKey = -1;
|
|
|
|
error = MapErrnoToIoErr(count);
|
|
goto out;
|
|
}
|
|
|
|
result = DOSTRUE;
|
|
|
|
out:
|
|
|
|
(*error_ptr) = error;
|
|
|
|
RETURN(result);
|
|
return(result);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
struct ExAllContext
|
|
{
|
|
struct ExAllData * ec_Last;
|
|
struct ExAllData * ec_Next;
|
|
ULONG ec_BytesLeft;
|
|
ULONG ec_MinSize;
|
|
struct ExAllControl * ec_Control;
|
|
ULONG ec_Type;
|
|
LONG ec_Error;
|
|
BOOL ec_FirstAttempt;
|
|
};
|
|
|
|
static int
|
|
dir_scan_callback_func_exall(
|
|
struct ExAllContext * ec,
|
|
int unused_fpos,
|
|
int nextpos,
|
|
char * name,
|
|
int eof,
|
|
smba_stat_t * st)
|
|
{
|
|
UBYTE decoded_name[MAX_FILENAME_LEN];
|
|
BOOL ignore_this_entry = FALSE;
|
|
int result = 0;
|
|
|
|
ENTER();
|
|
|
|
D((" '%s'",name));
|
|
D((" is_dir=%ld is_wp=%ld is_hidden=%ld size=%ld",
|
|
st->is_dir,st->is_wp,st->is_hidden,st->size));
|
|
D((" nextpos=%ld eof=%ld",nextpos,eof));
|
|
|
|
/* If necessary, translate the name of the file first, so that we
|
|
* can decide early on whether or not it should show up in a
|
|
* directory listing. This filters out, for example, files using
|
|
* characters which are not usable on the Amiga because they fall
|
|
* outside the 8 bit character set used.
|
|
*/
|
|
if(TranslateUTF8)
|
|
{
|
|
LONG name_len = strlen(name);
|
|
int decoded_name_len;
|
|
|
|
decoded_name_len = decode_utf8_as_iso8859_1_string(name,name_len,NULL,0);
|
|
if(decoded_name_len < 0 || decoded_name_len >= MAX_FILENAME_LEN)
|
|
{
|
|
ignore_this_entry = TRUE;
|
|
}
|
|
else
|
|
{
|
|
decode_utf8_as_iso8859_1_string(name,name_len,decoded_name,sizeof(decoded_name));
|
|
|
|
/* Use the decoded replacement name. */
|
|
name = decoded_name;
|
|
}
|
|
}
|
|
|
|
/* Skip file and drawer names that we wouldn't be
|
|
* able to handle in the first place.
|
|
*/
|
|
if(!ignore_this_entry && NameIsAcceptable((STRPTR)name,MAX_FILENAME_LEN) && NOT (st->is_hidden && OmitHidden))
|
|
{
|
|
struct ExAllData * ed;
|
|
ULONG size;
|
|
ULONG type = ec->ec_Type;
|
|
BOOL take_it;
|
|
|
|
size = (ec->ec_MinSize + strlen(name)+1 + 3) & ~3UL;
|
|
SHOWVALUE(size);
|
|
if(size > ec->ec_BytesLeft)
|
|
{
|
|
D(("size %ld > ec->ec_BytesLeft %ld",size,ec->ec_BytesLeft));
|
|
|
|
/* If this is the first directory entry,
|
|
* stop the entire process before it has
|
|
* really begun.
|
|
*/
|
|
if(ec->ec_FirstAttempt)
|
|
{
|
|
SHOWMSG("this was the first read attempt.");
|
|
ec->ec_Control->eac_Entries = 0;
|
|
ec->ec_Error = ERROR_NO_FREE_STORE;
|
|
}
|
|
else
|
|
{
|
|
SHOWMSG("try again");
|
|
ec->ec_Error = 0;
|
|
}
|
|
|
|
result = 1;
|
|
goto out;
|
|
}
|
|
|
|
ed = ec->ec_Next;
|
|
|
|
ed->ed_Next = NULL;
|
|
ed->ed_Name = (STRPTR)(((ULONG)ed) + ec->ec_MinSize);
|
|
strcpy(ed->ed_Name,name);
|
|
|
|
if(!TranslateUTF8 && TranslateNames)
|
|
TranslateCName(ed->ed_Name,M2A);
|
|
|
|
if(type >= ED_TYPE)
|
|
ed->ed_Type = st->is_dir ? ST_USERDIR : ST_FILE;
|
|
|
|
if(type >= ED_SIZE)
|
|
ed->ed_Size = st->size;
|
|
|
|
if(type >= ED_PROTECTION)
|
|
{
|
|
ed->ed_Prot = FIBF_OTR_READ|FIBF_OTR_EXECUTE|FIBF_OTR_WRITE|FIBF_OTR_DELETE|
|
|
FIBF_GRP_READ|FIBF_GRP_EXECUTE|FIBF_GRP_WRITE|FIBF_GRP_DELETE;
|
|
|
|
if(st->is_wp)
|
|
ed->ed_Prot ^= (FIBF_OTR_WRITE|FIBF_OTR_DELETE|FIBF_GRP_WRITE|FIBF_GRP_DELETE|FIBF_WRITE|FIBF_DELETE);
|
|
|
|
/* Careful: the 'archive' attribute has exactly the opposite
|
|
* meaning in the Amiga and the SMB worlds.
|
|
*/
|
|
if(NOT st->is_archive)
|
|
ed->ed_Prot |= FIBF_ARCHIVE;
|
|
|
|
if(st->is_system)
|
|
ed->ed_Prot |= FIBF_PURE;
|
|
}
|
|
|
|
if(type >= ED_DATE)
|
|
{
|
|
LONG seconds;
|
|
|
|
/* If modification time is 0 use creation time instead (cyfm 2009-03-18). */
|
|
seconds = (st->mtime == 0 ? st->ctime : st->mtime) - UNIX_TIME_OFFSET - GetTimeZoneDelta();
|
|
if(seconds < 0)
|
|
seconds = 0;
|
|
|
|
ed->ed_Days = (seconds / (24 * 60 * 60));
|
|
ed->ed_Mins = (seconds % (24 * 60 * 60)) / 60;
|
|
ed->ed_Ticks = (seconds % 60) * TICKS_PER_SECOND;
|
|
}
|
|
|
|
if(type >= ED_COMMENT)
|
|
ed->ed_Comment = "";
|
|
|
|
if(type >= ED_OWNER)
|
|
ed->ed_OwnerUID = ed->ed_OwnerGID = 0;
|
|
|
|
take_it = TRUE;
|
|
|
|
if(ec->ec_Control->eac_MatchString != NULL)
|
|
{
|
|
SHOWMSG("checking against match string");
|
|
if(NOT MatchPatternNoCase(ec->ec_Control->eac_MatchString,ed->ed_Name))
|
|
{
|
|
SHOWMSG("does not match");
|
|
take_it = FALSE;
|
|
}
|
|
}
|
|
|
|
if(take_it && ec->ec_Control->eac_MatchFunc != NULL)
|
|
{
|
|
SHOWMSG("calling match func");
|
|
|
|
/* NOTE: the order of the parameters passed to the match hook
|
|
* function can be somewhat confusing. For standard
|
|
* hook functions, the order of the parameters and the
|
|
* registers they go into is hook=A0, object=A2,
|
|
* message=A1. However, the documentation for the 'ExAll()'
|
|
* function always lists them in ascending order, that is
|
|
* hook=A0, message=A1, object=A2, which can lead to
|
|
* quite some confusion and strange errors.
|
|
*/
|
|
if(NOT CallHookPkt(ec->ec_Control->eac_MatchFunc,&type,ed))
|
|
{
|
|
SHOWMSG("does not match");
|
|
take_it = FALSE;
|
|
}
|
|
}
|
|
|
|
if(take_it)
|
|
{
|
|
SHOWMSG("registering new entry");
|
|
|
|
if(ec->ec_Last != NULL)
|
|
ec->ec_Last->ed_Next = ed;
|
|
|
|
ec->ec_Last = ed;
|
|
ec->ec_Next = (struct ExAllData *)(((ULONG)ed) + size);
|
|
ec->ec_BytesLeft -= size;
|
|
ec->ec_Control->eac_Entries++;
|
|
|
|
SHOWVALUE(ec->ec_Last->ed_Next);
|
|
SHOWVALUE(ed->ed_Name);
|
|
SHOWVALUE(ed->ed_Comment);
|
|
}
|
|
}
|
|
|
|
ec->ec_Control->eac_LastKey = (ULONG)(eof ? -1 : nextpos);
|
|
|
|
out:
|
|
|
|
ec->ec_FirstAttempt = FALSE;
|
|
|
|
RETURN(result);
|
|
return(result);
|
|
}
|
|
|
|
STATIC LONG
|
|
Action_ExamineAll(
|
|
struct FileLock * lock,
|
|
struct ExAllData * ed,
|
|
ULONG size,
|
|
ULONG type,
|
|
struct ExAllControl * eac,
|
|
LONG * error_ptr)
|
|
{
|
|
struct ExAllContext ec;
|
|
struct LockNode * ln;
|
|
LONG result = DOSFALSE;
|
|
LONG error = OK;
|
|
LONG offset;
|
|
int count;
|
|
|
|
ENTER();
|
|
|
|
SHOWVALUE(lock);
|
|
|
|
SHOWVALUE(eac->eac_LastKey);
|
|
|
|
eac->eac_Entries = 0;
|
|
|
|
if(size < sizeof(ed->ed_Next))
|
|
{
|
|
SHOWMSG("buffer is far too short.");
|
|
error = ERROR_NO_FREE_STORE;
|
|
goto out;
|
|
}
|
|
|
|
ed->ed_Next = NULL;
|
|
|
|
if(eac->eac_LastKey == (ULONG)-1)
|
|
{
|
|
SHOWMSG("scanning finished.");
|
|
error = ERROR_NO_MORE_ENTRIES;
|
|
goto out;
|
|
}
|
|
|
|
if(lock == NULL)
|
|
{
|
|
SHOWMSG("invalid lock");
|
|
error = ERROR_INVALID_LOCK;
|
|
goto out;
|
|
}
|
|
|
|
if(type < ED_NAME || type > ED_OWNER)
|
|
{
|
|
D(("type %ld not supported",type));
|
|
error = ERROR_BAD_NUMBER;
|
|
goto out;
|
|
}
|
|
|
|
SHOWVALUE(type);
|
|
|
|
memset(&ec,0,sizeof(ec));
|
|
|
|
ec.ec_Next = ed;
|
|
ec.ec_BytesLeft = size;
|
|
ec.ec_Control = eac;
|
|
ec.ec_Type = type;
|
|
ec.ec_Error = ERROR_NO_MORE_ENTRIES;
|
|
ec.ec_FirstAttempt = TRUE;
|
|
|
|
switch(type)
|
|
{
|
|
case ED_NAME:
|
|
|
|
ec.ec_MinSize = offsetof(struct ExAllData,ed_Type);
|
|
break;
|
|
|
|
case ED_TYPE:
|
|
|
|
ec.ec_MinSize = offsetof(struct ExAllData,ed_Size);
|
|
break;
|
|
|
|
case ED_SIZE:
|
|
|
|
ec.ec_MinSize = offsetof(struct ExAllData,ed_Prot);
|
|
break;
|
|
|
|
case ED_PROTECTION:
|
|
|
|
ec.ec_MinSize = offsetof(struct ExAllData,ed_Days);
|
|
break;
|
|
|
|
case ED_DATE:
|
|
|
|
ec.ec_MinSize = offsetof(struct ExAllData,ed_Comment);
|
|
break;
|
|
|
|
case ED_COMMENT:
|
|
|
|
ec.ec_MinSize = offsetof(struct ExAllData,ed_OwnerUID);
|
|
break;
|
|
|
|
case ED_OWNER:
|
|
|
|
ec.ec_MinSize = sizeof(struct ExAllData);
|
|
break;
|
|
}
|
|
|
|
SHOWVALUE(ec.ec_MinSize);
|
|
|
|
offset = eac->eac_LastKey;
|
|
|
|
ln = (struct LockNode *)lock->fl_Key;
|
|
|
|
/* Check if we should restart scanning the directory
|
|
* contents. This is tricky at best and may produce
|
|
* irritating results :(
|
|
*/
|
|
if(ln->ln_RestartExamine)
|
|
{
|
|
offset = 0;
|
|
|
|
ln->ln_RestartExamine = FALSE;
|
|
}
|
|
|
|
if(offset == 0)
|
|
{
|
|
smba_stat_t st;
|
|
|
|
SHOWMSG("first invocation");
|
|
|
|
SHOWMSG("getting file attributes");
|
|
error = smba_getattr(ln->ln_File,&st);
|
|
if(error < 0)
|
|
{
|
|
SHOWMSG("didn't work");
|
|
error = MapErrnoToIoErr(error);
|
|
eac->eac_LastKey = (ULONG)-1;
|
|
goto out;
|
|
}
|
|
|
|
if(NOT st.is_dir)
|
|
{
|
|
SHOWMSG("lock does not refer to a directory");
|
|
error = ERROR_OBJECT_WRONG_TYPE;
|
|
eac->eac_LastKey = (ULONG)-1;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
SHOWMSG("calling 'smba_readdir'");
|
|
SHOWVALUE(offset);
|
|
|
|
count = smba_readdir(ln->ln_File,offset,&ec,(smba_callback_t)dir_scan_callback_func_exall);
|
|
|
|
SHOWVALUE(count);
|
|
|
|
if(count == 0 || eac->eac_Entries == 0)
|
|
{
|
|
SHOWMSG("nothing to be read");
|
|
if(ec.ec_Error != OK)
|
|
{
|
|
SHOWMSG("flagging an error");
|
|
SHOWVALUE(ec.ec_Error);
|
|
eac->eac_LastKey = (ULONG)-1;
|
|
error = ec.ec_Error;
|
|
}
|
|
|
|
goto out;
|
|
}
|
|
else if (count == (-EIO))
|
|
{
|
|
SHOWMSG("ouch! directory read error");
|
|
eac->eac_LastKey = (ULONG)-1;
|
|
|
|
error = ERROR_NO_DEFAULT_DIR;
|
|
goto out;
|
|
}
|
|
else if (count < 0)
|
|
{
|
|
SHOWMSG("error whilst scanning");
|
|
eac->eac_LastKey = (ULONG)-1;
|
|
|
|
error = MapErrnoToIoErr(count);
|
|
goto out;
|
|
}
|
|
|
|
SHOWMSG("ok");
|
|
result = DOSTRUE;
|
|
|
|
out:
|
|
|
|
#if DEBUG
|
|
{
|
|
SHOWVALUE(eac->eac_Entries);
|
|
|
|
while(ed != NULL)
|
|
{
|
|
SHOWSTRING(ed->ed_Name);
|
|
|
|
ed = ed->ed_Next;
|
|
}
|
|
}
|
|
#endif /* DEBUG */
|
|
|
|
(*error_ptr) = error;
|
|
|
|
RETURN(result);
|
|
return(result);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
STATIC LONG
|
|
Action_Find(
|
|
LONG action,
|
|
struct FileHandle * fh,
|
|
struct FileLock * parent,
|
|
APTR bcpl_name,
|
|
LONG * error_ptr)
|
|
{
|
|
LONG result = DOSFALSE;
|
|
STRPTR parent_path = NULL;
|
|
STRPTR full_name = NULL;
|
|
LONG full_name_size;
|
|
struct FileNode * fn = NULL;
|
|
STRPTR parent_name;
|
|
UBYTE name[MAX_FILENAME_LEN];
|
|
BOOL create_new_file;
|
|
LONG error;
|
|
|
|
ENTER();
|
|
|
|
switch(action)
|
|
{
|
|
case ACTION_FINDINPUT:
|
|
D(("ACTION_FINDINPUT [Open(\"%b\",MODE_OLDFILE)]",MKBADDR(bcpl_name)));
|
|
break;
|
|
|
|
case ACTION_FINDOUTPUT:
|
|
D(("ACTION_FINDOUTPUT [Open(\"%b\",MODE_NEWFILE)]",MKBADDR(bcpl_name)));
|
|
break;
|
|
|
|
case ACTION_FINDUPDATE:
|
|
D(("ACTION_FINDUPDATE [Open(\"%b\",MODE_READWRITE)]",MKBADDR(bcpl_name)));
|
|
break;
|
|
}
|
|
|
|
SHOWVALUE(parent);
|
|
|
|
if(parent != NULL)
|
|
{
|
|
struct LockNode * ln = (struct LockNode *)parent->fl_Key;
|
|
|
|
parent_name = ln->ln_FullName;
|
|
}
|
|
else
|
|
{
|
|
parent_name = NULL;
|
|
}
|
|
|
|
ConvertBString(sizeof(name),name,bcpl_name);
|
|
|
|
if (TranslateUTF8)
|
|
{
|
|
UBYTE encoded_name[MAX_FILENAME_LEN];
|
|
int encoded_name_len;
|
|
LONG name_len = strlen(name);
|
|
|
|
encoded_name_len = encode_iso8859_1_as_utf8_string(name,name_len,NULL,0);
|
|
if(encoded_name_len < 0 || encoded_name_len >= sizeof(name))
|
|
{
|
|
error = ERROR_INVALID_COMPONENT_NAME;
|
|
goto out;
|
|
}
|
|
|
|
encode_iso8859_1_as_utf8_string(name,name_len,encoded_name,sizeof(encoded_name));
|
|
|
|
strlcpy(name,encoded_name,sizeof(name));
|
|
}
|
|
else if (TranslateNames)
|
|
{
|
|
TranslateCName(name,A2M);
|
|
}
|
|
|
|
if(IsReservedName(FilePart(name)))
|
|
{
|
|
error = ERROR_OBJECT_NOT_FOUND;
|
|
goto out;
|
|
}
|
|
|
|
error = BuildFullName(parent_name,name,&full_name,&full_name_size);
|
|
if(error != OK)
|
|
goto out;
|
|
|
|
/* Trying to open the root directory? */
|
|
if(full_name == NULL)
|
|
{
|
|
error = ERROR_OBJECT_WRONG_TYPE;
|
|
goto out;
|
|
}
|
|
|
|
fn = AllocateMemory(sizeof(*fn));
|
|
if(fn == NULL)
|
|
{
|
|
error = ERROR_NO_FREE_STORE;
|
|
goto out;
|
|
}
|
|
|
|
memset(fn,0,sizeof(*fn));
|
|
|
|
fn->fn_Handle = fh;
|
|
fn->fn_FullName = full_name;
|
|
fn->fn_Mode = (action == ACTION_FINDOUTPUT) ? EXCLUSIVE_LOCK : SHARED_LOCK;
|
|
|
|
error = CheckAccessModeCollision(full_name,fn->fn_Mode);
|
|
if(error != OK)
|
|
goto out;
|
|
|
|
SHOWSTRING(full_name);
|
|
|
|
if(action == ACTION_FINDOUTPUT)
|
|
{
|
|
/* Definitely create a new file. */
|
|
create_new_file = TRUE;
|
|
}
|
|
else if (action == ACTION_FINDINPUT)
|
|
{
|
|
/* Open an existing file for reading. */
|
|
create_new_file = FALSE;
|
|
}
|
|
else if (action == ACTION_FINDUPDATE)
|
|
{
|
|
smba_file_t * file = NULL;
|
|
smba_stat_t st;
|
|
|
|
if(smba_open(ServerData,full_name,full_name_size,&file) == OK &&
|
|
smba_getattr(file,&st) == OK)
|
|
{
|
|
/* File apparently opens Ok and information on it
|
|
* is available, don't try to replace it.
|
|
*/
|
|
create_new_file = FALSE;
|
|
}
|
|
else
|
|
{
|
|
/* We try to ignore the error here and assume
|
|
* that the remainder of the file opening
|
|
* procedure will produce a useful error
|
|
* report. In the mean time, assume that the
|
|
* file needs to be created.
|
|
*/
|
|
create_new_file = TRUE;
|
|
}
|
|
|
|
if(file != NULL)
|
|
smba_close(file);
|
|
}
|
|
else
|
|
{
|
|
/* What's that? */
|
|
error = ERROR_ACTION_NOT_KNOWN;
|
|
goto out;
|
|
}
|
|
|
|
/* Create a new file? */
|
|
if(create_new_file)
|
|
{
|
|
smba_stat_t st;
|
|
smba_file_t * dir;
|
|
STRPTR base_name;
|
|
LONG i;
|
|
|
|
if(WriteProtected)
|
|
{
|
|
error = ERROR_DISK_WRITE_PROTECTED;
|
|
goto out;
|
|
}
|
|
|
|
parent_path = AllocateMemory(strlen(full_name)+3);
|
|
if(parent_path == NULL)
|
|
{
|
|
error = ERROR_NO_FREE_STORE;
|
|
goto out;
|
|
}
|
|
|
|
strcpy(parent_path,full_name);
|
|
base_name = NULL;
|
|
for(i = strlen(parent_path)-1 ; i >= 0 ; i--)
|
|
{
|
|
if(parent_path[i] == SMB_PATH_SEPARATOR)
|
|
{
|
|
if(i == 0)
|
|
{
|
|
memmove(&parent_path[1],&parent_path[0],strlen(parent_path)+1);
|
|
i++;
|
|
}
|
|
|
|
parent_path[i] = '\0';
|
|
|
|
base_name = &parent_path[i+1];
|
|
break;
|
|
}
|
|
}
|
|
|
|
SHOWMSG("creating a file; finding parent path first");
|
|
SHOWSTRING(parent_path);
|
|
|
|
error = smba_open(ServerData,parent_path,strlen(full_name)+3,&dir);
|
|
if(error < 0)
|
|
{
|
|
error = MapErrnoToIoErr(error);
|
|
goto out;
|
|
}
|
|
|
|
/* Only one attribute counts: the file should not be write protected. */
|
|
memset(&st,0,sizeof(st));
|
|
|
|
SHOWMSG("now trying to create the file");
|
|
SHOWSTRING(base_name);
|
|
|
|
error = smba_create(dir,base_name,&st);
|
|
if(error < 0)
|
|
{
|
|
SHOWMSG("didn't work.");
|
|
SHOWVALUE(error);
|
|
|
|
smba_close(dir);
|
|
error = MapErrnoToIoErr(error);
|
|
|
|
SHOWVALUE(error);
|
|
|
|
goto out;
|
|
}
|
|
|
|
SHOWMSG("good.");
|
|
|
|
smba_close(dir);
|
|
}
|
|
|
|
/* Now for the remainder... */
|
|
error = smba_open(ServerData,full_name,full_name_size,&fn->fn_File);
|
|
if(error < 0)
|
|
{
|
|
error = MapErrnoToIoErr(error);
|
|
goto out;
|
|
}
|
|
|
|
fh->fh_Arg1 = (LONG)fn;
|
|
|
|
AddTail((struct List *)&FileList,(struct Node *)fn);
|
|
result = DOSTRUE;
|
|
|
|
out:
|
|
|
|
if(result == DOSFALSE)
|
|
{
|
|
FreeMemory(full_name);
|
|
FreeMemory(fn);
|
|
}
|
|
|
|
FreeMemory(parent_path);
|
|
|
|
(*error_ptr) = error;
|
|
|
|
RETURN(result);
|
|
return(result);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
STATIC LONG
|
|
Action_Read(
|
|
struct FileNode * fn,
|
|
APTR mem,
|
|
LONG length,
|
|
LONG * error_ptr)
|
|
{
|
|
LONG result = 0;
|
|
LONG error = OK;
|
|
|
|
ENTER();
|
|
|
|
if(length > 0)
|
|
{
|
|
result = smba_read(fn->fn_File,mem,length,fn->fn_Offset);
|
|
if(result < 0)
|
|
{
|
|
error = MapErrnoToIoErr(result);
|
|
result = -1;
|
|
goto out;
|
|
}
|
|
|
|
fn->fn_Offset += result;
|
|
}
|
|
|
|
out:
|
|
|
|
(*error_ptr) = error;
|
|
|
|
RETURN(result);
|
|
return(result);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
STATIC LONG
|
|
Action_Write(
|
|
struct FileNode * fn,
|
|
APTR mem,
|
|
LONG length,
|
|
LONG * error_ptr)
|
|
{
|
|
LONG result = DOSFALSE;
|
|
LONG error = OK;
|
|
|
|
ENTER();
|
|
|
|
if(WriteProtected)
|
|
{
|
|
error = ERROR_DISK_WRITE_PROTECTED;
|
|
goto out;
|
|
}
|
|
|
|
if(length > 0)
|
|
{
|
|
result = smba_write(fn->fn_File,mem,length,fn->fn_Offset);
|
|
if(result < 0)
|
|
{
|
|
error = MapErrnoToIoErr(result);
|
|
result = -1;
|
|
goto out;
|
|
}
|
|
|
|
fn->fn_Offset += result;
|
|
}
|
|
|
|
out:
|
|
|
|
(*error_ptr) = error;
|
|
|
|
RETURN(result);
|
|
return(result);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
STATIC LONG
|
|
Action_End(
|
|
struct FileNode * fn,
|
|
LONG * error_ptr)
|
|
{
|
|
Remove((struct Node *)fn);
|
|
|
|
smba_close(fn->fn_File);
|
|
FreeMemory(fn->fn_FullName);
|
|
FreeMemory(fn);
|
|
|
|
(*error_ptr) = OK;
|
|
return(DOSTRUE);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
STATIC LONG
|
|
Action_Seek(
|
|
struct FileNode * fn,
|
|
LONG position,
|
|
LONG mode,
|
|
LONG * error_ptr)
|
|
{
|
|
LONG previous_position = fn->fn_Offset;
|
|
LONG result = -1;
|
|
LONG offset;
|
|
LONG error;
|
|
|
|
ENTER();
|
|
|
|
/* olsen: This doesn't really work with Microsoft SMB servers, but it works with Samba. */
|
|
#if 0
|
|
{
|
|
switch(mode)
|
|
{
|
|
case OFFSET_BEGINNING:
|
|
|
|
mode = 0;
|
|
break;
|
|
|
|
case OFFSET_CURRENT:
|
|
|
|
mode = 1;
|
|
break;
|
|
|
|
case OFFSET_END:
|
|
|
|
mode = 2;
|
|
break;
|
|
|
|
default:
|
|
|
|
error = ERROR_ACTION_NOT_KNOWN;
|
|
goto out;
|
|
}
|
|
|
|
error = smba_seek (fn->fn_File, position, mode, (off_t *) &offset);
|
|
if(error < 0)
|
|
{
|
|
error = MapErrnoToIoErr(error);
|
|
goto out;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* olsen: This is the original implementation. */
|
|
#if 0
|
|
{
|
|
smba_stat_t st;
|
|
|
|
error = smba_getattr(fn->fn_File,&st);
|
|
if(error < 0)
|
|
{
|
|
error = MapErrnoToIoErr(error);
|
|
goto out;
|
|
}
|
|
|
|
offset = fn->fn_Offset;
|
|
|
|
switch(mode)
|
|
{
|
|
case OFFSET_BEGINNING:
|
|
|
|
offset = position;
|
|
break;
|
|
|
|
case OFFSET_CURRENT:
|
|
|
|
offset += position;
|
|
break;
|
|
|
|
case OFFSET_END:
|
|
|
|
offset = st.size + position;
|
|
break;
|
|
|
|
default:
|
|
|
|
error = ERROR_ACTION_NOT_KNOWN;
|
|
goto out;
|
|
}
|
|
|
|
if(offset < 0 || offset > st.size)
|
|
{
|
|
error = ERROR_SEEK_ERROR;
|
|
goto out;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* olsen: This is a mix of the two above. First we calculate the absolute
|
|
* position, then seek to that position. The SMB server is supposed
|
|
* to do its housekeeping before the position is changed. I wish this
|
|
* worked differently, but it seems we've got the best of both worlds
|
|
* here...
|
|
*/
|
|
#if 1
|
|
{
|
|
smba_stat_t st;
|
|
|
|
switch(mode)
|
|
{
|
|
case OFFSET_BEGINNING:
|
|
|
|
offset = position;
|
|
break;
|
|
|
|
case OFFSET_CURRENT:
|
|
|
|
offset = fn->fn_Offset + position;
|
|
break;
|
|
|
|
case OFFSET_END:
|
|
|
|
error = smba_getattr(fn->fn_File,&st);
|
|
if(error < 0)
|
|
{
|
|
error = MapErrnoToIoErr(error);
|
|
goto out;
|
|
}
|
|
|
|
offset = st.size + position;
|
|
break;
|
|
|
|
default:
|
|
|
|
error = ERROR_ACTION_NOT_KNOWN;
|
|
goto out;
|
|
}
|
|
|
|
if(offset < 0)
|
|
{
|
|
error = ERROR_SEEK_ERROR;
|
|
goto out;
|
|
}
|
|
|
|
error = smba_seek (fn->fn_File, offset, 0, (off_t *) &offset);
|
|
if(error < 0)
|
|
{
|
|
error = MapErrnoToIoErr(error);
|
|
goto out;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
error = OK;
|
|
|
|
fn->fn_Offset = offset;
|
|
|
|
result = previous_position;
|
|
|
|
out:
|
|
|
|
(*error_ptr) = error;
|
|
|
|
RETURN(result);
|
|
return(result);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
STATIC LONG
|
|
Action_SetFileSize(
|
|
struct FileNode * fn,
|
|
LONG position,
|
|
LONG mode,
|
|
LONG * error_ptr)
|
|
{
|
|
smba_stat_t st;
|
|
LONG result = -1;
|
|
LONG error;
|
|
long offset;
|
|
|
|
ENTER();
|
|
|
|
if(WriteProtected)
|
|
{
|
|
error = ERROR_DISK_WRITE_PROTECTED;
|
|
goto out;
|
|
}
|
|
|
|
error = smba_getattr(fn->fn_File,&st);
|
|
if(error < 0)
|
|
{
|
|
error = MapErrnoToIoErr(error);
|
|
goto out;
|
|
}
|
|
|
|
offset = fn->fn_Offset;
|
|
|
|
switch(mode)
|
|
{
|
|
case OFFSET_BEGINNING:
|
|
|
|
offset = position;
|
|
break;
|
|
|
|
case OFFSET_CURRENT:
|
|
|
|
offset += position;
|
|
break;
|
|
|
|
case OFFSET_END:
|
|
|
|
offset = st.size + position;
|
|
break;
|
|
|
|
default:
|
|
|
|
error = ERROR_ACTION_NOT_KNOWN;
|
|
goto out;
|
|
}
|
|
|
|
if(offset < 0)
|
|
{
|
|
error = ERROR_SEEK_ERROR;
|
|
goto out;
|
|
}
|
|
|
|
st.atime = -1;
|
|
st.ctime = -1;
|
|
st.mtime = -1;
|
|
st.size = offset;
|
|
|
|
error = smba_setattr(fn->fn_File,&st);
|
|
if(error < 0)
|
|
{
|
|
error = MapErrnoToIoErr(error);
|
|
goto out;
|
|
}
|
|
|
|
if(fn->fn_Offset > offset)
|
|
fn->fn_Offset = offset;
|
|
|
|
result = offset;
|
|
|
|
out:
|
|
|
|
(*error_ptr) = error;
|
|
|
|
RETURN(result);
|
|
return(result);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
STATIC LONG
|
|
Action_SetDate(
|
|
struct FileLock * parent,
|
|
APTR bcpl_name,
|
|
struct DateStamp * ds,
|
|
LONG * error_ptr)
|
|
{
|
|
LONG result = DOSFALSE;
|
|
STRPTR full_name = NULL;
|
|
LONG full_name_size;
|
|
smba_file_t * file = NULL;
|
|
STRPTR parent_name;
|
|
UBYTE name[MAX_FILENAME_LEN];
|
|
smba_stat_t st;
|
|
LONG seconds;
|
|
LONG error;
|
|
|
|
ENTER();
|
|
|
|
if(WriteProtected)
|
|
{
|
|
error = ERROR_DISK_WRITE_PROTECTED;
|
|
goto out;
|
|
}
|
|
|
|
SHOWVALUE(parent);
|
|
|
|
if(parent != NULL)
|
|
{
|
|
struct LockNode * ln = (struct LockNode *)parent->fl_Key;
|
|
|
|
parent_name = ln->ln_FullName;
|
|
}
|
|
else
|
|
{
|
|
parent_name = NULL;
|
|
}
|
|
|
|
ConvertBString(sizeof(name),name,bcpl_name);
|
|
|
|
if (TranslateUTF8)
|
|
{
|
|
UBYTE encoded_name[MAX_FILENAME_LEN];
|
|
int encoded_name_len;
|
|
LONG name_len = strlen(name);
|
|
|
|
encoded_name_len = encode_iso8859_1_as_utf8_string(name,name_len,NULL,0);
|
|
if(encoded_name_len < 0 || encoded_name_len >= sizeof(name))
|
|
{
|
|
error = ERROR_INVALID_COMPONENT_NAME;
|
|
goto out;
|
|
}
|
|
|
|
encode_iso8859_1_as_utf8_string(name,name_len,encoded_name,sizeof(encoded_name));
|
|
|
|
strlcpy(name,encoded_name,sizeof(name));
|
|
}
|
|
else if (TranslateNames)
|
|
{
|
|
TranslateCName(name,A2M);
|
|
}
|
|
|
|
error = BuildFullName(parent_name,name,&full_name,&full_name_size);
|
|
if(error != OK)
|
|
goto out;
|
|
|
|
/* Trying to change the date of the root directory? */
|
|
if(full_name == NULL)
|
|
{
|
|
error = ERROR_OBJECT_IN_USE;
|
|
goto out;
|
|
}
|
|
|
|
SHOWSTRING(full_name);
|
|
|
|
error = smba_open(ServerData,full_name,full_name_size,&file);
|
|
if(error < 0)
|
|
{
|
|
error = MapErrnoToIoErr(error);
|
|
goto out;
|
|
}
|
|
|
|
error = smba_getattr(file,&st);
|
|
if(error < 0)
|
|
{
|
|
error = MapErrnoToIoErr(error);
|
|
goto out;
|
|
}
|
|
|
|
seconds = (ds->ds_Days * 24 * 60 + ds->ds_Minute) * 60 + (ds->ds_Tick / TICKS_PER_SECOND);
|
|
|
|
st.atime = -1;
|
|
st.ctime = -1;
|
|
st.mtime = seconds + UNIX_TIME_OFFSET + GetTimeZoneDelta();
|
|
st.size = -1;
|
|
|
|
error = smba_setattr(file,&st);
|
|
if(error < 0)
|
|
{
|
|
error = MapErrnoToIoErr(error);
|
|
goto out;
|
|
}
|
|
|
|
result = DOSTRUE;
|
|
|
|
out:
|
|
|
|
FreeMemory(full_name);
|
|
|
|
if(file != NULL)
|
|
smba_close(file);
|
|
|
|
(*error_ptr) = error;
|
|
|
|
RETURN(result);
|
|
return(result);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
STATIC LONG
|
|
Action_ExamineFH(
|
|
struct FileNode * fn,
|
|
struct FileInfoBlock * fib,
|
|
LONG * error_ptr)
|
|
{
|
|
LONG result = DOSFALSE;
|
|
smba_stat_t st;
|
|
LONG error;
|
|
LONG seconds;
|
|
STRPTR name;
|
|
LONG name_len;
|
|
LONG i;
|
|
|
|
ENTER();
|
|
|
|
error = smba_getattr(fn->fn_File,&st);
|
|
if(error < 0)
|
|
{
|
|
error = MapErrnoToIoErr(error);
|
|
goto out;
|
|
}
|
|
|
|
name = fn->fn_FullName;
|
|
name_len = strlen(name);
|
|
|
|
for(i = name_len ; i >= 0 ; i--)
|
|
{
|
|
if(name[i] == SMB_PATH_SEPARATOR)
|
|
{
|
|
name = &name[i+1];
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Just checking: will the name fit? */
|
|
if(name_len >= sizeof(fib->fib_FileName))
|
|
{
|
|
error = ERROR_INVALID_COMPONENT_NAME;
|
|
goto out;
|
|
}
|
|
|
|
memset(fib,0,sizeof(*fib));
|
|
|
|
if(TranslateUTF8)
|
|
{
|
|
UBYTE decoded_name[MAX_FILENAME_LEN];
|
|
int decoded_name_len;
|
|
|
|
decoded_name_len = decode_utf8_as_iso8859_1_string(name,name_len,NULL,0);
|
|
if(decoded_name_len < 0 || decoded_name_len >= MAX_FILENAME_LEN)
|
|
{
|
|
error = ERROR_INVALID_COMPONENT_NAME;
|
|
goto out;
|
|
}
|
|
|
|
decoded_name_len = decode_utf8_as_iso8859_1_string(name,name_len,decoded_name,sizeof(decoded_name));
|
|
|
|
fib->fib_FileName[0] = decoded_name_len;
|
|
memcpy(&fib->fib_FileName[1],decoded_name,decoded_name_len);
|
|
}
|
|
else
|
|
{
|
|
ConvertCString(fib->fib_FileName,sizeof(fib->fib_FileName),name,name_len);
|
|
|
|
if(TranslateNames)
|
|
TranslateBName(fib->fib_FileName,M2A);
|
|
}
|
|
|
|
fib->fib_DirEntryType = ST_FILE;
|
|
fib->fib_EntryType = ST_FILE;
|
|
fib->fib_NumBlocks = (st.size + 511) / 512;
|
|
fib->fib_Size = st.size;
|
|
fib->fib_DiskKey = -1;
|
|
|
|
fib->fib_Protection = FIBF_OTR_READ|FIBF_OTR_EXECUTE|FIBF_OTR_WRITE|FIBF_OTR_DELETE|
|
|
FIBF_GRP_READ|FIBF_GRP_EXECUTE|FIBF_GRP_WRITE|FIBF_GRP_DELETE;
|
|
|
|
if(st.is_wp)
|
|
fib->fib_Protection ^= (FIBF_OTR_WRITE|FIBF_OTR_DELETE|FIBF_GRP_WRITE|FIBF_GRP_DELETE|FIBF_WRITE|FIBF_DELETE);
|
|
|
|
/* Careful: the 'archive' attribute has exactly the opposite
|
|
* meaning in the Amiga and the SMB worlds.
|
|
*/
|
|
if(NOT st.is_archive)
|
|
fib->fib_Protection |= FIBF_ARCHIVE;
|
|
|
|
if(st.is_system)
|
|
fib->fib_Protection |= FIBF_PURE;
|
|
|
|
/* If modification time is 0 use creation time instead (cyfm 2009-03-18). */
|
|
seconds = (st.mtime == 0 ? st.ctime : st.mtime) - UNIX_TIME_OFFSET - GetTimeZoneDelta();
|
|
if(seconds < 0)
|
|
seconds = 0;
|
|
|
|
fib->fib_Date.ds_Days = (seconds / (24 * 60 * 60));
|
|
fib->fib_Date.ds_Minute = (seconds % (24 * 60 * 60)) / 60;
|
|
fib->fib_Date.ds_Tick = (seconds % 60) * TICKS_PER_SECOND;
|
|
|
|
result = DOSTRUE;
|
|
|
|
out:
|
|
|
|
(*error_ptr) = error;
|
|
|
|
RETURN(result);
|
|
return(result);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
STATIC BPTR
|
|
Action_ParentFH(
|
|
struct FileNode * fn,
|
|
LONG * error_ptr)
|
|
{
|
|
BPTR result = ZERO;
|
|
struct LockNode * ln = NULL;
|
|
LONG error;
|
|
STRPTR full_name;
|
|
LONG full_name_size;
|
|
LONG i;
|
|
|
|
ENTER();
|
|
|
|
full_name_size = strlen(fn->fn_FullName)+3;
|
|
if(full_name_size < SMB_MAXNAMELEN+1)
|
|
full_name_size = SMB_MAXNAMELEN+1;
|
|
|
|
full_name = AllocateMemory(full_name_size);
|
|
if(full_name == NULL)
|
|
{
|
|
error = ERROR_NO_FREE_STORE;
|
|
goto out;
|
|
}
|
|
|
|
strcpy(full_name,fn->fn_FullName);
|
|
|
|
for(i = strlen(full_name)-1 ; i >= 0 ; i--)
|
|
{
|
|
if(i == 0)
|
|
{
|
|
strcpy(full_name,SMB_ROOT_DIR_NAME);
|
|
break;
|
|
}
|
|
else if (full_name[i] == SMB_PATH_SEPARATOR)
|
|
{
|
|
full_name[i] = '\0';
|
|
break;
|
|
}
|
|
}
|
|
|
|
ln = AllocateMemory(sizeof(*ln));
|
|
if(ln == NULL)
|
|
{
|
|
error = ERROR_NO_FREE_STORE;
|
|
goto out;
|
|
}
|
|
|
|
memset(ln,0,sizeof(*ln));
|
|
|
|
ln->ln_FileLock.fl_Key = (LONG)ln;
|
|
ln->ln_FileLock.fl_Access = SHARED_LOCK;
|
|
ln->ln_FileLock.fl_Task = FileSystemPort;
|
|
ln->ln_FileLock.fl_Volume = MKBADDR(VolumeNode);
|
|
ln->ln_FullName = full_name;
|
|
|
|
SHOWSTRING(full_name);
|
|
|
|
error = smba_open(ServerData,full_name,full_name_size,&ln->ln_File);
|
|
if(error < 0)
|
|
{
|
|
error = MapErrnoToIoErr(error);
|
|
goto out;
|
|
}
|
|
|
|
AddTail((struct List *)&LockList,(struct Node *)ln);
|
|
result = MKBADDR(&ln->ln_FileLock);
|
|
SHOWVALUE(&ln->ln_FileLock);
|
|
|
|
out:
|
|
|
|
if(result == ZERO)
|
|
{
|
|
FreeMemory(ln);
|
|
FreeMemory(full_name);
|
|
}
|
|
|
|
(*error_ptr) = error;
|
|
|
|
RETURN(result);
|
|
return(result);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
STATIC BPTR
|
|
Action_CopyDirFH(
|
|
struct FileNode * fn,
|
|
LONG * error_ptr)
|
|
{
|
|
BPTR result = ZERO;
|
|
struct LockNode * ln = NULL;
|
|
STRPTR full_name = NULL;
|
|
LONG full_name_size;
|
|
LONG error;
|
|
|
|
ENTER();
|
|
|
|
if(fn->fn_Mode != SHARED_LOCK)
|
|
{
|
|
error = ERROR_OBJECT_IN_USE;
|
|
goto out;
|
|
}
|
|
|
|
full_name_size = strlen(fn->fn_FullName)+3;
|
|
if(full_name_size < SMB_MAXNAMELEN+1)
|
|
full_name_size = SMB_MAXNAMELEN+1;
|
|
|
|
full_name = AllocateMemory(full_name_size);
|
|
if(full_name == NULL)
|
|
{
|
|
error = ERROR_NO_FREE_STORE;
|
|
goto out;
|
|
}
|
|
|
|
strcpy(full_name,fn->fn_FullName);
|
|
|
|
ln = AllocateMemory(sizeof(*ln));
|
|
if(ln == NULL)
|
|
{
|
|
error = ERROR_NO_FREE_STORE;
|
|
goto out;
|
|
}
|
|
|
|
memset(ln,0,sizeof(*ln));
|
|
|
|
ln->ln_FileLock.fl_Key = (LONG)ln;
|
|
ln->ln_FileLock.fl_Access = SHARED_LOCK;
|
|
ln->ln_FileLock.fl_Task = FileSystemPort;
|
|
ln->ln_FileLock.fl_Volume = MKBADDR(VolumeNode);
|
|
ln->ln_FullName = full_name;
|
|
|
|
SHOWSTRING(full_name);
|
|
|
|
error = smba_open(ServerData,full_name,full_name_size,&ln->ln_File);
|
|
if(error < 0)
|
|
{
|
|
error = MapErrnoToIoErr(error);
|
|
goto out;
|
|
}
|
|
|
|
AddTail((struct List *)&LockList,(struct Node *)ln);
|
|
result = MKBADDR(&ln->ln_FileLock);
|
|
SHOWVALUE(&ln->ln_FileLock);
|
|
|
|
out:
|
|
|
|
if(result == ZERO)
|
|
{
|
|
FreeMemory(ln);
|
|
FreeMemory(full_name);
|
|
}
|
|
|
|
(*error_ptr) = error;
|
|
|
|
RETURN(result);
|
|
return(result);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
STATIC LONG
|
|
Action_FHFromLock(
|
|
struct FileHandle * fh,
|
|
struct FileLock * fl,
|
|
LONG * error_ptr)
|
|
{
|
|
LONG result = DOSFALSE;
|
|
struct FileNode * fn;
|
|
struct LockNode * ln;
|
|
LONG error = OK;
|
|
|
|
ENTER();
|
|
|
|
SHOWVALUE(fl);
|
|
|
|
fn = AllocateMemory(sizeof(*fn));
|
|
if(fn == NULL)
|
|
{
|
|
error = ERROR_NO_FREE_STORE;
|
|
goto out;
|
|
}
|
|
|
|
memset(fn,0,sizeof(*fn));
|
|
|
|
ln = (struct LockNode *)fl->fl_Key;
|
|
|
|
fn->fn_Handle = fh;
|
|
fn->fn_FullName = ln->ln_FullName;
|
|
fn->fn_File = ln->ln_File;
|
|
fn->fn_Mode = fl->fl_Access;
|
|
|
|
Remove((struct Node *)ln);
|
|
FreeMemory(ln);
|
|
|
|
fh->fh_Arg1 = (LONG)fn;
|
|
|
|
AddTail((struct List *)&FileList,(struct Node *)fn);
|
|
result = DOSTRUE;
|
|
|
|
out:
|
|
|
|
(*error_ptr) = error;
|
|
|
|
RETURN(result);
|
|
return(result);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
STATIC LONG
|
|
Action_RenameDisk(
|
|
APTR bcpl_name,
|
|
LONG * error_ptr)
|
|
{
|
|
LONG result = DOSFALSE;
|
|
LONG error = OK;
|
|
STRPTR old_name;
|
|
STRPTR new_name;
|
|
UBYTE * name;
|
|
LONG len;
|
|
|
|
ENTER();
|
|
|
|
if(NOT VolumeNodeAdded)
|
|
{
|
|
error = ERROR_OBJECT_IN_USE;
|
|
goto out;
|
|
}
|
|
|
|
if(WriteProtected)
|
|
{
|
|
error = ERROR_DISK_WRITE_PROTECTED;
|
|
goto out;
|
|
}
|
|
|
|
/* Now for the really interesting part; the new name
|
|
* is to be a NUL-terminated BCPL string, and as such
|
|
* must be allocated via AllocVec().
|
|
*/
|
|
|
|
name = bcpl_name;
|
|
|
|
len = name[0];
|
|
|
|
new_name = AllocVec(1 + len + 1,MEMF_ANY|MEMF_PUBLIC);
|
|
if(new_name == NULL)
|
|
{
|
|
error = ERROR_NO_FREE_STORE;
|
|
goto out;
|
|
}
|
|
|
|
new_name[0] = len;
|
|
memcpy(&new_name[1],&name[1],len);
|
|
new_name[len+1] = '\0';
|
|
|
|
Forbid();
|
|
|
|
old_name = BADDR(VolumeNode->dol_Name);
|
|
VolumeNode->dol_Name = MKBADDR(new_name);
|
|
|
|
Permit();
|
|
|
|
FreeVec(old_name);
|
|
|
|
SendDiskChange(IECLASS_DISKINSERTED);
|
|
|
|
result = DOSTRUE;
|
|
|
|
out:
|
|
|
|
(*error_ptr) = error;
|
|
|
|
RETURN(result);
|
|
return(result);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
STATIC LONG
|
|
Action_ChangeMode(
|
|
LONG type,
|
|
APTR object,
|
|
LONG new_mode,
|
|
LONG * error_ptr)
|
|
{
|
|
LONG result = DOSFALSE;
|
|
struct FileLock * fl = NULL;
|
|
struct FileNode * fn = NULL;
|
|
struct LockNode * ln = NULL;
|
|
STRPTR name;
|
|
LONG old_mode;
|
|
LONG error = OK;
|
|
|
|
ENTER();
|
|
|
|
/* Sanity check; verify parameters */
|
|
if((type != CHANGE_LOCK && type != CHANGE_FH) ||
|
|
(new_mode != EXCLUSIVE_LOCK && new_mode != SHARED_LOCK))
|
|
{
|
|
error = ERROR_ACTION_NOT_KNOWN;
|
|
goto out;
|
|
}
|
|
|
|
/* Now obtain the data structures, name and mode
|
|
* associated with the object in question.
|
|
*/
|
|
if(type == CHANGE_LOCK)
|
|
{
|
|
fl = object;
|
|
ln = (struct LockNode *)fl->fl_Key;
|
|
name = ln->ln_FullName;
|
|
old_mode = fl->fl_Access;
|
|
}
|
|
else
|
|
{
|
|
struct FileHandle * fh = object;
|
|
|
|
fn = (struct FileNode *)fh->fh_Arg1;
|
|
name = fn->fn_FullName;
|
|
old_mode = fn->fn_Mode;
|
|
}
|
|
|
|
/* Do we need to change anything at all? */
|
|
if(new_mode == old_mode)
|
|
{
|
|
result = DOSTRUE;
|
|
goto out;
|
|
}
|
|
|
|
/* This is the easiest case; change an
|
|
* exclusive access mode to a shared
|
|
* access mode. Since the original mode
|
|
* can be used by one object only,
|
|
* we get away by updating the mode
|
|
* value.
|
|
*/
|
|
if(new_mode == SHARED_LOCK)
|
|
{
|
|
if(type == CHANGE_LOCK)
|
|
fl->fl_Access = new_mode;
|
|
else
|
|
fn->fn_Mode = new_mode;
|
|
|
|
result = DOSTRUE;
|
|
goto out;
|
|
}
|
|
|
|
/* Is there another shared access lock
|
|
* which refers to the same object?
|
|
*/
|
|
if(FindLockNode(name,ln) != NULL)
|
|
{
|
|
error = ERROR_OBJECT_IN_USE;
|
|
goto out;
|
|
}
|
|
|
|
/* Is there another shared access file
|
|
* which refers to the same object?
|
|
*/
|
|
if(FindFileNode(name,fn) != NULL)
|
|
{
|
|
error = ERROR_OBJECT_IN_USE;
|
|
goto out;
|
|
}
|
|
|
|
/* There is just one single reference
|
|
* to this object; change the mode
|
|
* and quit.
|
|
*/
|
|
if(type == CHANGE_LOCK)
|
|
fl->fl_Access = new_mode;
|
|
else
|
|
fn->fn_Mode = new_mode;
|
|
|
|
result = DOSTRUE;
|
|
|
|
out:
|
|
|
|
(*error_ptr) = error;
|
|
|
|
RETURN(result);
|
|
return(result);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
STATIC LONG
|
|
Action_WriteProtect(
|
|
LONG flag,
|
|
ULONG key,
|
|
LONG * error_ptr)
|
|
{
|
|
LONG result = DOSFALSE;
|
|
LONG error = OK;
|
|
|
|
ENTER();
|
|
|
|
if(flag == DOSFALSE)
|
|
{
|
|
if(WriteProtected)
|
|
{
|
|
if(key != WriteProtectKey)
|
|
{
|
|
error = ERROR_INVALID_LOCK;
|
|
goto out;
|
|
}
|
|
|
|
WriteProtected = FALSE;
|
|
|
|
if(VolumeNodeAdded)
|
|
{
|
|
SendDiskChange(IECLASS_DISKREMOVED);
|
|
SendDiskChange(IECLASS_DISKINSERTED);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(NOT WriteProtected)
|
|
{
|
|
WriteProtected = TRUE;
|
|
WriteProtectKey = key;
|
|
|
|
if(VolumeNodeAdded)
|
|
{
|
|
SendDiskChange(IECLASS_DISKREMOVED);
|
|
SendDiskChange(IECLASS_DISKINSERTED);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
error = ERROR_INVALID_LOCK;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
result = DOSTRUE;
|
|
|
|
out:
|
|
|
|
(*error_ptr) = error;
|
|
|
|
RETURN(result);
|
|
return(result);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
STATIC LONG
|
|
Action_MoreCache(
|
|
LONG buffer_delta,
|
|
LONG * error_ptr)
|
|
{
|
|
LONG result;
|
|
int old_size;
|
|
|
|
ENTER();
|
|
|
|
old_size = smba_get_dircache_size(ServerData);
|
|
|
|
result = smba_change_dircache_size(ServerData,old_size + buffer_delta);
|
|
|
|
if(result == old_size && buffer_delta != 0)
|
|
{
|
|
result = DOSFALSE;
|
|
(*error_ptr) = ERROR_NO_FREE_STORE;
|
|
}
|
|
|
|
RETURN(result);
|
|
return(result);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
STATIC LONG
|
|
Action_SetComment(
|
|
struct FileLock * parent,
|
|
APTR bcpl_name,
|
|
APTR bcpl_comment,
|
|
LONG * error_ptr)
|
|
{
|
|
LONG result = DOSFALSE;
|
|
STRPTR full_name = NULL;
|
|
LONG full_name_size;
|
|
smba_file_t * file = NULL;
|
|
STRPTR parent_name;
|
|
UBYTE name[MAX_FILENAME_LEN];
|
|
UBYTE comment[80];
|
|
LONG error;
|
|
|
|
ENTER();
|
|
|
|
if(WriteProtected)
|
|
{
|
|
error = ERROR_DISK_WRITE_PROTECTED;
|
|
goto out;
|
|
}
|
|
|
|
SHOWVALUE(parent);
|
|
|
|
if(parent != NULL)
|
|
{
|
|
struct LockNode * ln = (struct LockNode *)parent->fl_Key;
|
|
|
|
parent_name = ln->ln_FullName;
|
|
}
|
|
else
|
|
{
|
|
parent_name = NULL;
|
|
}
|
|
|
|
ConvertBString(sizeof(name),name,bcpl_name);
|
|
|
|
if (TranslateUTF8)
|
|
{
|
|
UBYTE encoded_name[MAX_FILENAME_LEN];
|
|
int encoded_name_len;
|
|
LONG name_len = strlen(name);
|
|
|
|
encoded_name_len = encode_iso8859_1_as_utf8_string(name,name_len,NULL,0);
|
|
if(encoded_name_len < 0 || encoded_name_len >= sizeof(name))
|
|
{
|
|
error = ERROR_INVALID_COMPONENT_NAME;
|
|
goto out;
|
|
}
|
|
|
|
encode_iso8859_1_as_utf8_string(name,name_len,encoded_name,sizeof(encoded_name));
|
|
|
|
strlcpy(name,encoded_name,sizeof(name));
|
|
}
|
|
else if (TranslateNames)
|
|
{
|
|
TranslateCName(name,A2M);
|
|
}
|
|
|
|
error = BuildFullName(parent_name,name,&full_name,&full_name_size);
|
|
if(error != OK)
|
|
goto out;
|
|
|
|
/* Trying to change the comment of the root directory? */
|
|
if(full_name == NULL)
|
|
{
|
|
error = ERROR_OBJECT_IN_USE;
|
|
goto out;
|
|
}
|
|
|
|
SHOWSTRING(full_name);
|
|
|
|
error = smba_open(ServerData,full_name,full_name_size,&file);
|
|
if(error < 0)
|
|
{
|
|
error = MapErrnoToIoErr(error);
|
|
goto out;
|
|
}
|
|
|
|
ConvertBString(sizeof(comment),comment,bcpl_comment);
|
|
|
|
SHOWSTRING(comment);
|
|
|
|
/* All this work and we're only doing something very silly... */
|
|
if(strlen(comment) > 0)
|
|
{
|
|
error = ERROR_COMMENT_TOO_BIG;
|
|
goto out;
|
|
}
|
|
|
|
result = DOSTRUE;
|
|
|
|
out:
|
|
|
|
FreeMemory(full_name);
|
|
|
|
if(file != NULL)
|
|
smba_close(file);
|
|
|
|
(*error_ptr) = error;
|
|
|
|
RETURN(result);
|
|
return(result);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
STATIC LONG
|
|
Action_LockRecord (
|
|
struct FileNode * fn,
|
|
LONG offset,
|
|
LONG length,
|
|
LONG mode,
|
|
ULONG timeout,
|
|
LONG * error_ptr)
|
|
{
|
|
LONG result = DOSFALSE;
|
|
LONG error;
|
|
LONG umode;
|
|
|
|
/* Sanity checks... */
|
|
if (mode < REC_EXCLUSIVE || mode > REC_SHARED_IMMED)
|
|
{
|
|
error = ERROR_ACTION_NOT_KNOWN;
|
|
goto out;
|
|
}
|
|
|
|
/* Invalid offset, size or integer overflow? */
|
|
if (offset < 0 || length <= 0 || offset + length < offset)
|
|
{
|
|
error = ERROR_LOCK_COLLISION;
|
|
goto out;
|
|
}
|
|
|
|
if ((mode == REC_SHARED) || (mode == REC_SHARED_IMMED))
|
|
umode = 1;
|
|
else
|
|
umode = 0;
|
|
|
|
if ((mode == REC_SHARED_IMMED) || (mode == REC_EXCLUSIVE_IMMED))
|
|
timeout = 0;
|
|
|
|
if (timeout > 0)
|
|
{
|
|
if (timeout > 214748364)
|
|
timeout = ~0UL; /* wait forever */
|
|
else
|
|
timeout *= 20; /* milliseconds instead of Ticks */
|
|
}
|
|
|
|
error = smba_lockrec (fn->fn_File, offset, length, umode, 0, (long)timeout);
|
|
if(error < 0)
|
|
{
|
|
error = MapErrnoToIoErr(error);
|
|
goto out;
|
|
}
|
|
|
|
result = DOSTRUE;
|
|
|
|
out:
|
|
|
|
(*error_ptr) = error;
|
|
|
|
RETURN(result);
|
|
return(result);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
STATIC LONG
|
|
Action_FreeRecord (
|
|
struct FileNode * fn,
|
|
LONG offset,
|
|
LONG length,
|
|
LONG * error_ptr)
|
|
{
|
|
LONG result = DOSFALSE;
|
|
LONG error;
|
|
|
|
/* Sanity checks... */
|
|
if(offset < 0 || length <= 0 || offset + length < offset)
|
|
{
|
|
error = ERROR_RECORD_NOT_LOCKED;
|
|
goto out;
|
|
}
|
|
|
|
error = smba_lockrec (fn->fn_File, offset, length, 2, -1, 0);
|
|
if (error < 0)
|
|
{
|
|
error = MapErrnoToIoErr(error);
|
|
goto out;
|
|
}
|
|
|
|
result = DOSTRUE;
|
|
|
|
out:
|
|
|
|
(*error_ptr) = error;
|
|
|
|
RETURN(result);
|
|
return(result);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
STATIC VOID
|
|
HandleFileSystem(STRPTR device_name,STRPTR volume_name,STRPTR service_name)
|
|
{
|
|
struct Process * this_process = (struct Process *)FindTask(NULL);
|
|
BOOL sign_off = FALSE;
|
|
BYTE old_priority;
|
|
ULONG signals;
|
|
BOOL done;
|
|
|
|
ENTER();
|
|
|
|
DisplayErrorList();
|
|
|
|
if(NOT Quiet && WBStartup == NULL)
|
|
{
|
|
struct CommandLineInterface * cli;
|
|
|
|
cli = Cli();
|
|
if(NOT cli->cli_Background)
|
|
{
|
|
UBYTE name[MAX_FILENAME_LEN];
|
|
LONG max_cli;
|
|
LONG which;
|
|
LONG i;
|
|
|
|
Forbid();
|
|
|
|
which = max_cli = MaxCli();
|
|
|
|
for(i = 1 ; i <= max_cli ; i++)
|
|
{
|
|
if(FindCliProc(i) == this_process)
|
|
{
|
|
which = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
Permit();
|
|
|
|
if(volume_name == NULL)
|
|
strlcpy(name,device_name,sizeof(name));
|
|
else
|
|
strlcpy(name,volume_name,sizeof(name));
|
|
|
|
for(i = strlen(name)-1 ; i >= 0 ; i--)
|
|
{
|
|
if(name[i] == ':')
|
|
name[i] = '\0';
|
|
else
|
|
break;
|
|
}
|
|
|
|
LocalPrintf("Connected '%s' to '%s:'; \"Break %ld\" or [Ctrl-C] to stop... ",
|
|
service_name,name,which);
|
|
|
|
Flush(Output());
|
|
|
|
sign_off = TRUE;
|
|
}
|
|
}
|
|
|
|
Quiet = TRUE;
|
|
|
|
done = FALSE;
|
|
|
|
/* Raise the Task priority of the file system to 10
|
|
* unless it already running at priority 10 or higher.
|
|
*/
|
|
Forbid();
|
|
|
|
old_priority = this_process->pr_Task.tc_Node.ln_Pri;
|
|
if(old_priority < 10)
|
|
SetTaskPri((struct Task *)this_process, 10);
|
|
|
|
Permit();
|
|
|
|
do
|
|
{
|
|
signals = Wait(SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_F | (1UL << FileSystemPort->mp_SigBit));
|
|
|
|
if(signals & (1UL << FileSystemPort->mp_SigBit))
|
|
{
|
|
struct DosPacket * dp;
|
|
struct Message * mn;
|
|
LONG res1,res2;
|
|
|
|
while((mn = GetMsg(FileSystemPort)) != NULL)
|
|
{
|
|
dp = (struct DosPacket *)mn->mn_Node.ln_Name;
|
|
|
|
D(("got packet; sender '%s'",((struct Node *)dp->dp_Port->mp_SigTask)->ln_Name));
|
|
|
|
res2 = 0;
|
|
|
|
switch(dp->dp_Action)
|
|
{
|
|
case ACTION_DIE:
|
|
|
|
SHOWMSG("ACTION_DIE");
|
|
if(IsListEmpty((struct List *)&FileList) && IsListEmpty((struct List *)&LockList))
|
|
{
|
|
SHOWMSG("no locks or files pending; quitting");
|
|
|
|
res1 = DOSTRUE;
|
|
}
|
|
else
|
|
{
|
|
SHOWMSG("locks or files still pending; cannot quit yet");
|
|
|
|
res1 = DOSFALSE;
|
|
res2 = ERROR_OBJECT_IN_USE;
|
|
}
|
|
|
|
Quit = TRUE;
|
|
break;
|
|
|
|
case ACTION_CURRENT_VOLUME:
|
|
/* (Ignore) -> VolumeNode */
|
|
|
|
res1 = MKBADDR(VolumeNode);
|
|
break;
|
|
|
|
case ACTION_LOCATE_OBJECT:
|
|
/* Lock,Name,Mode -> Lock */
|
|
|
|
res1 = Action_LocateObject((struct FileLock *)BADDR(dp->dp_Arg1),(APTR)BADDR(dp->dp_Arg2),dp->dp_Arg3,&res2);
|
|
break;
|
|
|
|
case ACTION_RENAME_DISK:
|
|
/* Name -> Bool */
|
|
|
|
res1 = Action_RenameDisk((UBYTE *)BADDR(dp->dp_Arg1),&res2);
|
|
break;
|
|
|
|
case ACTION_FREE_LOCK:
|
|
/* Lock -> Bool */
|
|
|
|
res1 = Action_FreeLock((struct FileLock *)BADDR(dp->dp_Arg1),&res2);
|
|
break;
|
|
|
|
case ACTION_DELETE_OBJECT:
|
|
/* Lock,Name -> Bool */
|
|
|
|
res1 = Action_DeleteObject((struct FileLock *)BADDR(dp->dp_Arg1),(APTR)BADDR(dp->dp_Arg2),&res2);
|
|
break;
|
|
|
|
case ACTION_RENAME_OBJECT:
|
|
/* Source lock,source name,destination lock,destination name -> Bool */
|
|
|
|
res1 = Action_RenameObject((struct FileLock *)BADDR(dp->dp_Arg1),BADDR(dp->dp_Arg2),
|
|
(struct FileLock *)BADDR(dp->dp_Arg3),BADDR(dp->dp_Arg4),&res2);
|
|
|
|
break;
|
|
|
|
case ACTION_MORE_CACHE:
|
|
/* Buffer delta -> Total number of buffers */
|
|
|
|
/* NOTE: documentation for this packet type is inconsistent;
|
|
* in the 'good old' 1.x days 'res1' was documented as
|
|
* the total number of buffers to be returned. In the
|
|
* 2.x documentation it is said that 'res1' should
|
|
* return the success code, with 'res2' to hold the
|
|
* total number of buffers. However, the 'AddBuffers'
|
|
* shell command doesn't work that way, and the
|
|
* dos.library implementation of 'AddBuffers()' doesn't
|
|
* work that way either. The 1.3 'AddBuffers' command
|
|
* appears to treat a zero result as failure and a
|
|
* non-zero result as success, which suggests that this
|
|
* is how the packet is supposed to work, contrary to
|
|
* what the official documentation says.
|
|
*/
|
|
res1 = Action_MoreCache(dp->dp_Arg1,&res2);
|
|
break;
|
|
|
|
case ACTION_COPY_DIR:
|
|
/* Lock -> Lock */
|
|
|
|
res1 = Action_CopyDir((struct FileLock *)BADDR(dp->dp_Arg1),&res2);
|
|
break;
|
|
|
|
case ACTION_SET_PROTECT:
|
|
/* (Ignore),Lock,Name,Mask -> Bool */
|
|
|
|
res1 = Action_SetProtect((struct FileLock *)BADDR(dp->dp_Arg2),BADDR(dp->dp_Arg3),dp->dp_Arg4,&res2);
|
|
break;
|
|
|
|
case ACTION_CREATE_DIR:
|
|
/* Lock,Name -> Lock */
|
|
|
|
res1 = Action_CreateDir((struct FileLock *)BADDR(dp->dp_Arg1),(APTR)BADDR(dp->dp_Arg2),&res2);
|
|
break;
|
|
|
|
case ACTION_EXAMINE_OBJECT:
|
|
/* FileLock,FileInfoBlock -> Bool */
|
|
|
|
res1 = Action_ExamineObject((struct FileLock *)BADDR(dp->dp_Arg1),(struct FileInfoBlock *)BADDR(dp->dp_Arg2),&res2);
|
|
break;
|
|
|
|
case ACTION_EXAMINE_NEXT:
|
|
/* FileLock,FileInfoBlock -> Bool */
|
|
|
|
res1 = Action_ExamineNext((struct FileLock *)BADDR(dp->dp_Arg1),(struct FileInfoBlock *)BADDR(dp->dp_Arg2),&res2);
|
|
break;
|
|
|
|
case ACTION_DISK_INFO:
|
|
/* InfoData -> Bool */
|
|
|
|
Action_DiskInfo((struct InfoData *)BADDR(dp->dp_Arg1),&res2);
|
|
res1 = DOSTRUE;
|
|
res2 = 0;
|
|
break;
|
|
|
|
case ACTION_INFO:
|
|
/* FileLock,InfoData -> Bool */
|
|
|
|
res1 = Action_Info((struct FileLock *)BADDR(dp->dp_Arg1),(struct InfoData *)BADDR(dp->dp_Arg2),&res2);
|
|
break;
|
|
|
|
case ACTION_SET_COMMENT:
|
|
/* (Ignore),FileLock,Name,Comment -> Bool */
|
|
|
|
res1 = Action_SetComment((struct FileLock *)BADDR(dp->dp_Arg2),BADDR(dp->dp_Arg3),BADDR(dp->dp_Arg4),&res2);
|
|
break;
|
|
|
|
case ACTION_PARENT:
|
|
/* Lock -> Lock */
|
|
|
|
res1 = Action_Parent((struct FileLock *)BADDR(dp->dp_Arg1),&res2);
|
|
break;
|
|
|
|
case ACTION_INHIBIT:
|
|
|
|
SHOWMSG("ACTION_INHIBIT");
|
|
res1 = DOSTRUE;
|
|
break;
|
|
|
|
case ACTION_SET_DATE:
|
|
/* (Ignore),FileLock,Name,DateStamp(APTR) -> Bool */
|
|
|
|
res1 = Action_SetDate((struct FileLock *)BADDR(dp->dp_Arg2),(APTR)BADDR(dp->dp_Arg3),(struct DateStamp *)dp->dp_Arg4,&res2);
|
|
break;
|
|
|
|
case ACTION_SAME_LOCK:
|
|
/* Lock,Lock -> Bool */
|
|
|
|
res1 = Action_SameLock((struct FileLock *)BADDR(dp->dp_Arg1),(struct FileLock *)BADDR(dp->dp_Arg2),&res2);
|
|
break;
|
|
|
|
case ACTION_READ:
|
|
/* FileHandle->fh_Arg1,Buffer(APTR),Length -> Length */
|
|
|
|
res1 = Action_Read((struct FileNode *)dp->dp_Arg1,(APTR)dp->dp_Arg2,dp->dp_Arg3,&res2);
|
|
break;
|
|
|
|
case ACTION_WRITE:
|
|
/* FileHandle->fh_Arg1,Buffer(APTR),Length -> Length */
|
|
|
|
res1 = Action_Write((struct FileNode *)dp->dp_Arg1,(APTR)dp->dp_Arg2,dp->dp_Arg3,&res2);
|
|
break;
|
|
|
|
case ACTION_FINDUPDATE:
|
|
case ACTION_FINDINPUT:
|
|
case ACTION_FINDOUTPUT:
|
|
/* FileHandle,FileLock,Name -> Bool */
|
|
|
|
res1 = Action_Find(dp->dp_Action,(struct FileHandle *)BADDR(dp->dp_Arg1),(struct FileLock *)BADDR(dp->dp_Arg2),(APTR)BADDR(dp->dp_Arg3),&res2);
|
|
break;
|
|
|
|
case ACTION_END:
|
|
/* FileHandle->fh_Arg1 -> Bool */
|
|
|
|
res1 = Action_End((struct FileNode *)dp->dp_Arg1,&res2);
|
|
break;
|
|
|
|
case ACTION_SEEK:
|
|
/* FileHandle->fh_Arg1,Position,Mode -> Position */
|
|
|
|
res1 = Action_Seek((struct FileNode *)dp->dp_Arg1,dp->dp_Arg2,dp->dp_Arg3,&res2);
|
|
break;
|
|
|
|
case ACTION_SET_FILE_SIZE:
|
|
/* FileHandle->fh_Arg1,Offset,Mode -> New file size */
|
|
|
|
res1 = Action_SetFileSize((struct FileNode *)dp->dp_Arg1,dp->dp_Arg2,dp->dp_Arg3,&res2);
|
|
break;
|
|
|
|
case ACTION_WRITE_PROTECT:
|
|
/* Flag,Key -> Bool */
|
|
|
|
res1 = Action_WriteProtect(dp->dp_Arg1,dp->dp_Arg2,&res2);
|
|
break;
|
|
|
|
case ACTION_FH_FROM_LOCK:
|
|
/* FileHandle(BPTR),FileLock -> Bool */
|
|
|
|
res1 = Action_FHFromLock((struct FileHandle *)BADDR(dp->dp_Arg1),(struct FileLock *)BADDR(dp->dp_Arg2),&res2);
|
|
break;
|
|
|
|
case ACTION_IS_FILESYSTEM:
|
|
|
|
SHOWMSG("ACTION_IS_FILESYSTEM");
|
|
res1 = DOSTRUE;
|
|
break;
|
|
|
|
case ACTION_CHANGE_MODE:
|
|
/* Type,Object,Mode -> Bool */
|
|
|
|
res1 = Action_ChangeMode(dp->dp_Arg1,(APTR)BADDR(dp->dp_Arg2),dp->dp_Arg3,&res2);
|
|
break;
|
|
|
|
case ACTION_COPY_DIR_FH:
|
|
/* FileHandle->fh_Arg1 -> Bool */
|
|
|
|
res1 = Action_CopyDirFH((struct FileNode *)dp->dp_Arg1,&res2);
|
|
break;
|
|
|
|
case ACTION_PARENT_FH:
|
|
/* FileHandle->fh_Arg1 -> Bool */
|
|
|
|
res1 = Action_ParentFH((struct FileNode *)dp->dp_Arg1,&res2);
|
|
break;
|
|
|
|
case ACTION_EXAMINE_ALL:
|
|
/* FileLock,ExAllData(APTR),Size,Type,ExAllControl(APTR) -> Bool */
|
|
|
|
res1 = Action_ExamineAll((struct FileLock *)BADDR(dp->dp_Arg1),(struct ExAllData *)dp->dp_Arg2,
|
|
dp->dp_Arg3,dp->dp_Arg4,(struct ExAllControl *)dp->dp_Arg5,&res2);
|
|
|
|
break;
|
|
|
|
case ACTION_EXAMINE_FH:
|
|
/* FileHandle->fh_Arg1,FileInfoBlock -> Bool */
|
|
|
|
res1 = Action_ExamineFH((struct FileNode *)dp->dp_Arg1,(struct FileInfoBlock *)BADDR(dp->dp_Arg2),&res2);
|
|
break;
|
|
|
|
case ACTION_EXAMINE_ALL_END:
|
|
/* FileLock,ExAllData(APTR),Size,Type,ExAllControl(APTR) -> Bool */
|
|
|
|
res1 = DOSTRUE;
|
|
break;
|
|
|
|
case ACTION_LOCK_RECORD:
|
|
/* FileHandle->fh_Arg1,position,length,mode,time-out -> Bool */
|
|
res1 = Action_LockRecord((struct FileNode *)dp->dp_Arg1,dp->dp_Arg2,dp->dp_Arg3,dp->dp_Arg4,(ULONG)dp->dp_Arg5,&res2);
|
|
break;
|
|
|
|
case ACTION_FREE_RECORD:
|
|
/* FileHandle->fh_Arg1,position,length -> Bool */
|
|
res1 = Action_FreeRecord((struct FileNode *)dp->dp_Arg1,dp->dp_Arg2,dp->dp_Arg3,&res2);
|
|
break;
|
|
|
|
default:
|
|
|
|
D(("Anything goes: dp->dp_Action=%ld (0x%lx)",dp->dp_Action,dp->dp_Action));
|
|
|
|
/* Return -1 for ACTION_READ_LINK, for which DOSFALSE (= 0)
|
|
* would otherwise be a valid response. Bug fix contributed
|
|
* by Harry 'Piru' Sintonen.
|
|
*/
|
|
res1 = (dp->dp_Action == ACTION_READ_LINK) ? -1 : DOSFALSE;
|
|
res2 = ERROR_ACTION_NOT_KNOWN;
|
|
|
|
break;
|
|
}
|
|
|
|
SHOWVALUE(res1);
|
|
SHOWVALUE(res2);
|
|
|
|
ReplyPkt(dp,res1,res2);
|
|
|
|
D(("\n"));
|
|
}
|
|
}
|
|
|
|
#if DEBUG
|
|
{
|
|
if(signals & SIGBREAKF_CTRL_F)
|
|
{
|
|
struct FileNode * fn;
|
|
struct LockNode * ln;
|
|
|
|
D(("list of open files:"));
|
|
|
|
for(fn = (struct FileNode *)FileList.mlh_Head ;
|
|
fn->fn_MinNode.mln_Succ != NULL ;
|
|
fn = (struct FileNode *)fn->fn_MinNode.mln_Succ)
|
|
{
|
|
D((" name='%s'",fn->fn_FullName));
|
|
D((" mode=%ld, offset=%ld",fn->fn_Mode,fn->fn_Offset));
|
|
D((""));
|
|
}
|
|
|
|
D(("list of allocated locks:"));
|
|
|
|
for(ln = (struct LockNode *)LockList.mlh_Head ;
|
|
ln->ln_MinNode.mln_Succ != NULL ;
|
|
ln = (struct LockNode *)ln->ln_MinNode.mln_Succ)
|
|
{
|
|
D((" name='%s'",ln->ln_FullName));
|
|
D((" mode=%ld",ln->ln_FileLock.fl_Access));
|
|
D((""));
|
|
}
|
|
}
|
|
}
|
|
#endif /* DEBUG */
|
|
|
|
if(signals & SIGBREAKF_CTRL_C)
|
|
Quit = TRUE;
|
|
|
|
if(Quit)
|
|
{
|
|
if(IsListEmpty((struct List *)&FileList) && IsListEmpty((struct List *)&LockList))
|
|
{
|
|
SHOWMSG("no locks or files pending; quitting");
|
|
done = TRUE;
|
|
}
|
|
else
|
|
{
|
|
SHOWMSG("locks or files still pending; cannot quit yet");
|
|
}
|
|
}
|
|
}
|
|
while(NOT done);
|
|
|
|
/* Restore the priority of the file system, unless the priority
|
|
* has already been changed.
|
|
*/
|
|
Forbid();
|
|
|
|
if(old_priority < 10 && this_process->pr_Task.tc_Node.ln_Pri == 10)
|
|
SetTaskPri((struct Task *)this_process, old_priority);
|
|
|
|
Permit();
|
|
|
|
if(sign_off)
|
|
LocalPrintf("stopped.\n");
|
|
|
|
LEAVE();
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
/*
|
|
* Copy src to string dst of size siz. At most siz-1 characters
|
|
* will be copied. Always NUL terminates (unless siz == 0).
|
|
* Returns strlen(src); if retval >= siz, truncation occurred.
|
|
*/
|
|
size_t
|
|
strlcpy(char *dst, const char *src, size_t siz)
|
|
{
|
|
char *d = dst;
|
|
const char *s = src;
|
|
size_t n = siz;
|
|
size_t result;
|
|
|
|
/* Copy as many bytes as will fit */
|
|
if(n != 0 && --n != 0)
|
|
{
|
|
do
|
|
{
|
|
if(((*d++) = (*s++)) == '\0')
|
|
break;
|
|
}
|
|
while(--n != 0);
|
|
}
|
|
|
|
/* Not enough room in dst, add NUL and traverse rest of src */
|
|
if(n == 0)
|
|
{
|
|
if(siz != 0)
|
|
(*d) = '\0'; /* NUL-terminate dst */
|
|
|
|
while((*s++) != '\0')
|
|
;
|
|
}
|
|
|
|
result = s - src - 1; /* count does not include NUL */
|
|
|
|
return(result);
|
|
}
|
|
|
|
/*
|
|
* Appends src to string dst of size siz (unlike strncat, siz is the
|
|
* full size of dst, not space left). At most siz-1 characters
|
|
* will be copied. Always NUL terminates (unless siz <= strlen(dst)).
|
|
* Returns strlen(src) + MIN(siz, strlen(initial dst)).
|
|
* If retval >= siz, truncation occurred.
|
|
*/
|
|
size_t
|
|
strlcat(char *dst, const char *src, size_t siz)
|
|
{
|
|
char *d = dst;
|
|
const char *s = src;
|
|
size_t n = siz;
|
|
size_t dlen;
|
|
size_t result;
|
|
|
|
/* Find the end of dst and adjust bytes left but don't go past end */
|
|
while(n-- != 0 && (*d) != '\0')
|
|
d++;
|
|
|
|
dlen = d - dst;
|
|
n = siz - dlen;
|
|
|
|
if(n == 0)
|
|
{
|
|
result = dlen + strlen(s);
|
|
}
|
|
else
|
|
{
|
|
while((*s) != '\0')
|
|
{
|
|
if(n != 1)
|
|
{
|
|
(*d++) = (*s);
|
|
n--;
|
|
}
|
|
|
|
s++;
|
|
}
|
|
|
|
(*d) = '\0';
|
|
|
|
result = dlen + (s - src); /* count does not include NUL */
|
|
}
|
|
|
|
return(result);
|
|
}
|