/* * :ts=4 * * SMB file system wrapper for AmigaOS, using the AmiTCP V3 API * * Copyright (C) 2000-2018 by Olaf `Olsen' Barthel * * 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 * smbfs maxtransmit=16600 debuglevel=2 dumpsmb dumpsmblevel=2 domain=workgroup user=olsen password=... volume=olsen //felix/olsen * Samba 4.6.7: smbfs debuglevel=2 dumpsmb dumpsmblevel=2 volume=ubuntu-test //ubuntu-17-olaf/test * Samba 4.7.6: smbfs debuglevel=2 dumpsmb dumpsmblevel=2 volume=ubuntu-test //ubuntu-18-olaf/test * Samba 3.0.25: smbfs debuglevel=2 dumpsmb dumpsmblevel=1 user=olsen password=... volume=olsen //192.168.1.118/olsen */ #include "smbfs.h" /****************************************************************************/ #include "smb_abstraction.h" #include "utf-8-iso-8859-1-conversion.h" #include "cp437.h" #include "cp850.h" #include "errors.h" #include "dump_smb.h" /****************************************************************************/ #include #include #include /****************************************************************************/ #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. */ 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); STATIC LONG CVSPrintf(STRPTR format_string, APTR args); STATIC VOID VSPrintf(STRPTR buffer, STRPTR formatString, APTR args); 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, int opt_timeout, LONG *opt_time_zone_offset, LONG *opt_dst_offset, BOOL opt_raw_smb, BOOL opt_write_behind, BOOL opt_prefer_write_raw, BOOL opt_disable_write_raw, BOOL opt_disable_read_raw, STRPTR opt_native_os, STRPTR device_name, STRPTR volume_name, STRPTR translation_file); 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 = %lu\n",stack_usage_exit(stk)); */ } /* Sufficient stack space should be 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 unused_stack_space,i; unused_stack_space = 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; unused_stack_space++; } return(stack_size - unused_stack_space); } /****************************************************************************/ /* 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 Timeout; NUMBER DebugLevel; KEY DebugFile; NUMBER TimeZoneOffset; NUMBER DSTOffset; SWITCH NetBIOSTransport; SWITCH WriteBehind; SWITCH PreferWriteRaw; SWITCH DisableWriteRaw; SWITCH DisableReadRaw; KEY NativeOS; SWITCH DumpSMB; NUMBER DumpSMBLevel; KEY DumpSMBFile; SWITCH UTF8; SWITCH CP437; SWITCH CP850; 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," "TIMEOUT/N/K," "DEBUGLEVEL=DEBUG/N/K," "DEBUGFILE/K," "TZ=TIMEZONEOFFSET/N/K," "DST=DSTOFFSET/N/K," "NETBIOS/S," "WRITEBEHIND/S," "PREFERWRITERAW/S," "DISABLEWRITERAW/S," "DISABLEREADRAW/S," "NATIVEOS/K," "DUMPSMB/S," "DUMPSMBLEVEL/N/K," "DUMPSMBFILE/K," "UTF8/S," "CP437/S," "CP850/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; LONG timeout = 0; 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,"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,"DISABLEWRITERAW") != NULL) args.DisableWriteRaw = TRUE; if(FindToolType(Icon->do_ToolTypes,"DISABLEREADRAW") != NULL) args.DisableReadRaw = TRUE; str = FindToolType(Icon->do_ToolTypes,"NATIVEOS"); if(str != NULL) args.NativeOS = str; str = FindToolType(Icon->do_ToolTypes,"TRANSLATE"); if(str == NULL) str = FindToolType(Icon->do_ToolTypes,"TRANSLATIONFILE"); if(str != NULL) { args.TranslationFile = str; } else { if (FindToolType(Icon->do_ToolTypes,"UTF8") != NULL) args.UTF8 = TRUE; else if (FindToolType(Icon->do_ToolTypes,"CP437") != NULL) args.CP437 = TRUE; else if (FindToolType(Icon->do_ToolTypes,"CP850") != NULL) args.CP850 = 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; } str = FindToolType(Icon->do_ToolTypes,"TIMEOUT"); if(str != NULL) { if(StrToLong(str,&number) == -1 || number < 0) { ReportError("Invalid number '%s' for 'TIMEOUT' parameter.",str); goto out; } timeout = 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); if(args.Timeout != NULL && (*args.Timeout) >= 0) timeout = (*args.Timeout); } /* 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"; /* Code page based translation using a file disables * UTF-8 and built-in CP437 and CP850 translation. */ if(args.TranslationFile != NULL) { args.UTF8 = args.CP437 = args.CP850 = FALSE; } else { if (args.UTF8) args.CP437 = args.CP850 = FALSE; else if (args.CP437) args.CP850 = FALSE; } /* Use one of the built-in code page translation tables? */ if (args.CP437) { memmove(A2M,unicode_to_cp437,sizeof(unicode_to_cp437)); memmove(M2A,cp437_to_unicode,sizeof(cp437_to_unicode)); TranslateNames = TRUE; } else if (args.CP850) { memmove(A2M,unicode_to_cp850,sizeof(unicode_to_cp850)); memmove(M2A,cp850_to_unicode,sizeof(cp850_to_unicode)); TranslateNames = TRUE; } 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) { #if !defined(DEBUG) { if(WBStartup == NULL) ReportError("This version of smbfs cannot create debug output."); } #endif /* !DEBUG */ 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); } #else { if(WBStartup == NULL && (args.DumpSMBLevel != NULL || args.DumpSMB)) ReportError("This version of smbfs cannot create SMB debug output."); } #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, timeout, args.TimeZoneOffset, args.DSTOffset, !args.NetBIOSTransport, /* Use raw SMB transport instead of NetBIOS transport? */ args.WriteBehind, args.PreferWriteRaw, args.DisableWriteRaw, args.DisableReadRaw, args.NativeOS, 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); } /****************************************************************************/ static LONG VARARGS68K LocalFPrintf(BPTR output, const UBYTE * format, ...) { va_list args; LONG result; if(output == ZERO) output = Output(); #if defined(__amigaos4__) { va_startlinear(args,format); result = VFPrintf(output, format, va_getlinearva(args,APTR)); va_end(args); } #else { va_start(args,format); result = VFPrintf(output, 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, or by * the SMB POSIX error conversion code. */ STRPTR posix_strerror(int error) { STRPTR result; /* Is this one of our own error codes? */ if(error >= error_end_of_file) { static const struct { int code; const char * message; } messages[] = { { error_end_of_file, "end of file" }, { error_invalid_netbios_session, "invalid NetBIOS session" }, { error_message_exceeds_buffer_size, "message exceeds buffer size" }, { error_invalid_buffer_format, "invalid buffer format" }, { error_data_exceeds_buffer_size, "data exceeds buffer size" }, { error_invalid_parameter_size, "invalid parameter size" }, { error_check_smb_error, "check SMB error class and code" }, { error_server_setup_incomplete, "server setup incomplete" }, { error_server_connection_invalid, "server connection invalid" }, { error_smb_message_signature_missing, "SMB message signature missing" }, { error_smb_message_too_short, "SMB message too short" }, { error_smb_message_invalid_command, "SMB message invalid command" }, { error_smb_message_invalid_word_count, "SMB message invalid word count" }, { error_smb_message_invalid_byte_count, "SMB message invalid byte count" }, { error_looping_in_find_next, "looping in find_next" }, { error_invalid_directory_size, "invalid directory size" }, { error_session_request_failed, "session request failed" }, { error_unsupported_dialect, "unsupported dialect" }, { -1, NULL } }; int i; result = ""; for(i = 0 ; messages[i].code != -1 ; i++) { if(messages[i].code == error) { result = (STRPTR)messages[i].message; break; } } } else { struct TagItem tags[2]; 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 { struct Process * this_process = (struct Process *)FindTask(NULL); UBYTE program_name[MAX_FILENAME_LEN]; BPTR output; GetProgramName(program_name,sizeof(program_name)); if(this_process->pr_CES != ZERO) output = this_process->pr_CES; else output = Output(); LocalFPrintf(output, "%s: ",FilePart(program_name)); #if defined(__amigaos4__) { va_startlinear(args,fmt); VFPrintf(output, fmt, va_getlinearva(args,APTR)); va_end(args); } #else { va_start(args,fmt); VFPrintf(output,fmt,args); va_end(args); } #endif /* __amigaos4__ */ LocalFPrintf(output, "\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(const char *name, const 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 = recv(sock_fd, buffer, sizeof(buffer), 0); 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; int 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) { int 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_posix_to_amigados[][2] = { { EPERM, ERROR_READ_PROTECTED }, /* 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_READ_PROTECTED }, /* 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_OBJECT_IN_USE }, /* 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_DIR_NOT_FOUND }, /* Can't assign requested address */ { ENETDOWN, ERROR_DIR_NOT_FOUND }, /* Network is down */ { ENETUNREACH, ERROR_DIR_NOT_FOUND }, /* Network is unreachable */ { ENETRESET, ERROR_DIR_NOT_FOUND }, /* Network dropped connection on reset */ { ECONNABORTED, ERROR_DIR_NOT_FOUND }, /* Software caused connection abort */ { ECONNRESET, ERROR_DIR_NOT_FOUND }, /* Connection reset by peer */ { ENOBUFS, ERROR_BUFFER_OVERFLOW }, /* 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_DIR_NOT_FOUND }, /* Host is down */ { EHOSTUNREACH, ERROR_DIR_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 } }; int original_error = error; LONG result = ERROR_ACTION_NOT_KNOWN; int i; ENTER(); /* Try our best to translate the SMB error class and code into a POSIX * error code... */ if(error == error_check_smb_error) { int error_class = ((struct smb_server *)ServerData)->rcls; int error_code = ((struct smb_server *)ServerData)->err; error = smb_errno(error_class, error_code); } for(i = 0 ; map_posix_to_amigados[i][0] != -1 ; i++) { if(map_posix_to_amigados[i][0] == error) { result = map_posix_to_amigados[i][1]; break; } } #if DEBUG { UBYTE amigados_error_text[256]; Fault(result,NULL,amigados_error_text,sizeof(amigados_error_text)); if(original_error == error_check_smb_error) { int error_class = ((struct smb_server *)ServerData)->rcls; int error_code = ((struct smb_server *)ServerData)->err; char * smb_class_name; char * smb_code_text; smb_translate_error_class_and_code(error_class,error_code,&smb_class_name,&smb_code_text); LOG(("Translated SMB %ld/%ld (%s/%s) -> POSIX %ld (%s) -> AmigaDOS error %ld (%s)\n", error_class, error_code, smb_class_name, smb_code_text, error, posix_strerror(error), result, amigados_error_text)); } else { LOG(("Translated POSIX %ld (%s) -> AmigaDOS error %ld (%s)\n", error, posix_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. Returns TRUE if the translation could be performed * and FALSE if at least one character cannot be translated. */ STATIC BOOL TranslateBName(UBYTE * name,const UBYTE * map) { BOOL success = TRUE; UBYTE c; int len; len = (*name++); while(len-- > 0) { c = map[(*name)]; /* The NUL means that the respective mapping cannot * represent the desired character. */ if(c == '\0') { success = FALSE; break; } (*name++) = c; } return(success); } /* Translate a NUL terminated file name via a mapping table. Returns TRUE if * the translation could be performed and FALSE if at least one character * cannot be translated. */ STATIC BOOL TranslateCName(UBYTE * name,const UBYTE * map) { BOOL success = TRUE; UBYTE c; while((c = (*name)) != '\0') { c = map[c]; /* The NUL means that the respective mapping cannot * represent the desired character. */ if(c == '\0') { success = FALSE; break; } (*name++) = c; } return(success); } /****************************************************************************/ /* 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, int opt_timeout, LONG * opt_time_zone_offset, LONG * opt_dst_offset, BOOL opt_raw_smb, BOOL opt_write_behind, BOOL opt_prefer_write_raw, BOOL opt_disable_write_raw, BOOL opt_disable_read_raw, STRPTR opt_native_os, STRPTR device_name, STRPTR volume_name, STRPTR translation_file) { BOOL result = FALSE; struct DosList * dl; int error = 0; int smb_error_class = 0, smb_error = 0; 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,posix_strerror(error)); goto out; } if(opt_changecase) { for(i = 0 ; i < (LONG)strlen(opt_password) ; i++) opt_password[i] = ToUpper(opt_password[i]); } /* 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; } } if(smba_start( service, workgroup, username, opt_password, opt_clientname, opt_servername, opt_cachesize, opt_max_transmit, opt_timeout, opt_raw_smb, opt_write_behind, opt_prefer_write_raw, opt_disable_write_raw, opt_disable_read_raw, (char *)opt_native_os, &error, &smb_error_class, &smb_error, &ServerData) < 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. */ STATIC VOID ConvertBString(LONG max_len,STRPTR cstring,const void * bstring) { const UBYTE * 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. */ STATIC VOID ConvertCString(void * bstring,LONG max_len,const UBYTE * cstring,LONG len) { UBYTE * 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( const UBYTE * parent_name, STRPTR name, STRPTR * result_ptr, LONG * result_size_ptr) { int 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; int 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); if(smba_open(ServerData,full_name,full_name_size,open_read_only,open_dont_truncate,&ln->ln_File,&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, const void * 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; int ignored_error; smba_stat_t st; int 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) { if(!TranslateCName(name,A2M)) { error = ERROR_INVALID_COMPONENT_NAME; goto out; } } 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); if(smba_open(ServerData,full_name,full_name_size,open_writable,open_dont_truncate,&file,&error) < 0) { error = MapErrnoToIoErr(error); goto out; } if(smba_getattr(file,&st,&error) < 0) { error = MapErrnoToIoErr(error); goto out; } smba_close(file,&ignored_error); file = NULL; if(st.is_dir) { SHOWMSG("removing a directory"); if(smba_rmdir(ServerData,full_name,&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_check_smb_error && smb_errno(((struct smb_server *)ServerData)->rcls,((struct smb_server *)ServerData)->err) == EACCES)) error = ERROR_DIRECTORY_NOT_EMPTY; else error = MapErrnoToIoErr(error); goto out; } } else { SHOWMSG("removing a file"); if(smba_remove(ServerData,full_name,&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,&ignored_error); (*error_ptr) = error; RETURN(result); return(result); } /****************************************************************************/ STATIC BPTR Action_CreateDir( struct FileLock * parent, const void * 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; size_t dir_name_size; smba_file_t * dir = NULL; STRPTR base_name; UBYTE name[MAX_FILENAME_LEN]; int ignored_error; int 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) { if(!TranslateCName(name,A2M)) { error = ERROR_INVALID_COMPONENT_NAME; goto out; } } 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; } SHOWSTRING(full_name); dir_name_size = strlen(full_name)+3; dir_name = AllocateMemory(dir_name_size); 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) { /* Is this a case of '\name'? If so, see to it that * dir_name becomes '\' and base_name becomes 'foo'. */ 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; } } D(("full path name = '%s', directory name = '%s'\n", dir_name, base_name)); 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; if(smba_open(ServerData,dir_name,dir_name_size,open_read_only,open_dont_truncate,&dir,&error) < 0) { error = MapErrnoToIoErr(error); goto out; } if(smba_mkdir(dir,base_name,&error) < 0) { error = MapErrnoToIoErr(error); goto out; } smba_close(dir,&ignored_error); dir = NULL; SHOWSTRING(full_name); if(smba_open(ServerData,full_name,full_name_size,open_read_only,open_dont_truncate,&ln->ln_File,&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,&ignored_error); 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, const void * 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]; int 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) { if(!TranslateCName(name,A2M)) { error = ERROR_INVALID_COMPONENT_NAME; goto out; } } 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); if(smba_open(ServerData,full_name,full_name_size,open_read_only,open_dont_truncate,&ln->ln_File,&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; int 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); if(smba_open(ServerData,full_name,full_name_size,open_read_only,open_dont_truncate,&ln->ln_File,&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; int ignored_error; int 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,&ignored_error); 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; int 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, const void * 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; int 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) { if(!TranslateCName(name,A2M)) { error = ERROR_INVALID_COMPONENT_NAME; goto out; } } 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); if(smba_open(ServerData,full_name,full_name_size,open_read_only,open_dont_truncate,&file,&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; if((mask & FIBF_DELETE) != 0) { 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); if(smba_setattr(file,&st,NULL,&error) < 0) { error = MapErrnoToIoErr(error); goto out; } result = DOSTRUE; out: FreeMemory(full_name); if(file != NULL) { int ignored_error; smba_close(file, &ignored_error); } (*error_ptr) = error; RETURN(result); return(result); } /****************************************************************************/ STATIC LONG Action_RenameObject( struct FileLock * source_lock, const void * source_bcpl_name, struct FileLock * destination_lock, const void * 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; int 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) { if(!TranslateCName(name,A2M)) { error = ERROR_INVALID_COMPONENT_NAME; goto out; } } 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) { if(!TranslateCName(name,A2M)) { error = ERROR_INVALID_COMPONENT_NAME; goto out; } } 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); if(smba_rename(ServerData,full_source_name,full_destination_name,&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; int error; ENTER(); memset(id,0,sizeof(*id)); if(WriteProtected) id->id_DiskState = ID_WRITE_PROTECTED; else id->id_DiskState = ID_VALIDATED; if(smba_statfs(ServerData,&block_size,&num_blocks,&num_blocks_free,&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; int error = OK; ENTER(); SHOWVALUE(lock); memset(fib,0,sizeof(*fib)); if(lock == NULL) { const UBYTE * volume_name = BADDR(VolumeNode->dol_Name); LONG len = volume_name[0]; SHOWMSG("ZERO root lock"); ASSERT( len < sizeof(fib->fib_FileName) ); 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; if(smba_getattr(ln->ln_File,&st,&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) { const UBYTE * volume_name = BADDR(VolumeNode->dol_Name); LONG len = volume_name[0]; SHOWMSG("root lock"); ASSERT( len < sizeof(fib->fib_FileName) ); 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 { const UBYTE * name; int name_len; int i; name = ln->ln_FullName; name_len = strlen(name); /* We just want the base name, not the path * leading up to it. */ for(i = name_len-1 ; i >= 0 ; i--) { if(name[i] == SMB_PATH_SEPARATOR) { name = &name[i+1]; name_len -= i+1; break; } } /* 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 name, 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 >= sizeof(fib->fib_FileName)) { 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 { /* Will the name fit? */ if(name_len >= sizeof(fib->fib_FileName)) { error = ERROR_INVALID_COMPONENT_NAME; goto out; } /* 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) { if(!TranslateBName(fib->fib_FileName,M2A)) { error = ERROR_INVALID_COMPONENT_NAME; goto out; } } } 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_DELETE|FIBF_GRP_DELETE|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(const UBYTE * 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, const UBYTE * 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(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); /* Skip file names which we could not represent. */ if(decoded_name_len < 0 || decoded_name_len >= (int)sizeof(fib->fib_FileName)) 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 { /* Skip file names which we could not represent. */ if(name_len >= (int)sizeof(fib->fib_FileName)) goto out; ConvertCString(fib->fib_FileName,sizeof(fib->fib_FileName),name,name_len); if(TranslateNames) { if(!TranslateBName(fib->fib_FileName,M2A)) goto out; } } 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; int 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,&error); SHOWVALUE(count); if(error == OK && (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 (error != OK) { SHOWMSG("error whilst scanning"); SHOWVALUE(error); fib->fib_DiskKey = -1; error = MapErrnoToIoErr(error); 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, const UBYTE * 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)) { ULONG type = ec->ec_Type; struct ExAllData * ed; BOOL take_it = TRUE; ULONG size; 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) { if(!TranslateCName(ed->ed_Name,M2A)) take_it = FALSE; } 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; 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; int 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"); if(smba_getattr(ln->ln_File,&st,&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,&error); SHOWVALUE(count); if(error == OK && (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 (error != OK) { SHOWMSG("error whilst scanning"); eac->eac_LastKey = (ULONG)-1; error = MapErrnoToIoErr(error); 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, const void * 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; int 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) { if(!TranslateCName(name,A2M)) { error = ERROR_INVALID_COMPONENT_NAME; goto out; } } 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; int ignored_error; smba_stat_t st; if(smba_open(ServerData,full_name,full_name_size,open_read_only,open_dont_truncate,&file,&ignored_error) == OK && smba_getattr(file,&st,&ignored_error) == 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,&ignored_error); } else { /* What's that? */ error = ERROR_ACTION_NOT_KNOWN; goto out; } /* Create a new file? */ if(create_new_file) { int ignored_error; 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); if(smba_open(ServerData,parent_path,strlen(full_name)+3,open_read_only,open_dont_truncate,&dir,&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); if(smba_create(dir,base_name,&st,&error) < 0) { SHOWMSG("didn't work."); SHOWVALUE(error); smba_close(dir,&ignored_error); error = MapErrnoToIoErr(error); SHOWVALUE(error); goto out; } SHOWMSG("good."); smba_close(dir,&ignored_error); } /* Now for the remainder... */ if(smba_open(ServerData,full_name,full_name_size,action != ACTION_FINDINPUT,create_new_file,&fn->fn_File,&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; int error = OK; ENTER(); SHOWVALUE(length); if(length > 0) { result = smba_read(fn->fn_File,mem,length,fn->fn_Offset,&error); 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; int error = OK; ENTER(); if(WriteProtected) { error = ERROR_DISK_WRITE_PROTECTED; goto out; } SHOWVALUE(length); if(length > 0) { result = smba_write(fn->fn_File,mem,length,fn->fn_Offset,&error); if(result < 0) { error = MapErrnoToIoErr(error); 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) { int ignored_error; Remove((struct Node *)fn); smba_close(fn->fn_File,&ignored_error); 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; smba_stat_t st; LONG offset; int error; ENTER(); switch(mode) { case OFFSET_BEGINNING: offset = position; break; case OFFSET_CURRENT: offset = fn->fn_Offset + position; break; case OFFSET_END: if(smba_getattr(fn->fn_File,&st,&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 = 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) { LONG result = -1; smba_stat_t st; int error; long offset; dword size; ENTER(); if(WriteProtected) { error = ERROR_DISK_WRITE_PROTECTED; goto out; } if(smba_getattr(fn->fn_File,&st,&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; } size = (dword)offset; if(smba_setattr(fn->fn_File,NULL,&size,&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, const void * bcpl_name, const 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; int 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) { if(!TranslateCName(name,A2M)) { error = ERROR_INVALID_COMPONENT_NAME; goto out; } } 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); if(smba_open(ServerData,full_name,full_name_size,open_read_only,open_dont_truncate,&file,&error) < 0) { error = MapErrnoToIoErr(error); goto out; } if(smba_getattr(file,&st,&error) < 0) { error = MapErrnoToIoErr(error); goto out; } seconds = (ds->ds_Days * 24 * 60 + ds->ds_Minute) * 60 + (ds->ds_Tick / TICKS_PER_SECOND); memset(&st,0,sizeof(st)); st.atime = -1; st.ctime = -1; st.mtime = seconds + UNIX_TIME_OFFSET + GetTimeZoneDelta(); if(smba_setattr(file,&st,NULL,&error) < 0) { error = MapErrnoToIoErr(error); goto out; } result = DOSTRUE; out: FreeMemory(full_name); if(file != NULL) { int ignored_error; smba_close(file,&ignored_error); } (*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; int error; LONG seconds; STRPTR name; LONG name_len; LONG i; ENTER(); if(smba_getattr(fn->fn_File,&st,&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]; name_len -= i+1; break; } } 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 >= sizeof(fib->fib_FileName)) { 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 { /* Will the name fit? */ if(name_len >= sizeof(fib->fib_FileName)) { error = ERROR_INVALID_COMPONENT_NAME; goto out; } ConvertCString(fib->fib_FileName,sizeof(fib->fib_FileName),name,name_len); if(TranslateNames) { if(!TranslateBName(fib->fib_FileName,M2A)) { error = ERROR_INVALID_COMPONENT_NAME; goto out; } } } 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; int 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); if(smba_open(ServerData,full_name,full_name_size,open_read_only,open_dont_truncate,&ln->ln_File,&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; int 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); if (smba_open(ServerData,full_name,full_name_size,open_read_only,open_dont_truncate,&ln->ln_File,&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; int 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( const void * bcpl_name, LONG * error_ptr) { LONG result = DOSFALSE; int error = OK; STRPTR old_name; STRPTR new_name; const 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; int 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; int 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, const void * bcpl_name, const void * 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]; int 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) { if(!TranslateCName(name,A2M)) { error = ERROR_INVALID_COMPONENT_NAME; goto out; } } 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); if (smba_open(ServerData,full_name,full_name_size,open_read_only,open_dont_truncate,&file,&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) { int ignored_error; smba_close(file, &ignored_error); } (*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; int 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 */ } if (smba_lockrec (fn->fn_File, offset, length, umode, 0, (long)timeout, &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; int error; /* Sanity checks... */ if(offset < 0 || length <= 0 || offset + length < offset) { error = ERROR_RECORD_NOT_LOCKED; goto out; } if (smba_lockrec (fn->fn_File, offset, length, 2, -1, 0, &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; } LocalFPrintf(ZERO, "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; case ACTION_READ_LINK: /* Return -1 for ACTION_READ_LINK, for which DOSFALSE (= 0) * would otherwise be a valid response. Bug fix contributed * by Harry 'Piru' Sintonen. */ res1 = -1; res2 = ERROR_ACTION_NOT_KNOWN; break; default: D(("Anything goes: dp->dp_Action=%ld (0x%lx)",dp->dp_Action,dp->dp_Action)); res1 = 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) LocalFPrintf(ZERO, "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); }