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