mirror of
https://github.com/obarthel/amiga-smbfs.git
synced 2025-12-08 14:58:35 +00:00
The signature "SMB\0" is now stored in DosList->dol_misc.dol_volume.dol_DiskType, which can be obtained through Info() or by walking through the data structures associated with a FileLock. This was suggested by Chris Handley and Chris Young. Thank you very much!
7089 lines
149 KiB
C
7089 lines
149 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=... 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:
|
|
* 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>
|
|
|
|
/****************************************************************************/
|
|
|
|
/* 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(STRPTR args, LONG args_length, struct ExecBase * exec_base);
|
|
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(const 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, BOOL opt_write_behind, BOOL opt_prefer_write_raw, 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);
|
|
|
|
/****************************************************************************/
|
|
|
|
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];
|
|
|
|
/****************************************************************************/
|
|
|
|
#if defined(__amigaos4__)
|
|
|
|
/* The AmigaOS 2.x/3.x version needs about 20000 bytes of stack,
|
|
* which is allocated at runtime. This is how it's done with
|
|
* AmigaOS 4.x, for which the shell allocates the stack
|
|
* before launching the program.
|
|
*/
|
|
const char stack_size_cookie[] = "$STACK: 30000";
|
|
|
|
#endif /* __amigaos4__ */
|
|
|
|
/****************************************************************************/
|
|
|
|
extern int STDARGS swap_stack_and_call(struct StackSwapStruct * stk,APTR function);
|
|
|
|
/****************************************************************************/
|
|
|
|
LONG
|
|
_start(STRPTR args, LONG args_length, struct ExecBase * exec_base)
|
|
{
|
|
struct StackSwapStruct * stk = NULL;
|
|
APTR new_stack = NULL;
|
|
LONG new_stack_size = 20000;
|
|
struct Process * this_process;
|
|
LONG result = RETURN_FAIL;
|
|
|
|
#if defined(__amigaos4__)
|
|
{
|
|
SysBase = (struct Library *)exec_base;
|
|
|
|
IExec = (struct ExecIFace *)((struct ExecBase *)SysBase)->MainInterface;
|
|
}
|
|
#else
|
|
{
|
|
SysBase = (struct Library *)AbsExecBase;
|
|
}
|
|
#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;
|
|
}
|
|
|
|
/* For AmigaOS4 we expect the stack size to be
|
|
* sufficient, because of the "STACK" string
|
|
* cookie.
|
|
*/
|
|
#if defined(__amigaos4__)
|
|
{
|
|
result = main();
|
|
}
|
|
/* For AmigaOS 2.x/3.x we check how much stack size the
|
|
* program has available and, if necessary, allocate
|
|
* a larger stack and use that instead.
|
|
*/
|
|
#else
|
|
{
|
|
/* Not enough stack size available? */
|
|
if(get_stack_size() < new_stack_size)
|
|
{
|
|
/* Make the new stack size a multiple of 32 bytes. */
|
|
new_stack_size = 32 + ((new_stack_size + 31UL) & ~31UL);
|
|
|
|
/* Allocate the new 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);
|
|
|
|
/* Testing: fill the stack with a preset pattern, so that we
|
|
* can measure later how much stack space was used.
|
|
*/
|
|
/* stack_usage_init(stk); */
|
|
|
|
result = swap_stack_and_call(stk,(APTR)main);
|
|
|
|
/* Testing: figure out how much stack space was used. */
|
|
/* Printf("stack size used = %ld\n",stack_usage_exit(stk)); */
|
|
}
|
|
/* Sufficient stack space is available. */
|
|
else
|
|
{
|
|
result = main();
|
|
}
|
|
}
|
|
#endif /* __amigaos4__ */
|
|
|
|
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);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
/* Figure out how much stack size is available to the running
|
|
* Task. Returns the size in bytes.
|
|
*/
|
|
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);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
/* This byte value is used to detect how much stack size
|
|
* was being used.
|
|
*/
|
|
#define STACK_FILL_COOKIE 0xA1
|
|
|
|
/****************************************************************************/
|
|
|
|
/* Testing: Fill the stack memory with a well-defined value which can later
|
|
* be used to figure out how much stack space was used by the program.
|
|
* This is needed by stack_usage_exit().
|
|
*/
|
|
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);
|
|
}
|
|
|
|
/* Testing: Check how much stack space was used by looking at where
|
|
* the test pattern previously written to the stack memory was
|
|
* overwritten. Returns how much space was used in bytes.
|
|
*/
|
|
STATIC ULONG
|
|
stack_usage_exit(const 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);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
/* This is the traditional main() program. */
|
|
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;
|
|
KEY DebugFile;
|
|
NUMBER TimeZoneOffset;
|
|
NUMBER DSTOffset;
|
|
SWITCH NetBIOSTransport;
|
|
SWITCH WriteBehind;
|
|
SWITCH PreferWriteRaw;
|
|
SWITCH DumpSMB;
|
|
NUMBER DumpSMBLevel;
|
|
KEY DumpSMBFile;
|
|
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,"
|
|
"DEBUGFILE/K,"
|
|
"TZ=TIMEZONEOFFSET/N/K,"
|
|
"DST=DSTOFFSET/N/K,"
|
|
"NETBIOS/S,"
|
|
"WRITEBEHIND/S,"
|
|
"PREFERWRITERAW/S,"
|
|
"DUMPSMB/S,"
|
|
"DUMPSMBLEVEL/N/K,"
|
|
"DUMPSMBFILE/K,"
|
|
"UTF8/S,"
|
|
"TRANSLATE=TRANSLATIONFILE/K,"
|
|
"SERVICE/A";
|
|
|
|
BPTR debug_file = (BPTR)NULL;
|
|
BOOL close_debug_file = FALSE;
|
|
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,"DEBUGFILE");
|
|
if(str != NULL)
|
|
args.DebugFile = str;
|
|
|
|
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,"WRITEBEHIND") != NULL)
|
|
args.WriteBehind = TRUE;
|
|
|
|
if(FindToolType(Icon->do_ToolTypes,"PREFERWRITERAW") != NULL)
|
|
args.PreferWriteRaw = 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;
|
|
|
|
#if DEBUG
|
|
{
|
|
if(args.DebugFile != NULL)
|
|
{
|
|
/* Try to append the output to an existing file. */
|
|
debug_file = Open(args.DebugFile,MODE_OLDFILE);
|
|
if(debug_file == (BPTR)NULL)
|
|
{
|
|
/* File does not exist? Then create a new file. */
|
|
if(IoErr() == ERROR_OBJECT_NOT_FOUND)
|
|
{
|
|
debug_file = Open(args.DebugFile,MODE_NEWFILE);
|
|
if(debug_file != (BPTR)NULL)
|
|
ChangeMode(CHANGE_FH,debug_file,SHARED_LOCK);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* File exists; seek to the end of it. */
|
|
Seek(debug_file,0,OFFSET_END);
|
|
}
|
|
|
|
close_debug_file = TRUE;
|
|
}
|
|
else
|
|
{
|
|
if(WBStartup == NULL)
|
|
debug_file = Output();
|
|
}
|
|
|
|
SETDEBUGFILE(debug_file);
|
|
}
|
|
#endif /* DEBUG */
|
|
|
|
/* 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)
|
|
{
|
|
LONG dump_smb_level = 0;
|
|
|
|
if(args.DumpSMBLevel != NULL)
|
|
dump_smb_level = (*args.DumpSMBLevel);
|
|
|
|
if(args.DumpSMB && WBStartup == NULL)
|
|
control_smb_dump(TRUE, dump_smb_level, (const char *)args.DumpSMBFile);
|
|
}
|
|
#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.WriteBehind,
|
|
args.PreferWriteRaw,
|
|
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:
|
|
|
|
#if defined(DUMP_SMB)
|
|
{
|
|
if(args.DumpSMB && WBStartup == NULL)
|
|
control_smb_dump(FALSE, 0, NULL);
|
|
}
|
|
#endif /* DUMP_SMB */
|
|
|
|
if(close_debug_file && debug_file != (BPTR)NULL)
|
|
Close(debug_file);
|
|
|
|
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;
|
|
|
|
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 the descriptive text associated with a host lookup failure code. */
|
|
STRPTR
|
|
host_strerror(int error)
|
|
{
|
|
struct TagItem tags[2];
|
|
STRPTR result;
|
|
|
|
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);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
/* 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;
|
|
}
|
|
}
|
|
|
|
#if DEBUG
|
|
{
|
|
UBYTE amigados_error_text[256];
|
|
|
|
Fault(result,NULL,amigados_error_text,sizeof(amigados_error_text));
|
|
|
|
LOG(("Translated POSIX error code %ld (%s), to AmigaDOS error code %ld (%s)\n", error, amitcp_strerror(error), result, amigados_error_text));
|
|
}
|
|
#endif /* DEBUG */
|
|
|
|
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
|
|
* we 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,
|
|
BOOL opt_write_behind,
|
|
BOOL opt_prefer_write_raw,
|
|
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,opt_write_behind,opt_prefer_write_raw,&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);
|
|
|
|
/* Allow the file system to be identified by looking at the
|
|
* contents of the volume node. We put "SMB\0" into the
|
|
* dol_DiskType field. This was suggested by Chris Handley
|
|
* and Chris Young. Thank you very much!
|
|
*/
|
|
VolumeNode->dol_misc.dol_volume.dol_DiskType = ID_SMB_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;
|
|
}
|
|
|
|
/* Name string, as given in the DOS packet, is in
|
|
* BCPL format and needs to be converted into
|
|
* 'C' format.
|
|
*/
|
|
ConvertBString(sizeof(name),name,bcpl_name);
|
|
|
|
/* Translate the Amiga file name into UTF-8 encoded form? */
|
|
if (TranslateUTF8)
|
|
{
|
|
UBYTE encoded_name[MAX_FILENAME_LEN];
|
|
int encoded_name_len;
|
|
LONG name_len = strlen(name);
|
|
|
|
/* Figure out how long the UTF-8 version will become. */
|
|
encoded_name_len = encode_iso8859_1_as_utf8_string(name,name_len,NULL,0);
|
|
|
|
/* Encoding error occured, or the resulting name is longer than the buffer will hold? */
|
|
if(encoded_name_len < 0 || encoded_name_len >= sizeof(name))
|
|
{
|
|
error = ERROR_INVALID_COMPONENT_NAME;
|
|
goto out;
|
|
}
|
|
|
|
/* Repeat the encoding process, writing the result to the
|
|
* replacement name buffer now.
|
|
*/
|
|
encode_iso8859_1_as_utf8_string(name,name_len,encoded_name,sizeof(encoded_name));
|
|
|
|
/* Copy it back to the conversion buffer (which is not terribly efficient, though). */
|
|
strlcpy(name,encoded_name,sizeof(name));
|
|
}
|
|
/* Translate the Amiga file name using a translation table? */
|
|
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;
|
|
}
|
|
|
|
/* Translate the name of the file/directory from UTF-8
|
|
* into AmigaOS ISO 8859-1 (ISO Latin 1) encoding?
|
|
*/
|
|
if(TranslateUTF8)
|
|
{
|
|
UBYTE decoded_name[MAX_FILENAME_LEN];
|
|
int decoded_name_len;
|
|
|
|
/* Try to decode the file file, translating it into ISO 8859-1 format. */
|
|
decoded_name_len = decode_utf8_as_iso8859_1_string(name,name_len,NULL,0);
|
|
|
|
/* Decoding error occured, or the decoded name would be longer than
|
|
* buffer would allow?
|
|
*/
|
|
if(decoded_name_len < 0 || decoded_name_len >= MAX_FILENAME_LEN)
|
|
{
|
|
error = ERROR_INVALID_COMPONENT_NAME;
|
|
goto out;
|
|
}
|
|
|
|
/* Decode the name for real. */
|
|
decoded_name_len = decode_utf8_as_iso8859_1_string(name,name_len,decoded_name,sizeof(decoded_name));
|
|
|
|
/* Store the decoded name in the form expected
|
|
* by dos.library (which will then translate it again).
|
|
*/
|
|
fib->fib_FileName[0] = decoded_name_len;
|
|
memcpy(&fib->fib_FileName[1],decoded_name,decoded_name_len);
|
|
}
|
|
else
|
|
{
|
|
/* Store the file/directory name in the form expected
|
|
* by dos.library.
|
|
*/
|
|
ConvertCString(fib->fib_FileName,sizeof(fib->fib_FileName),name,name_len);
|
|
|
|
/* If necessary, translate the name to Amiga format using a mapping table. */
|
|
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 is 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);
|
|
}
|