From ed8def52098ffa73d7e201aed817e79f10afd586 Mon Sep 17 00:00:00 2001 From: Olaf Barthel Date: Tue, 24 May 2005 13:22:14 +0000 Subject: [PATCH] - Initial import into sourceforge.net CVS. git-svn-id: file:///Users/olsen/Code/migration-svn-zu-git/logical-line-staging/amiga-smbfs/trunk@3 26594b9e-b914-4e86-b7a1-9402bd427170 --- documentation/history.doc | 563 +++ documentation/smbfs.doc | 445 ++ source_code/COPYING | 339 ++ source_code/GNUmakefile.68k | 69 + source_code/GNUmakefile.os4 | 73 + source_code/assert.c | 428 ++ source_code/assert.h | 121 + source_code/crypt.c | 712 +++ source_code/include/smb/smb.h | 83 + source_code/include/smb/smb_fs.h | 84 + source_code/include/smb/smb_fs.h.1 | 60 + source_code/include/smb/smb_fs_sb.h | 50 + source_code/include/smb/smb_mount.h | 34 + source_code/include/smb/smbno.h | 269 ++ source_code/main.c | 6384 +++++++++++++++++++++++++++ source_code/proc.c | 2616 +++++++++++ source_code/smakefile | 118 + source_code/smb_abstraction.c | 1488 +++++++ source_code/smb_abstraction.h | 86 + source_code/smbfs.h | 159 + source_code/smbfs_rev.h | 6 + source_code/smbfs_rev.rev | 1 + source_code/sock.c | 524 +++ source_code/system_headers.c | 29 + source_code/system_headers.h | 121 + 25 files changed, 14862 insertions(+) create mode 100644 documentation/history.doc create mode 100644 documentation/smbfs.doc create mode 100644 source_code/COPYING create mode 100644 source_code/GNUmakefile.68k create mode 100644 source_code/GNUmakefile.os4 create mode 100644 source_code/assert.c create mode 100644 source_code/assert.h create mode 100644 source_code/crypt.c create mode 100644 source_code/include/smb/smb.h create mode 100644 source_code/include/smb/smb_fs.h create mode 100644 source_code/include/smb/smb_fs.h.1 create mode 100644 source_code/include/smb/smb_fs_sb.h create mode 100644 source_code/include/smb/smb_mount.h create mode 100644 source_code/include/smb/smbno.h create mode 100644 source_code/main.c create mode 100644 source_code/proc.c create mode 100644 source_code/smakefile create mode 100644 source_code/smb_abstraction.c create mode 100644 source_code/smb_abstraction.h create mode 100644 source_code/smbfs.h create mode 100644 source_code/smbfs_rev.h create mode 100644 source_code/smbfs_rev.rev create mode 100644 source_code/sock.c create mode 100644 source_code/system_headers.c create mode 100644 source_code/system_headers.h diff --git a/documentation/history.doc b/documentation/history.doc new file mode 100644 index 0000000..0e8e67c --- /dev/null +++ b/documentation/history.doc @@ -0,0 +1,563 @@ +smbfs 1.19 (27.11.2000) + +- Fixed ACTION_FINDOUTPUT implementation; the memory to hold the + name of the file to be created was released before the file + was created. This could cause garbage to be written to the + output file. + +- Fixed ACTION_COPY_DIR implementation; the name of the lock to + be duplicated did not get duplicated. + +- Fixed ACTION_EXAMINE_OBJECT and ACTION_EXAMINE_NEXT + implementations; protection bits are now reported 'properly' + with respect to read/write access. + + +smbfs 1.20 (27.11.2000) + +- ACTION_EXAMINE_NEXT now filters out SMB names that include + '/' or ':' characters. + + +smbfs 1.21 (28.11.2000) + +- Modified ACTION_RENAME_DISK to remove the volume node before + changing it. It also sends the appropriate diskremove/diskinserted + input events now. + +- Reorganized and rewrote the code to reduce dependencies on + the compiler runtime library. + +- Unified error reporting code. + +- SMBFS can now be launched from Workbench using the same parameters + you would use when starting it from Shell. Error reporting + has been adapted to show an error requester and to pool error + messages rather than printing them to the console. + + +smbfs 1.22 (30.11.2000) + +- Fixed the test that looks for file/lock access mode collisions. + +- No longer changes the case of the password; there is now an + option to make the change. + +- Fixed ACTION_SAME_LOCK which was comparing the same lock with + itself. + +- Added support for ACTION_EXAMINE_ALL, ACTION_EXAMINE_ALL_END + and ACTION_WRITE_PROTECT packet types. + +- No longer allows for files and locks to refer to "." and "..". + The use of the backslash character '\' is also disallowed in + Amiga file/directory names. + + +smbfs 1.23 (30.11.2000) + +- In the ExNext()/ExAll() handling functions local error variables + were shadowing each other. Fixed. + +- The error reporting requester now also lists the name of the + service to connect to. This should help in telling different + invocations of the program apart. + +- The size of the directory cache is now a configurable option. + +- Added a new option which forces all name comparisons to be + done in a case-sensitive fashion. + +- Reading/writing data to/from a file in chunks larger than + 65535 bytes could fail and cause the file system to hang. + + +smbfs 1.24 (1.12.2000) + +- Added an option to allow hidden files to be omitted from + directory listings. + +- Improved upon the error reporting in the code that directly + calls the TCP/IP API functions. + +- Reorganized the AmigaDOS packet handling switch..case + list. + +- Added support for ACTION_MORE_CACHE packet type. The + cache in this context refers to the directory scanning + cache. + +- Improved upon the host lookup error reporting code. + +- Added support for file name translation such as via the + CrossDOS translation table files. + + +smbfs 1.25 (2.12.2000) + +- SMBFS now displays some configuration information as soon as + it has successfully connected to the file server and is ready + for action. This output can be suppressed with the new + "QUIET" command line parameter. + +- When removing a directory, the error code returned is examined + more closely to see whether the directory was not yet empty. + +- When setting the protection bits of a file or directory, the + file system now treats either 'protected from deletion' and + 'protected from writing' as indications to mark a file as + write protected. Previously, a file had to be both protected + from deletion and from writing to be treated as write protected. + +- Cleaned up the program's data structures and eliminated all + static data that would hold state information. The entire + client side is now completely reentrant. + + +smbfs 1.26 (3.12.2000) + +- File and directory names are no longer duplicated in the + SMB abstraction layer. The strings supplied by the Amiga + file system layer are referenced instead. + +- Changed the way the ACTION_MORE_CACHE implementation returns + its result code to be compatible with current practices. + + +smbfs 1.27 (4.12.2000) + +- The changes I made to the file read command in order to allow it + to read more than 65535 bytes at a time did not allow read + attempts at the end of the file to come out properly. Fixed. + +- Integrated password encryption code lifted from Samba. Hope + it works as it should, because if it doesn't, I wouldn't know + how to fix it... + + +smbfs 1.28 (4.12.2000) + +- Oh dear, more fixes to the password encryption code and the + connection setup to follow it. This time I managed to test the + code briefly with Samba and password encryption enabled. + Amazingly, it seems to work this time! + + +smbfs 1.29 (6.12.2000) + +- Writes larger than 65535 bytes could stall and slow down the + file system. Fixed. [Thore Böckelmann] + +- The ACTION_MORE_CACHE implementation never returned the total + number of cache entries allocated. Fixed. [Thore Böckelmann] + +- Plain text passwords didn't seem to work any more after I made + the changes to allow for encrypted passwords to be used. Fixed. + + +smbfs 1.30 (6.12.2000) + +- Changed the structure of the share names by which the file system + refers to the data on the server; they all start with a backslash + now. This seems to help NT and doesn't break the rest. + +- For file shares exported using 'share level security', no passwords + will be transmitted. + + +smbfs 1.31 (7.12.2000) + +- The ACTION_EXAMINE and ACTION_EXAMINE_FH implementations now verify + that the name to return will fit into the FileInfoBlock's limited + buffer space. + +- Deleting files and directories from a directory currently being + scanned via ACTION_EXAMINE_NEXT or ACTION_EXAMINE_ALL will now cause + the directory scanner to restart. This is ugly, I know, but at least + it won't cause the scanning process to hickup or lose entries + directory entries during its course of duty. The flip side is that + the scanner will probably return some entries twice. There must + be a better way to implement this, but for now, this is it. + +- Tried to make device and volume node removal safer. + +- SMBFS now always adds a device node to go along with the volume node. + It's safer that way because for every file system you either have a + single device node in the system (and no volume node) or a volume + node *and* a device node. If the device node is missing, there's + typically something wrong and some application software cannot handle + this case properly. SMBFS will attempt to add a new, unique device + node every time it is launched. + +- When specifying the name of the file system device to add, SMBFS + now complains if that name already exists. + + +smbfs 1.32 (8.12.2000) + +- File and directory modification dates were always off by six hours and + one second. I don't know how that happens, it doesn't seem to be a bug + in the date conversion routines. I worked around the problem by adding + and subtracting an adjustment value. + + +smbfs 1.33 (10.12.2000) + +- The ACTION_PARENT_DIR implementation did not return a ZERO lock for + the root directory. Fixed. [Rudolph Riedel] + +- Replaced the doubly-linked Exec lists with skip lists which + are better suited for the repeated lookup operations that + are so common in the file system. This time the list code + seems to work properly. + +- Added -- largely untested -- time and date decoding routines for + directory entries returned by NT. + + +smbfs 1.34 (10.12.2000) + +- Had to take out the skip list code again since it did not seem to + work reliably. + +- Further tweaking to the NT date and time decoding code. + + +smbfs 1.35 (10.12.2000) + +- Small modification to the ACTION_EXAMINE_NEXT/ACTION_EXAMINE_ALL code + which tries to work around a strange effect caused by Amiga Samba + returning the contents of an empty drawer. + + +smbfs 1.36 (11.12.2000) + +- More fixes to the NT date and time conversion code and the + strange six hour fixup that seems to be necessary for the + 'regular' time conversion to work. + + +smbfs 1.37 (11.12.2000) + +- Replaced the entire 'regular' date and time conversion code. The + strange six hour fixup is no longer necessary. + + +smbfs 1.38 (11.12.2000) + +- Ditched the packet size kludges in "sock.c" and "proc.c" I had put + in there. It seems that only the 'smba_write()'/'smba_read()' code + could trigger it, and that code was fixed quite a while ago. + +- Migrated some code changes from the Samba source over to the + directory scanner. + +- Maximum transmission size per packet is now the same as the + number the server allowed us to use rather than a fixed number + imposed by VM page sizes. + +- Tried to clean up more of the code. + +- Discovered that the 'smb_valid_packet()' routine never could have + worked and fixed it so that it now finally does what it should do. + +- Better error propagation for some routines in "proc.c". + +- More streamlined error testing code. + + +smbfs 1.39 (11.12.2000) + +- Almost fixed several bugs in 'smb_proc_readdir_long()'. + +- 'smb_proc_readdir_long()' does not work correctly. It only seems to + work, but will fail if the scanner is to restart before the entire + directory is read. Restored the old behaviour :( + + +smbfs 1.40 (13.12.2000) + +- Took out some more unused and unnecessary code. + +- Rewrote the time and date conversion routines. + +- Modified the directory reading code so that it no longer relies + upon the last directory entry index which doesn't seem to be + correct most of the time. + +- Directory scanning errors are now flagged as such. + + +smbfs 1.41 (16.12.2000) + +- In the SMB abstraction layer, the directory cache is now invalidated + only if the respective action that could have invalidated it succeeds. + +- Now clears the directory cache only if the respective action would + have affected the directory being cached. + +- In debug mode [Ctrl]+F will display a list of open files and allocated + locks. + +- Added very silly support for ACTION_SET_COMMENT packet. + +- Found an interesting bug fix for 'smb_proc_readdir_long()' in the + Linux 2.4.0 kernel which seemed worth integrating into the file system. + + +smbfs 1.42 (19.12.2000) + +- Integrated Chris Hertel's broadcast name query code. This means that + the name of the host whose file shares you want to access need no + longer be in the hosts database. However, you still cannot refer to + a host solely by its IP address. + + +smbfs 1.43 (21.12.2000) + +- The integrated broadcase name query code would conflict with the + DNS lookup. Fixed. [Thore Böckelmann] + +- The NetBIOS name query code now retries four times to get a response + out of the network. + +- Put the mysterious 6 hour time offset fix back in. + + +smbfs 1.44 (29.12.2000) + +- Rewrote the host lookup error reporting code to use the appropriate + API function. + +- Discovered that all the functionality required by SMBFS was supported + by the original free AmiTCP 3.0 release. Changed the "bsdsocket.library" + open parameters. + +- The ACTION_EXAMINE_OBJECT implementation consistently got the name of + the root directory wrong. Fixed. + + +smbfs 1.45 (7.1.2001) + +- Discovered why file and directory creation dates were wrong by about + six hours. Fixed. + +- The order of the parameters passed to the 'ExAll()' filter hook was + wrong. Fixed [Thorsteinn Sveinsson and Matt Sealey]. + + +smbfs 1.46 (1.2.2001) + +- Removed the workarounds for the mysterious 'off by six hours' bug. + The time delta value added was consistently wrong. + + +smbfs 1.47 (18.2.2001) + +- The name of the domain/workgroup, the name of the user and the + password to be used can now be preset using environment variables. + +- Changed the command line argument template to work better with the + newly introduced environment variable settings. + + +smbfs 1.48 (3.3.2001) + +- Modified the command template so that the final parameter can no + longer cause all other parameters to be swallowed. Also updated + the documentation. + + +smbfs 1.49 (3.3.2001) + +- Handling of ACTION_FINDUPDATE needed improving. It should have + created a file that didn't already exist. It now does [David Gerber]. + + +smbfs 1.50 (4.3.2001) + +- ACTION_FINDUPDATE is now associated with a shared lock. It used + to be an exclusive lock [David Gerber]. + +- The test to follow ACTION_FINDUPDATE that determines whether the + file to open already exists now also tries to 'stat()' the file + in question to find out whether it exists. + + +smbfs 1.51 (13.3.2001) + +- Whoops. The new code to pull data from environment variables didn't + actually check whether the work group name could be set up properly + and thus didn't complain about it either. It now notifies you if + no work group name was provided and none could be found. + + +smbfs 1.52 (22.6.2001) + +- Added another option which allows you to preset the time zone offset + to use when translating between the local time and the SMB server + time. Using the default locale doesn't work for that purpose all the + time since there are so many misconfigured servers out there. + +- The ACTION_DIE packet didn't have the desired effect if the file + system didn't have a reason to wait for any resources to be + released again. It just wouldn't quit. Now it does. + +- Before it exits, the file system no longer posts a disk change + event if it was launched from Workbench. I found that the steps + taken so far could end up deadlocking Workbench. + + +smbfs 1.53 (26.6.2001) + +- Changed the file name parser; it should now be possible to tie assigns + to files and directories on an smb volume. Since the updated file name + translation code also is a bit smarter about when to use which + file name separator character, accesses to the media should be a + bit faster, too. + +- When translating path names, trailing '/' characters are now properly + parsed, i.e. Lock("/",...) should always bring up the parent directory + [Matt Sealey]. + +- Duplicating a ZERO lock now works as expected. + + +smbfs 1.54 (27.6.2001) + +- Directory and file creation didn't work that properly after applying + the changes I made to the parser. Fixed [Matt Sealey]. + + +smbfs 1.55 (3.3.2002) + +- At least samba 2.2.2-12 on linux can send total_data of 0 bytes. This + caused sock.c/smb_receive_trans2() malloc() (==AllocVecPooled) to fail + on smba_readdir(). Fixed [Harry Sintonen]. + +- Added quickfix against crash if smb_abstraction.c/smb_smba_readdir() + fails. This is by no means a real fix, but at least it doesn't crash + [Harry Sintonen]. + +- There is definitely something fishy in the smbfs restart/retry scheme, + it just doesn't work properly (all state data has to be, and is, + reset when restarts occur, but still it tries to use the old data as if + nothing happened). Someone should really take a closer look at this + [Harry Sintonen]. + + +smbfs 1.56 (3.9.2002) + +- Rewrote the Setup() code which allocates and initializes the device and + volume nodes. Lost all Forbid()..Permit() pairs on the way, making the + code more robust and less weird. + +- In proc.c/smb_proc_readdir_long() now specifically watches for the + smb_trans2_request() function return empty data and parameter values. + This is now treated as an attempt to read from an empty directory. + Previously, there was a slight change that the information was + taken seriously even if NULL was returned. + + +smbfs 1.57 (3.9.2002) + +- Ran the code through GCC and rewrote the code to get rid of the compiler + warnings that appeared to have something more serious about them. This + includes, for example, comparisons between signed and unsigned values. + + +smbfs 1.58 (5.9.2002) + +- Rebuilt using my own TCP/IP stack SDK. Rebuilding the code with the + Miami SDK should still be possible, though. + +- Changed the way smb_proc_readdir_long() aborts the job if no valid + data is read. + + +smbfs 1.59 (10.9.2003) [Harry Sintonen ] + +- Fixed ACTION_DELETE_OBJECT to restart directory scanning, it + searched for wrong object. Fixes the problem where every other + file was skipped when deleting directory. + + +smbfs 1.60 (11.2.2004) [Peter Riede ] + +- Fixed ACTION_SEEK, it doesn't works if followed by ACTION_WRITE. + Fixes the problem by using the SMBlseek packet instead of just + remembering the position in the local FileNode structure + +- Fixed ACTION_SETFILESIZE, has returned an errorcode of 37 instead + of 0L on success. + +- Added support for the ACTION_LOCK_RECORD and ACTION_UNLOCK_RECORD + packets. + + +smbfs 1.61 (27.4.2004) + +- Reworked the ACTION_SEEK implementation. Turns out that the SMB + lseek packet only seems to work for the Samba server but produces + unpredictable results with Microsoft flavoured servers. The new + code, which helps to avoid trouble with overlapping write access + and seeks, now performs much like the old one except that it + "notifies" the SMB server of the new file position to be used. + Tested both with Samba and Windows XP: works as it should. + + +smbfs 1.62 (8.5.2004) + +- Added sanity checks to the record locking code. For example, + locking a record of length 0 must be considered illegal. Also, + since the protocol deals with 32 bit signed integers for + file position and record length, parameters that are "too large" + and come out as negative values are now rejected. + + +smbfs 1.63 (9.5.2004) + +- Added more sanity checks to the record locking code, this time + to avoid integer overflows. + +- Added support for the SMB 'archive' and 'system' file attributes, + which are mapped to the Amiga 'archived' and 'pure' protection + bits. + +- Cleaned up the header files. + + +smbfs 1.64 (13.5.2004) + +- ExamineFH() did not fill in the file protection bits. Fixed. + +- Ported to AmigaOS4. + + +smbfs 1.65 (16.5.2004) + +- The port numbers of the NetBIOS session and name services are no + longer hard-coded. They are now looked up (netbios-ns/udp and + netbios-ssn/tcp), and if that fails, the hard-coded default port + numbers are used instead. + +- Tried to reduce the risk of triggering buffer overflows by replacing + calls to strcpy/strcat. This revealed weaknesses in a number of + places, including the code that fills in the names of the directory + entries. + +- Instead of calling memcpy(), the code now invokes exec.library/CopyMem(), + which should be a wee bit faster than what the 'C' runtime library can do. + +- Merged with Peter Riede's changes: for small packets to be sent, + SMBwrite is used instead of SMBwritebraw (which greatly enhances + performance), the server max_xmit field is initialized differently + if the session was opened with a SMBtconx packet and ACTION_SEEK + no longer sets the error code to an undefined value in case of + success. + + +smbfs 1.66 (10.6.2004) + +- Added a paranoia check to the ExAll() implementation which should + avoid trouble with extremely short data buffers. diff --git a/documentation/smbfs.doc b/documentation/smbfs.doc new file mode 100644 index 0000000..e16e804 --- /dev/null +++ b/documentation/smbfs.doc @@ -0,0 +1,445 @@ +A SMB file system wrapper for AmigaOS, using the AmiTCP V3 API +============================================================== + +1. What is it? + +This document briefly describes the SMBFS program, which implements an SMB +file system for AmigaOS. This file system can be used to access files made +available by file servers which implement the SMB protocol, such as 'Microsoft +Windows' or any other platform which supports the free 'Samba' product. These +files can be accessed using shell commands such as 'List', the Workbench or +utilities such as 'Directory Opus' as if the file server were a local disk +drive. + + +2. What do you need to get started? + +You need a TCP/IP stack that supports the AmiTCP V3 API, such as 'Miami', the +original free 'AmiTCP 3.0' release, 'AmiTCP 4.x', 'Miami Deluxe' or 'AmiTCP +Genesis' and the obligatory networking gear. All these items need to be in +good shape and properly configured. + +Most important, you need a computer which exports file sharing services using +the SMB protocol. + +It often helps to have 'Samba' installed on your Amiga, too, as this can aid +in tracking down bugs and obtaining information which SMBFS cannot obtain all +by itself. + +Last but not least, you need to be proficient in configuring and using the +TCP/IP stack; networking knowledge is definitely assumed. + +SMBFS requires AmigaOS 2.04 or higher to work. + + +3. Preparations + +You need to know which computer's files you want to share using the SMBFS file +system. That computer must be known by name, it is not sufficient just to know +its IP address. If you know the IP address but cannot refer to the host by its +name then SMBFS will not work. In that case, make sure that you add a host +name entry referring to the IP address to your TCP/IP stack's host database +(e.g. the "AmiTCP:bin/hosts" file or the corresponding page in the stack's +configuration user interface). + +The name of the computer to connect to must not be too long. If it is longer +than 16 characters, SMBFS will not work properly. + +You need to know which service you want to connect to on the target computer. +You can find out which services are available on a certain computer by using +the Samba 'smbclient' program. For example, if you were to query the services +offered by a machine called 'sourcery' you could enter the following: + + samba:bin/smbclient -L sourcery + +And you might get the following information: + + added interface ip=192.168.0.1 bcast=192.168.0.255 nmask=255.255.255.0 + Password: Domain=[ARBEITSGRUPPE] OS=[AmigaOS] Server=[Samba 2.0.7] + + Sharename Type Comment + --------- ---- ------- + All Disk All volumes in the system + IPC$ IPC IPC Service (Amiga 3000UX) + olsen Disk Home Directories + + Server Comment + --------- ------- + SOURCERY Amiga 3000UX + + Workgroup Master + --------- ------- + ARBEITSGRUPPE SOURCERY + +The share name to connect to would be "ALL" in this case. + +You need to know which login name and which password are required to connect +to the shared resource, and you need to know the name of the workgroup or +domain the file server is a member of. + + +4. Starting and stopping the file system + +SMBFS is an uncommon kind of file system in that you do not use the 'Mount' +command to mount it. In fact, SMBFS is a shell program which can be launched +from the shell, using command line parameters to tell it which resources +should be used. But you can also start it from Workbench: in this case you +would have to put the program's command line options into icon tool types. + +By now you should have assembled the following information: + + - Name of the computer to connect to; this would be the + file server + - Name of the shared SMB resource to connect to + - Login name and password + +That's basically everything you need to know to continue -- unless something +goes wrong, but more on that lateron. + +Now you can start the file system. For example, to connect to the file server +called 'sourcery' and the shared 'all' resource it exports, with that computer +being a member of the workgroup 'Arbeitsgruppe', using the login name +'PCGuest' and not providing any password you would enter the following: + + Run >NIL: SMBFS Workgroup=Arbeitsgruppe User=PCGuest Service=//sourcery/all + +This would cause a new device by the name of "SMBFS:" to be mounted, showing +all files and directories the 'sourcery' server makes available for sharing. + +How do you 'unmount' the file system? That's very easy, just check the output +of the 'Status' shell command. You might get the following output: + + Process 1: Loaded as command: TURBOTEXT + Process 2: Loaded as command: Work:Tools/Blowup + Process 3: Loaded as command: Work:Tools/Sashimi + Process 4: Loaded as command: Work:CyberTools/CyberGuard + Process 5: Loaded as command: Work:Tools/OpenDevicePatch + Process 6: Loaded as command: CED + Process 7: Loaded as command: Workbench + Process 8: Loaded as command: Status + Process 9: No command loaded + Process 10: Loaded as command: SMBFS '//sourcery/all' + +Look at the last line describing process number 10: it shows the name of the +file system program SMBFS and the name of the SMB share it is connected to. To +stop this file system and effectively unmount it, use the shell 'Break' +command; in this case you would enter "Break 10" to stop the file system. Note +that the program may not terminate immediately; it may have to wait until the +last client has released all resources referring to the file system. You may +have to send more than one "Break" command to stop the program. + + +5. Startup options + +The SMBFS program supports a number of command line options, as will be +described below. The command template looks like this: + + 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 + +The individual options serve the following purposes: + + DOMAIN=WORKGROUP/K + + You must specify the name of the work group or domain which the file server + to connect to is a member of. The name of this workgroup or domain must not + be longer than 16 characters. The name you provide will be translated to + all upper case characters. + + You need not provide for a work group or domain name on the command line. + Alternatively, you may configure an environment variable whose contents + will be used instead. The variable could be set up like this: + + SetEnv smbfs_workgroup + Copy ENV:smbfs_workgroup ENVARC: + + You may also use the 'smbfs_domain' environment varilable in place of the + 'smbfs_workgroup' variable. The two are aliases for one another, but + smbfs will read only one of the two. + + USER=USERNAME/K + + To connect to an SMB share you must authenticate yourself by providing a + user name. With this program the user name is optional; if you do not + provide one, SMBFS will use the default, which is "GUEST". The user name + must not be longer than 64 characters. The name you provide will be + translated to all upper case characters. + + You need not provide for a user name on the command line. Alternatively, + you may configure an environment variable whose contents will be used + instead. The variable could be set up like this: + + SetEnv smbfs_username + Copy ENV:smbfs_username ENVARC: + + You may also use the 'smbfs_user' environment varilable in place of the + 'smbfs_username' variable. The two are aliases for one another, but + smbfs will read only one of the two. + + PASSWORD/K + + As part of the authentication process required to make the connection to + an SMB share, you must provide for a user name and a password. The + password is optional; if you do not provide one, an empty password will + be transmitted. The password must not be longer than 64 characters. + + You need not provide for a password on the command line. Alternatively, + you may configure an environment variable whose contents will be used + instead. The variable could be set up like this: + + SetEnv smbfs_password + Copy ENV:smbfs_password ENVARC: + + Keep in mind that passwords like these really should not be exposed by + storing them in environment variables. But then the protocol smbfs uses + is almost as insecure as it gets anyway. + + The authentication process only works if the machine you are connecting + to knows about the user name and password you want to use. As of this + writing, smbfs cannot be used for authenticating against a password + server that is not the same machine as the one from which you wish to + import a share. + + CHANGECASE/S + + By default the password will not be changed to all upper case characters. + If this is required, you should either provide the password in this + form or resort to this option, which will cause it to be translated + to all upper case characters. + + CASE=CASESENSITIVE/S + + Some file servers treat file and directory names differently which + differ only in whether they are written using upper/lower case + characters. For these servers you should activate the CASESENSITIVE + switch to treat those files properly. There is a catch though: the + AmigaDOS file naming scheme does not follow this model and you may + run into problems when you are trying to use it. By default, the + file system does not treat file and directory names differently + which only differ with respect to the case of letters. + + OMITHIDDEN/S + + When requesting a directory listing, the server may return some files + and directories tagged as being hidden. By default this file system + will report these 'hidden' entries anyway, but you can request + specifically that what is intended to be hidden should be omitted + from directory listings, too. Note that even though a file may be + hidden you should still be able to open and examine it. + + QUIET/S + + When started from Shell, SMBFS will print a message as soon as the + connection to the file server has been established. If you do not + want to see that message displayed, use the QUIET parameter. Also, + no such message will appear if the program has been started to run + in the background. + + CLIENT=CLIENTNAME/K + + SMBFS will attempt to connect to the file server by providing the name + of the computer you connect from. In some cases this may be undesirable + as the computer's name differs from what the file server expects. You + can use the CLIENT parameter to tell SMBFS under which name it should + announce itself to the server. This parameter is optional and will be + translated to all upper case characters; it cannot be longer than 16 + characters. + + SERVER=SERVERNAME/K + + SMBFS will attempt to connect to the file server by providing the name + you specified using the SERVICE command line parameter. In some cases + this may be undesirable as the server's name differs from what you + specified as the share name. You can use the SERVER parameter to tell + SMBFS under which name it should contact the server. This parameter is + optional and will be translated to all upper case characters; it cannot + be longer than 16 characters. + + DEVICE=DEVICENAME/K + VOLUME=VOLUMENAME/K + + The SMBFS program can announce itself as an AmigaDOS file system by + using one of two different methods. + + The first method involves announcing itself only as a file system + device. This should be sufficient in most cases but has a drawback in + that the device will not be usable from Workbench since the file system + will not appear as a disk icon. You tell SMBFS to use a specific device + name by using the DEVICE command line parameter, e.g. "DEVICE=SMBFS:". + Note that device names must be unique, i.e. there must be no other + device by the same name in the system; SMBFS will report an error and + exit if it finds one. + + The second method involves announcing itself as a volume. This has the + benefit of making the file system usable from Workbench since a disk + icon will appear for it. You tell SMBFS to use a specific volume name by + using the VOLUME command line parameter, e.g. "VOLUME=Sourcery:". + + Both methods have advantages and drawbacks. The drawback of the VOLUME + method is that it may deadlock the native Amiga Samba port as soon as + the file system is mounted. The drawback of the DEVICE method is that + the file system will not be usable from Workbench. + + If you wish, you can combine both methods; this is the approach most + other file systems use. And in fact, when you tell SMBFS to add a + volume it will also add a device to go along with it. + + The VOLUME and DEVICE keywords are optional; if you omit both, SMBFS + will pretend that you had used the "DEVICE=SMBFS:" parameter. + + CACHE=CACHESIZE/N/K + + The file system attempts to optimize accesses to the file server when + directory contents are being scanned. These contents are buffered in + a directory cache which by default will hold 170 entries. Since each + entry will require about 255 bytes of storage, the entire 170 entry + cache will occupy more than 40K bytes of memory. You may want to change + this requirement, by making the cache smaller or larger using the + CACHESIZE parameter. The size of the directory cache cannot be smaller + than 10 entries. + + DEBUGLEVEL=DEBUG/N/K + + By default SMBFS operates in silent mode. It does not report what it is + doing, it just tries to respond to file system requests. To obtain + debugging output you may want to use the DEBUG option and specify a + debug level greater than 0. The larger the number you specify the more + debugging output will be created. Note that all debugging output will be + produced using the operating system's debug output functionality which + requires that you have a capturing program like 'Sashimi' running in the + background. + + TZ=TIMEZONEOFFSET/N/K + + By default the file system will use the current Locale settings to + translate between the local time and the time used by the SMB + server. For some configurations, however, this is impractical since + the server's time zone is not configured properly. For these + rare cases you may want to hard code a certain time zone offset + using the 'TIMEZONEOFFSET' options. The number you provide must + be the number of minutes to add to the local time in order to + translate it into the corresponding GMT value. For example, in + central Europe using CET, you would use "TZ=-60" since CET is + one hour ahead of GMT. + + TRANSLATE=TRANSLATIONFILE/K + + The Amiga and the file server SMBFS connects to may not share the same + character set. International characters used in file names on either + side may not come out correctly on the other side. To remedy this + problem, you can resort to file name translation. How the individual + names are to be translated is determined by the contents of a file + name translation table file such as the ones that ship with Workbench + in the "L:FileSystem_Trans" drawer. The first 256 bytes of each such + file must consist of the mapping of Amiga characters to the different + character set, and the second 256 characters must describe a mapping + back from the different character set to the Amiga. In most cases the + "L:FileSystem_Trans/INTL.crossdos" translation table file should be + sufficient. To specify which file contains the translation tables to + use you would use the TRANSLATIONFILE parameter. + + SERVICE/A + + This is the last parameter to be specified on the command line. It + should refer to the file server you want to connect to and the resource + it exports, e.g. a file system. This parameter must start with two + slashes which must be immediately followed by the file server's name, + which must be followed by the resource to connect to. + + For now no special characters are allowed in the name of the service + as no translation is performed like would be the case for file names + on the volume. + +The same parameters are also used when starting SMBFS from Workbench. SMBFS +will examine its icon tool types and use these in place of the shell command +line. + + +6. Known problems + +The design of smbfs follows the original file system concept behind the +code which the 'Sharity-Light' file system is based upon. And that is a +Unix file system which differs from Amiga specific file systems in many +ways which can lead to problems which are discussed briefly below: + + - Single threaded design + + This means that it is not possible for several programs to fairly + share the use of the file system. For example, a program that posts + a long read request can tie up the file system almost exclusively + for itself, and while it is busy all other clients will have to + wait. Same goes for directory scanning. + + - Poor scalability + + This is associated with the single threaded design. When several + programs are accessing the file system at the same time, overhead + and unfair sharing of resources will drastically reduce the + performance of the file system. + + - Separation of file data and metadata + + This means that the core of the file system treats the contents of + a directory and the data attached to each file inside that + directory as something different. This is a common concept with + Unix file systems, but it is very different with Amiga file systems. + In smbfs this data separation can cause problems when deleting + files from a directory while that directory is being scanned, + such as how this is being done by the 'Delete' shell command. The + effects of these problems are that a directory may not be deleted + even though it is empty or that for the same directory the same + file may be reported twice in the listing. + +While there are no easy solutions for any of these problems, it does not +mean that smbfs is unusable. You just have to be more careful when you +use the file system. For example, if a directory's contents cannot be +deleted due to one of the problems mentioned above, you might want to +retry later. + +It should be noted that the problems described above are not inherent +to the original file system design. It's just that transferring that +design to an Amiga file system created the problems. + + +7. Credits + +This file system is based upon prior work by Paal-Kr. Engstad, Volker +Lendecke, Mark A. Shand, Donald J. Becker, Rick Sladkey, Fred N. van Kempen, +Eric Kasten and Rudolf Koenig. It is a direct descendant of the +'Sharity-Light' file system written by Christian Starkjohann. + +The password encryption code was lifted from the Samba package. It was +written by Andrew Tridgell and the Samba Team. + + +8. Author + +The 'Sharity-Light' source code was adapted and wrapped into an AmigaOS layer +by Olaf `Olsen' Barthel. If you wish to contact me, please send e-mail to the +following address: + + olsen@sourcery.han.de + +Or, alternatively, you might want to contact me via my postal address: + + Olaf Barthel + Gneisenaustr. 43 + D-31275 Lehrte + Federal Republic of Germany + +If you want to submit a bug report or an enhancement request, please enclose +sufficient information to allow me to make sense of the problem. That includes +debugging logs produced using the DEBUG option. + + +9. Source code + +SMBFS is distributed under the terms of the GNU General Public License +(version 2). The source code should have accompanied this program; if it +hasn't, please contact the author for a copy. + +The program was compiled using the SAS/C 6.58 compiler, with the Miami SDK +providing for the TCP/IP stack API header files. diff --git a/source_code/COPYING b/source_code/COPYING new file mode 100644 index 0000000..a43ea21 --- /dev/null +++ b/source_code/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + 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. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/source_code/GNUmakefile.68k b/source_code/GNUmakefile.68k new file mode 100644 index 0000000..81fda5b --- /dev/null +++ b/source_code/GNUmakefile.68k @@ -0,0 +1,69 @@ +# +# $Id: GNUmakefile.68k,v 1.1.1.1 2005-05-24 13:22:06 obarthel Exp $ +# +# :ts=8 +# +# 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. +# + +.c.o: + @echo "Compiling $<" + @gcc -c $(CFLAGS) -o $*.o $< + +############################################################################### + +PROJECT = smbfs + +############################################################################### + +WARNINGS = \ + -Wall -W -Wshadow -Wpointer-arith -Wsign-compare -Wmissing-prototypes \ + -Wundef -Wbad-function-cast -Wmissing-declarations -Wconversion + +CPU = -m68020-60 +OPTIONS = -DNDEBUG -fno-builtin -Inetinclude -Iinclude +OPTIMIZE = -O2 -fomit-frame-pointer +DEBUG = -g + +############################################################################### + +CFLAGS = $(WARNINGS) $(OPTIMIZE) $(DEBUG) $(CPU) $(OPTIONS) -Iinclude -I. +LFLAGS = -nostartfiles -nostdlib -L. + +############################################################################### + +OBJS = main.o proc.o smb_abstraction.o sock.o crypt.o + +############################################################################### + +LIBS = -lc -ldebug -lamiga + +############################################################################### + +all: $(PROJECT) + +$(PROJECT): $(OBJS) + gcc -o $@.debug $(OBJS) $(CFLAGS) $(LFLAGS) $(LIBS) + strip --strip-all -o $@ $@.debug + protect $@ +e + +############################################################################### + +clean: + -delete #?.o $(PROJECT)(%|.debug) diff --git a/source_code/GNUmakefile.os4 b/source_code/GNUmakefile.os4 new file mode 100644 index 0000000..45c7b06 --- /dev/null +++ b/source_code/GNUmakefile.os4 @@ -0,0 +1,73 @@ +# +# $Id: GNUmakefile.os4,v 1.1.1.1 2005-05-24 13:22:06 obarthel Exp $ +# +# :ts=8 +# +# 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. +# + +.c.o: + @echo "Compiling $<" + @$(CC) -c $(CFLAGS) -o $*.o $< + +########################################################################### + +PROJECT = smbfs + +########################################################################### + +CC = ppc-amigaos-gcc + +########################################################################### + +#WARNINGS = \ +# -Wall -W -Wshadow -Wpointer-arith -Wsign-compare -Wmissing-prototypes \ +# -Wundef -Wbad-function-cast -Wmissing-declarations -Wconversion + +WARNINGS = \ + -Wall -W -Wshadow -Wpointer-arith -Wsign-compare -Wmissing-prototypes \ + -Wundef -Wbad-function-cast -Wmissing-declarations + +CPU = -mcpu=604e -msoft-float +OPTIONS = -DNDEBUG -fno-builtin +OPTIMIZE = -O2 -fomit-frame-pointer -fstrength-reduce -finline-functions +DEBUG = -g + +########################################################################### + +CFLAGS = $(WARNINGS) $(OPTIMIZE) $(DEBUG) $(CPU) $(OPTIONS) \ + -I. -I/V/include -I/usr/local/amiga/ppc-amigaos/sys-include -Iinclude -Inetinclude +LFLAGS = -nostartfiles -nostdlib -L. + +########################################################################### + +OBJS = main.o proc.o smb_abstraction.o sock.o crypt.o + +########################################################################### + +LIBS = -lc + +########################################################################### + +all: $(PROJECT) + +$(PROJECT): $(OBJS) + @echo "Linking $@" + @$(CC) -o $@.debug $(CFLAGS) $(LFLAGS) $(OBJS) $(LIBS) -Wl,--cref,-M,-Map=$@.map + ppc-amigaos-strip -R.comment -o $@ $@.debug diff --git a/source_code/assert.c b/source_code/assert.c new file mode 100644 index 0000000..f871645 --- /dev/null +++ b/source_code/assert.c @@ -0,0 +1,428 @@ +/* + * $Id: assert.c,v 1.1.1.1 2005-05-24 13:22:06 obarthel Exp $ + * + * :ts=8 + * + * 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. + */ + +/****************************************************************************/ + +#include + +#include +#include + +#ifdef __SASC +#include +#include +#else +#include +#include +#endif /* __SASC */ + +#include + +extern struct Library * AbsExecBase; + +#define SysBase AbsExecBase + +extern void kprintf(const char *,...); +extern void __stdargs kputc(char c); + +/****************************************************************************/ + +#include + +/****************************************************************************/ + +#define DEBUGLEVEL_OnlyAsserts 0 +#define DEBUGLEVEL_Reports 1 +#define DEBUGLEVEL_CallTracing 2 + +/****************************************************************************/ + +static int indent_level = 0; +int __debug_level = DEBUGLEVEL_CallTracing; + +static char program_name[40]; +static int program_name_len = 0; + +/****************************************************************************/ + +void +_SETPROGRAMNAME(char *name) +{ + if(name != NULL && name[0] != '\0') + { + program_name_len = strlen(name); + if(program_name_len >= sizeof(program_name)) + program_name_len = sizeof(program_name)-1; + + strncpy(program_name,name,program_name_len); + program_name[program_name_len] = '\0'; + } + else + { + program_name_len = 0; + } +} + +/****************************************************************************/ + +int +_SETDEBUGLEVEL(int level) +{ + int old_level = __debug_level; + + __debug_level = level; + + return(old_level); +} + +/****************************************************************************/ + +int +_GETDEBUGLEVEL(void) +{ + return(__debug_level); +} + +/****************************************************************************/ + +static int previous_debug_level = -1; + +void +_PUSHDEBUGLEVEL(int level) +{ + previous_debug_level = _SETDEBUGLEVEL(level); +} + +void +_POPDEBUGLEVEL(void) +{ + if(previous_debug_level != -1) + { + _SETDEBUGLEVEL(previous_debug_level); + + previous_debug_level = -1; + } +} + +/****************************************************************************/ + +void +_INDENT(void) +{ + if(program_name_len > 0) + kprintf("(%s) ",program_name); + + if(__debug_level >= DEBUGLEVEL_CallTracing) + { + int i; + + for(i = 0 ; i < indent_level ; i++) + kprintf(" "); + } +} + +/****************************************************************************/ + +void +_SHOWVALUE( + unsigned long value, + int size, + const char *name, + const char *file, + int line) +{ + if(__debug_level >= DEBUGLEVEL_Reports) + { + char *fmt; + + switch(size) + { + case 1: + + fmt = "%s:%ld:%s = %ld, 0x%02lx"; + break; + + case 2: + + fmt = "%s:%ld:%s = %ld, 0x%04lx"; + break; + + default: + + fmt = "%s:%ld:%s = %ld, 0x%08lx"; + break; + } + + _INDENT(); + + kprintf(fmt,file,line,name,value,value); + + if(size == 1 && value < 256) + { + if(value < ' ' || (value >= 127 && value < 160)) + kprintf(", '\\x%02lx'",value); + else + kprintf(", '%lc'",value); + } + + kprintf("\n"); + } +} + +/****************************************************************************/ + +void +_SHOWPOINTER( + void *pointer, + const char *name, + const char *file, + int line) +{ + if(__debug_level >= DEBUGLEVEL_Reports) + { + char *fmt; + + _INDENT(); + + if(pointer != NULL) + fmt = "%s:%ld:%s = 0x%08lx\n"; + else + fmt = "%s:%ld:%s = NULL\n"; + + kprintf(fmt,file,line,name,pointer); + } +} + +/****************************************************************************/ + +void +_SHOWSTRING( + const char *string, + const char *name, + const char *file, + int line) +{ + if(__debug_level >= DEBUGLEVEL_Reports) + { + _INDENT(); + kprintf("%s:%ld:%s = 0x%08lx \"%s\"\n",file,line,name,string,string); + } +} + +/****************************************************************************/ + +void +_SHOWMSG( + const char *string, + const char *file, + int line) +{ + if(__debug_level >= DEBUGLEVEL_Reports) + { + _INDENT(); + kprintf("%s:%ld:%s\n",file,line,string); + } +} + +/****************************************************************************/ + +void +_DPRINTF_HEADER( + const char *file, + int line) +{ + if(__debug_level >= DEBUGLEVEL_Reports) + { + _INDENT(); + kprintf("%s:%ld:",file,line); + } +} + +#ifdef __SASC + +static void __asm +putch(register __d0 UBYTE c) +{ + if(c != '\0') + kputc(c); +} + +#else + +static void +putch(UBYTE c __asm("d0")) +{ + if(c != '\0') + kputc(c); +} + +#endif /* __SASC */ + +void +_DPRINTF(const char *fmt,...) +{ + if(__debug_level >= DEBUGLEVEL_Reports) + { + va_list args; + + va_start(args,fmt); + RawDoFmt((char *)fmt,args,(VOID (*)())putch,NULL); + va_end(args); + + kprintf("\n"); + } +} + +void +_DLOG(const char *fmt,...) +{ + if(__debug_level >= DEBUGLEVEL_Reports) + { + va_list args; + + va_start(args,fmt); + RawDoFmt((char *)fmt,args,(VOID (*)())putch,NULL); + va_end(args); + } +} + +/****************************************************************************/ + +void +_ENTER( + const char *file, + int line, + const char *function) +{ + if(__debug_level >= DEBUGLEVEL_CallTracing) + { + _INDENT(); + kprintf("%s:%ld:Entering %s\n",file,line,function); + } + + indent_level++; +} + +void +_LEAVE( + const char *file, + int line, + const char *function) +{ + indent_level--; + + if(__debug_level >= DEBUGLEVEL_CallTracing) + { + _INDENT(); + kprintf("%s:%ld: Leaving %s\n",file,line,function); + } +} + +void +_RETURN( + const char *file, + int line, + const char *function, + unsigned long result) +{ + indent_level--; + + if(__debug_level >= DEBUGLEVEL_CallTracing) + { + _INDENT(); + kprintf("%s:%ld: Leaving %s (result 0x%08lx, %ld)\n",file,line,function,result,result); + } +} + +/****************************************************************************/ + +void +_ASSERT( + int x, + const char *xs, + const char *file, + int line, + const char *function) +{ + #ifdef CONFIRM + { + STATIC BOOL ScrollMode = FALSE; + STATIC BOOL BatchMode = FALSE; + + if(BatchMode == FALSE) + { + if(x == 0) + { + kprintf("%s:%ld:Expression `%s' failed assertion in %s().\n", + file, + line, + xs, + function); + + if(ScrollMode == FALSE) + { + ULONG Signals; + + SetSignal(0,SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D | SIGBREAKF_CTRL_E); + + kprintf(" ^C to continue, ^D to enter scroll mode, ^E to enter batch mode\r"); + + Signals = Wait(SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D | SIGBREAKF_CTRL_E); + + if(Signals & SIGBREAKF_CTRL_D) + { + ScrollMode = TRUE; + + kprintf("Ok, entering scroll mode\033[K\n"); + } + else if (Signals & SIGBREAKF_CTRL_E) + { + BatchMode = TRUE; + + kprintf("Ok, entering batch mode\033[K\n"); + } + else + { + /* Continue */ + + kprintf("\033[K\r"); + } + } + } + } + } + #else + { + if(x == 0) + { + _INDENT(); + kprintf("%s:%ld:Expression `%s' failed assertion in %s().\n", + file, + line, + xs, + function); + } + } + #endif /* CONFIRM */ +} diff --git a/source_code/assert.h b/source_code/assert.h new file mode 100644 index 0000000..5e4a9b0 --- /dev/null +++ b/source_code/assert.h @@ -0,0 +1,121 @@ +/* + * $Id: assert.h,v 1.1.1.1 2005-05-24 13:22:06 obarthel Exp $ + * + * :ts=8 + * + * 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. + */ + +#ifndef _ASSERT_H +#define _ASSERT_H 1 + +/****************************************************************************/ + +/* IMPORTANT: If DEBUG is redefined, it must happen only here. This + * will cause all modules to depend upon it to be rebuilt + * by the smakefile (that is, provided the smakefile has + * all the necessary dependency lines in place). + */ + +/*#define DEBUG*/ + +/****************************************************************************/ + +#ifdef ASSERT +#undef ASSERT +#endif /* ASSERT */ + +#define PUSH_ASSERTS() PUSHDEBUGLEVEL(0) +#define PUSH_REPORTS() PUSHDEBUGLEVEL(1) +#define PUSH_CALLS() PUSHDEBUGLEVEL(2) +#define PUSH_ALL() PUSHDEBUGLEVEL(2) +#define POP() POPDEBUGLEVEL() + +#if defined(DEBUG) /*&& defined(__SASC)*/ + void _ASSERT(int x,const char *xs,const char *file,int line,const char *function); + void _SHOWVALUE(unsigned long value,int size,const char *name,const char *file,int line); + void _SHOWPOINTER(void *p,const char *name,const char *file,int line); + void _SHOWSTRING(const char *string,const char *name,const char *file,int line); + void _SHOWMSG(const char *msg,const char *file,int line); + void _ENTER(const char *file,int line,const char *function); + void _LEAVE(const char *file,int line,const char *function); + void _RETURN(const char *file,int line,const char *function,unsigned long result); + void _DPRINTF_HEADER(const char *file,int line); + void _DPRINTF(const char *format,...); + void _DLOG(const char *format,...); + int _SETDEBUGLEVEL(int level); + void _PUSHDEBUGLEVEL(int level); + void _POPDEBUGLEVEL(void); + int _GETDEBUGLEVEL(void); + void _SETPROGRAMNAME(char *name); + void _INDENT(void); + + #define ASSERT(x) _ASSERT((int)(x),#x,__FILE__,__LINE__,__FUNC__) + #define ENTER() _ENTER(__FILE__,__LINE__,__FUNC__) + #define LEAVE() _LEAVE(__FILE__,__LINE__,__FUNC__) + #define RETURN(r) _RETURN(__FILE__,__LINE__,__FUNC__,(unsigned long)r) + #define SHOWVALUE(v) _SHOWVALUE((ULONG)v,sizeof(v),#v,__FILE__,__LINE__) + #define SHOWPOINTER(p) _SHOWPOINTER(p,#p,__FILE__,__LINE__) + #define SHOWSTRING(s) _SHOWSTRING(s,#s,__FILE__,__LINE__) + #define SHOWMSG(s) _SHOWMSG(s,__FILE__,__LINE__) + #define D(s) do { _DPRINTF_HEADER(__FILE__,__LINE__); _DPRINTF s; } while(0) + #define PRINTHEADER() _DPRINTF_HEADER(__FILE__,__LINE__) + #define PRINTF(s) _DLOG s + #define LOG(s) do { _DPRINTF_HEADER(__FILE__,__LINE__); _DLOG("<%s()>:",__FUNC__); _DLOG s; } while(0) + #define SETDEBUGLEVEL(l) _SETDEBUGLEVEL(l) + #define PUSHDEBUGLEVEL(l) _PUSHDEBUGLEVEL(l) + #define POPDEBUGLEVEL() _POPDEBUGLEVEL() + #define SETPROGRAMNAME(n) _SETPROGRAMNAME(n) + #define GETDEBUGLEVEL() _GETDEBUGLEVEL() + #define INDENT() _INDENT() + + extern int __debug_level; + + #undef DEBUG + #define DEBUG 1 +#else + #define ASSERT(x) ((void)0) + #define ENTER() ((void)0) + #define LEAVE() ((void)0) + #define RETURN(r) ((void)0) + #define SHOWVALUE(v) ((void)0) + #define SHOWPOINTER(p) ((void)0) + #define SHOWSTRING(s) ((void)0) + #define SHOWMSG(s) ((void)0) + #define D(s) ((void)0) + #define PRINTHEADER() ((void)0) + #define PRINTF(s) ((void)0) + #define LOG(s) ((void)0) + #define SETDEBUGLEVEL(l) ((void)0) + #define PUSHDEBUGLEVEL(l) ((void)0) + #define POPDEBUGLEVEL() ((void)0) + #define SETPROGRAMNAME(n) ((void)0) + #define GETDEBUGLEVEL() (0) + #define INDENT() ((void)0) + + #ifdef DEBUG + #undef DEBUG + #endif /* DEBUG */ + + #define DEBUG 0 +#endif /* DEBUG */ + +/****************************************************************************/ + +#endif /* _ASSERT_H */ diff --git a/source_code/crypt.c b/source_code/crypt.c new file mode 100644 index 0000000..4e2e4f0 --- /dev/null +++ b/source_code/crypt.c @@ -0,0 +1,712 @@ +/* + * $Id: crypt.c,v 1.1.1.1 2005-05-24 13:22:06 obarthel Exp $ + * + * :ts=8 + * + * Password encryption routines, lifted from the Samba source code, + * "libsmb/smbencrypt.c", "libsmb/smbdes.c" and "lib/md4.c": + * + * Unix SMB/Netbios implementation. + * Version 1.9. + * + * SMB parameters and setup + * + * A partial implementation of DES designed for use in the + * SMB authentication protocol + * + * An implementation of MD4 designed for use in the SMB authentication protocol + * + * Copyright (C) Andrew Tridgell 1992-1998 + * Modified by Jeremy Allison 1995. + * + * 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. + */ + +#include "smbfs.h" + +/****************************************************************************/ + +#include + +/****************************************************************************/ + +static void permute (char *out, char *in, const unsigned char * const p, int n); +static void left_shift (char *d, int count, int n); +static void concat (char *out, char *in1, char *in2, int l1, int l2); +static void xor (char *out, char *in1, char *in2, int n); +static void dohash (char *out, char *in, char *key, int forw); +static void str_to_key (unsigned char *str, unsigned char *key); +static void smbhash (unsigned char *out, unsigned char *in, unsigned char *key, int forw); +static void E_P16 (unsigned char *p14, unsigned char *p16); +static void E_P24 (unsigned char *p21, unsigned char *c8, unsigned char *p24); +static int local_wcslen (short *str); +static int local_mbstowcs (short *dst, unsigned char *src, int len); +static void E_md4hash (unsigned char *passwd, unsigned char *p16); +static void smb_owf_encrypt (unsigned char *passwd, unsigned char *c8, unsigned char *p24); +static unsigned long F (unsigned long X, unsigned long Y, unsigned long Z); +static unsigned long G (unsigned long X, unsigned long Y, unsigned long Z); +static unsigned long H (unsigned long X, unsigned long Y, unsigned long Z); +static unsigned long lshift (unsigned long x, int s); +static void mdfour64 (unsigned long *M); +static void copy64 (unsigned long *M, unsigned char *in); +static void copy4 (unsigned char *out, unsigned long x); +static void mdfour (unsigned char *out, unsigned char *in, int n); + +/****************************************************************************/ + +/* NOTES: + + This code makes no attempt to be fast! In fact, it is a very + slow implementation + + This code is NOT a complete DES implementation. It implements only + the minimum necessary for SMB authentication, as used by all SMB + products (including every copy of Microsoft Windows95 ever sold) + + In particular, it can only do a unchained forward DES pass. This + means it is not possible to use this code for encryption/decryption + of data, instead it is only useful as a "hash" algorithm. + + There is no entry point into this code that allows normal DES operation. + + I believe this means that this code does not come under ITAR + regulations but this is NOT a legal opinion. If you are concerned + about the applicability of ITAR regulations to this code then you + should confirm it for yourself (and maybe let me know if you come + up with a different answer to the one above) */ + +static const unsigned char perm1[56] = +{ + 57, 49, 41, 33, 25, 17, 9, + 1, 58, 50, 42, 34, 26, 18, + 10, 2, 59, 51, 43, 35, 27, + 19, 11, 3, 60, 52, 44, 36, + 63, 55, 47, 39, 31, 23, 15, + 7, 62, 54, 46, 38, 30, 22, + 14, 6, 61, 53, 45, 37, 29, + 21, 13, 5, 28, 20, 12, 4 +}; + +static const unsigned char perm2[48] = +{ + 14, 17, 11, 24, 1, 5, + 3, 28, 15, 6, 21, 10, + 23, 19, 12, 4, 26, 8, + 16, 7, 27, 20, 13, 2, + 41, 52, 31, 37, 47, 55, + 30, 40, 51, 45, 33, 48, + 44, 49, 39, 56, 34, 53, + 46, 42, 50, 36, 29, 32 +}; + +static const unsigned char perm3[64] = +{ + 58, 50, 42, 34, 26, 18, 10, 2, + 60, 52, 44, 36, 28, 20, 12, 4, + 62, 54, 46, 38, 30, 22, 14, 6, + 64, 56, 48, 40, 32, 24, 16, 8, + 57, 49, 41, 33, 25, 17, 9, 1, + 59, 51, 43, 35, 27, 19, 11, 3, + 61, 53, 45, 37, 29, 21, 13, 5, + 63, 55, 47, 39, 31, 23, 15, 7 +}; + +static const unsigned char perm4[48] = +{ + 32, 1, 2, 3, 4, 5, + 4, 5, 6, 7, 8, 9, + 8, 9, 10, 11, 12, 13, + 12, 13, 14, 15, 16, 17, + 16, 17, 18, 19, 20, 21, + 20, 21, 22, 23, 24, 25, + 24, 25, 26, 27, 28, 29, + 28, 29, 30, 31, 32, 1 +}; + +static const unsigned char perm5[32] = +{ + 16, 7, 20, 21, + 29, 12, 28, 17, + 1, 15, 23, 26, + 5, 18, 31, 10, + 2, 8, 24, 14, + 32, 27, 3, 9, + 19, 13, 30, 6, + 22, 11, 4, 25 +}; + +static const unsigned char perm6[64] = +{ + 40, 8, 48, 16, 56, 24, 64, 32, + 39, 7, 47, 15, 55, 23, 63, 31, + 38, 6, 46, 14, 54, 22, 62, 30, + 37, 5, 45, 13, 53, 21, 61, 29, + 36, 4, 44, 12, 52, 20, 60, 28, + 35, 3, 43, 11, 51, 19, 59, 27, + 34, 2, 42, 10, 50, 18, 58, 26, + 33, 1, 41, 9, 49, 17, 57, 25 +}; + +static const unsigned char sc[16] = +{ + 1, 1, 2, 2, + 2, 2, 2, 2, + 1, 2, 2, 2, + 2, 2, 2, 1 +}; + +static const unsigned char sbox[8][4][16] = +{ + { + {14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7}, + {0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8}, + {4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0}, + {15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13} + }, + + { + {15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10}, + {3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5}, + {0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15}, + {13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9} + }, + + { + {10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8}, + {13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1}, + {13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7}, + {1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12} + }, + + { + {7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15}, + {13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9}, + {10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4}, + {3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14} + }, + + { + {2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9}, + {14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6}, + {4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14}, + {11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3} + }, + + { + {12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11}, + {10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8}, + {9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6}, + {4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13} + }, + + { + {4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1}, + {13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6}, + {1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2}, + {6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12} + }, + + { + {13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7}, + {1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2}, + {7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8}, + {2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11} + } +}; + +static void +permute (char *out, char *in, const unsigned char * const p, int n) +{ + int i; + + for (i = 0; i < n; i++) + out[i] = in[p[i] - 1]; +} + +static void +left_shift (char *d, int count, int n) +{ + char out[64]; + int i; + + for (i = 0; i < n; i++) + out[i] = d[(i + count) % n]; + + for (i = 0; i < n; i++) + d[i] = out[i]; +} + +static void +concat (char *out, char *in1, char *in2, int l1, int l2) +{ + while (l1--) + (*out++) = (*in1++); + + while (l2--) + (*out++) = (*in2++); +} + +static void +xor (char *out, char *in1, char *in2, int n) +{ + int i; + + for (i = 0; i < n; i++) + out[i] = in1[i] ^ in2[i]; +} + +static void +dohash (char *out, char *in, char *key, int forw) +{ + int i, j, k; + char pk1[56]; + char c[28]; + char d[28]; + char cd[56]; + char ki[16][48]; + char pd1[64]; + char l[32], r[32]; + char rl[64]; + + permute (pk1, key, perm1, 56); + + for (i = 0; i < 28; i++) + c[i] = pk1[i]; + + for (i = 0; i < 28; i++) + d[i] = pk1[i + 28]; + + for (i = 0; i < 16; i++) + { + left_shift (c, sc[i], 28); + left_shift (d, sc[i], 28); + + concat (cd, c, d, 28, 28); + permute (ki[i], cd, perm2, 48); + } + + permute (pd1, in, perm3, 64); + + for (j = 0; j < 32; j++) + { + l[j] = pd1[j]; + r[j] = pd1[j + 32]; + } + + for (i = 0; i < 16; i++) + { + char er[48]; + char erk[48]; + char b[8][6]; + char cb[32]; + char pcb[32]; + char r2[32]; + + permute (er, r, perm4, 48); + + xor (erk, er, ki[forw ? i : 15 - i], 48); + + for (j = 0; j < 8; j++) + { + for (k = 0; k < 6; k++) + b[j][k] = erk[j * 6 + k]; + } + + for (j = 0; j < 8; j++) + { + int m, n; + + m = (b[j][0] << 1) | b[j][5]; + + n = (b[j][1] << 3) | (b[j][2] << 2) | (b[j][3] << 1) | b[j][4]; + + for (k = 0; k < 4; k++) + b[j][k] = (sbox[j][m][n] & (1 << (3 - k))) ? 1 : 0; + } + + for (j = 0; j < 8; j++) + { + for (k = 0; k < 4; k++) + cb[j * 4 + k] = b[j][k]; + } + + permute (pcb, cb, perm5, 32); + + xor (r2, l, pcb, 32); + + for (j = 0; j < 32; j++) + l[j] = r[j]; + + for (j = 0; j < 32; j++) + r[j] = r2[j]; + } + + concat (rl, r, l, 32, 32); + + permute (out, rl, perm6, 64); +} + +static void +str_to_key (unsigned char *str, unsigned char *key) +{ + int i; + + key[0] = str[0] >> 1; + key[1] = ((str[0] & 0x01) << 6) | (str[1] >> 2); + key[2] = ((str[1] & 0x03) << 5) | (str[2] >> 3); + key[3] = ((str[2] & 0x07) << 4) | (str[3] >> 4); + key[4] = ((str[3] & 0x0F) << 3) | (str[4] >> 5); + key[5] = ((str[4] & 0x1F) << 2) | (str[5] >> 6); + key[6] = ((str[5] & 0x3F) << 1) | (str[6] >> 7); + key[7] = str[6] & 0x7F; + + for (i = 0; i < 8; i++) + key[i] = (key[i] << 1); +} + +static void +smbhash (unsigned char *out, unsigned char *in, unsigned char *key, int forw) +{ + int i; + char outb[64]; + char inb[64]; + char keyb[64]; + unsigned char key2[8]; + + str_to_key (key, key2); + + for (i = 0; i < 64; i++) + { + inb[i] = (in[i / 8] & (1 << (7 - (i % 8)))) ? 1 : 0; + keyb[i] = (key2[i / 8] & (1 << (7 - (i % 8)))) ? 1 : 0; + outb[i] = 0; + } + + dohash (outb, inb, keyb, forw); + + for (i = 0; i < 8; i++) + out[i] = 0; + + for (i = 0; i < 64; i++) + { + if (outb[i]) + out[i / 8] |= (1 << (7 - (i % 8))); + } +} + +static void +E_P16 (unsigned char *p14, unsigned char *p16) +{ + unsigned char sp8[8] = + { + 0x4b, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25 + }; + + smbhash (p16, sp8, p14, 1); + smbhash (p16 + 8, sp8, p14 + 7, 1); +} + +static void +E_P24 (unsigned char *p21, unsigned char *c8, unsigned char *p24) +{ + smbhash (p24, c8, p21, 1); + smbhash (p24 + 8, c8, p21 + 7, 1); + smbhash (p24 + 16, c8, p21 + 14, 1); +} + +/****************************************************************************/ + +/* This implements the X/Open SMB password encryption + It takes a password, a 8 byte "crypt key" and puts 24 bytes of + encrypted password into p24 */ +void +smb_encrypt (unsigned char *passwd, unsigned char *c8, unsigned char *p24) +{ + unsigned char p14[15], p21[21]; + int len; + + memset (p21, '\0', sizeof(p21)); + memset (p14, '\0', sizeof(p14)); + + len = strlen(passwd); + if(len > (int)sizeof(p14)-1) + len = sizeof(p14)-1; + + memcpy (p14, passwd, len); + + StringToUpper ((char *) p14); + E_P16 (p14, p21); + + smb_owf_encrypt (p21, c8, p24); +} + +/****************************************************************************/ + +/* Routines for Windows NT MD4 Hash functions. */ +static int +local_wcslen (short *str) +{ + int len = 0; + + while ((*str++) != 0) + len++; + + return len; +} + +/* Convert a string into an NT UNICODE string. + Note that regardless of processor type + this must be in intel (little-endian) + format. */ +static int +local_mbstowcs (short *dst, unsigned char *src, int len) +{ + int i; + short val; + + for (i = 0; i < len; i++) + { + val = (*src++); + WSET(dst,0,val); + dst++; + + if (val == 0) + break; + } + + return i; +} + +/* Creates the MD4 Hash of the users password in NT UNICODE. */ +static void +E_md4hash (unsigned char *passwd, unsigned char *p16) +{ + short wpwd[129]; + int len; + + /* Password cannot be longer than 128 characters */ + len = strlen ((char *) passwd); + if (len > 128) + len = 128; + + /* Password must be converted to NT unicode */ + local_mbstowcs (wpwd, passwd, len); + wpwd[len] = 0; /* Ensure string is null terminated */ + + /* Calculate length in bytes */ + len = local_wcslen (wpwd) * sizeof (short); + + mdfour (p16, (unsigned char *) wpwd, len); +} + +/* Does the des encryption from the NT or LM MD4 hash. */ +static void +smb_owf_encrypt (unsigned char *passwd, unsigned char *c8, unsigned char *p24) +{ + unsigned char p21[21]; + + memset (p21, '\0', sizeof(p21)); + + memcpy (p21, passwd, 16); + E_P24 (p21, c8, p24); +} + +/****************************************************************************/ + +/* Does the NT MD4 hash then des encryption. */ +void +smb_nt_encrypt (unsigned char *passwd, unsigned char *c8, unsigned char *p24) +{ + unsigned char p21[21]; + + memset (p21, '\0', sizeof(p21)); + + E_md4hash (passwd, p21); + smb_owf_encrypt (p21, c8, p24); +} + +/****************************************************************************/ + +static unsigned long A, B, C, D; + +static unsigned long +F(unsigned long X, unsigned long Y, unsigned long Z) +{ + return (X & Y) | ((~X) & Z); +} + +static unsigned long +G(unsigned long X, unsigned long Y, unsigned long Z) +{ + return (X & Y) | (X & Z) | (Y & Z); +} + +static unsigned long +H(unsigned long X, unsigned long Y, unsigned long Z) +{ + return X ^ Y ^ Z; +} + +static unsigned long +lshift (unsigned long x, int s) +{ + return (x << s) | (x >> (32 - s)); +} + +#define ROUND1(a,b,c,d,k,s) a = lshift(a + F(b,c,d) + X[k], s) +#define ROUND2(a,b,c,d,k,s) a = lshift(a + G(b,c,d) + X[k] + 0x5A827999UL,s) +#define ROUND3(a,b,c,d,k,s) a = lshift(a + H(b,c,d) + X[k] + 0x6ED9EBA1UL,s) + +/* this applies md4 to 64 byte chunks */ +static void +mdfour64 (unsigned long *M) +{ + unsigned long AA, BB, CC, DD; + unsigned long X[16]; + + memcpy(X,M,sizeof(X)); + + AA = A; + BB = B; + CC = C; + DD = D; + + ROUND1 (A, B, C, D, 0, 3); + ROUND1 (D, A, B, C, 1, 7); + ROUND1 (C, D, A, B, 2, 11); + ROUND1 (B, C, D, A, 3, 19); + ROUND1 (A, B, C, D, 4, 3); + ROUND1 (D, A, B, C, 5, 7); + ROUND1 (C, D, A, B, 6, 11); + ROUND1 (B, C, D, A, 7, 19); + ROUND1 (A, B, C, D, 8, 3); + ROUND1 (D, A, B, C, 9, 7); + ROUND1 (C, D, A, B, 10, 11); + ROUND1 (B, C, D, A, 11, 19); + ROUND1 (A, B, C, D, 12, 3); + ROUND1 (D, A, B, C, 13, 7); + ROUND1 (C, D, A, B, 14, 11); + ROUND1 (B, C, D, A, 15, 19); + + ROUND2 (A, B, C, D, 0, 3); + ROUND2 (D, A, B, C, 4, 5); + ROUND2 (C, D, A, B, 8, 9); + ROUND2 (B, C, D, A, 12, 13); + ROUND2 (A, B, C, D, 1, 3); + ROUND2 (D, A, B, C, 5, 5); + ROUND2 (C, D, A, B, 9, 9); + ROUND2 (B, C, D, A, 13, 13); + ROUND2 (A, B, C, D, 2, 3); + ROUND2 (D, A, B, C, 6, 5); + ROUND2 (C, D, A, B, 10, 9); + ROUND2 (B, C, D, A, 14, 13); + ROUND2 (A, B, C, D, 3, 3); + ROUND2 (D, A, B, C, 7, 5); + ROUND2 (C, D, A, B, 11, 9); + ROUND2 (B, C, D, A, 15, 13); + + ROUND3 (A, B, C, D, 0, 3); + ROUND3 (D, A, B, C, 8, 9); + ROUND3 (C, D, A, B, 4, 11); + ROUND3 (B, C, D, A, 12, 15); + ROUND3 (A, B, C, D, 2, 3); + ROUND3 (D, A, B, C, 10, 9); + ROUND3 (C, D, A, B, 6, 11); + ROUND3 (B, C, D, A, 14, 15); + ROUND3 (A, B, C, D, 1, 3); + ROUND3 (D, A, B, C, 9, 9); + ROUND3 (C, D, A, B, 5, 11); + ROUND3 (B, C, D, A, 13, 15); + ROUND3 (A, B, C, D, 3, 3); + ROUND3 (D, A, B, C, 11, 9); + ROUND3 (C, D, A, B, 7, 11); + ROUND3 (B, C, D, A, 15, 15); + + A += AA; + B += BB; + C += CC; + D += DD; +} + +static void +copy64 (unsigned long *M, unsigned char *in) +{ + int i; + + for (i = 0; i < 16; i++) + { + M[i] = (in[i * 4 + 3] << 24) | (in[i * 4 + 2] << 16) | + (in[i * 4 + 1] << 8) | (in[i * 4 + 0] << 0); + } +} + +static void +copy4 (unsigned char *out, unsigned long x) +{ + out[0] = x & 0xFF; + out[1] = (x >> 8) & 0xFF; + out[2] = (x >> 16) & 0xFF; + out[3] = (x >> 24) & 0xFF; +} + +/* produce a md4 message digest from data of length n bytes */ +static void +mdfour (unsigned char *out, unsigned char *in, int n) +{ + unsigned char buf[128]; + unsigned long M[16]; + unsigned long b = n * 8; + + A = 0x67452301; + B = 0xefcdab89; + C = 0x98badcfe; + D = 0x10325476; + + while (n > 64) + { + copy64 (M, in); + mdfour64 (M); + + in += 64; + n -= 64; + } + + memset (buf, 0, sizeof(buf)); + memcpy (buf, in, n); + buf[n] = 0x80; + + if (n <= 55) + { + copy4 (buf + 56, b); + copy64 (M, buf); + mdfour64 (M); + } + else + { + copy4 (buf + 120, b); + copy64 (M, buf); + mdfour64 (M); + copy64 (M, buf + 64); + mdfour64 (M); + } + + memset (buf, 0, sizeof(buf)); + copy64 (M, buf); + + copy4 (out, A); + copy4 (out + 4, B); + copy4 (out + 8, C); + copy4 (out + 12, D); +} diff --git a/source_code/include/smb/smb.h b/source_code/include/smb/smb.h new file mode 100644 index 0000000..41c4bcf --- /dev/null +++ b/source_code/include/smb/smb.h @@ -0,0 +1,83 @@ +/* + * $Id: smb.h,v 1.1.1.1 2005-05-24 13:22:14 obarthel Exp $ + * + * :ts=8 + * + * smb.h + * + * Copyright (C) 1995 by Paal-Kr. Engstad and Volker Lendecke + * Modified for use with AmigaOS by Olaf Barthel + */ + +#ifndef _LINUX_SMB_H +#define _LINUX_SMB_H + +#define SMB_PORT 139 +#define SMB_MAXNAMELEN 255 + +typedef unsigned char byte; +typedef unsigned short word; +typedef unsigned long dword; + +/* + * Set/Get values in SMB-byte order + */ + +#define PVAL(buf,pos) ((unsigned)BVAL(buf,pos)) + +#define BVAL(buf,pos) (((unsigned char *)(buf))[pos]) +#define WVAL(buf,pos) (PVAL(buf,pos)|PVAL(buf,(pos)+1)<<8) +#define DVAL(buf,pos) (WVAL(buf,pos)|WVAL(buf,(pos)+2)<<16) + +#define BSET(buf,pos,val) (BVAL(buf,pos)=((val) & 0xFF)) +#define WSET(buf,pos,val) do { BVAL(buf,pos)=((val) & 0xFF); BVAL(buf,(pos)+1)=(((unsigned)(val))>>8) & 0xFF; } while (0) +#define DSET(buf,pos,val) do { WSET(buf,pos,(val)); WSET(buf,(pos)+2,((unsigned)(val))>>16); } while (0) + +#define smb_base(buf) ((byte *)(((byte *)(buf))+4)) + +enum smb_protocol +{ + PROTOCOL_NONE, + PROTOCOL_CORE, + PROTOCOL_COREPLUS, + PROTOCOL_LANMAN1, + PROTOCOL_LANMAN2, + PROTOCOL_NT1 +}; + +enum smb_conn_state +{ + CONN_VALID, /* everything's fine */ + CONN_INVALID, /* Something went wrong, but did not + try to reconnect yet. */ + CONN_RETRIED /* Tried a reconnection, but was refused */ +}; + +struct smb_dskattr +{ + word total; + word allocblocks; + word blocksize; + word free; +}; + +/* + * Contains all relevant data on a SMB networked file. + */ +struct smb_dirent +{ + int opened; /* is it open on the fileserver? */ + word fileid; /* What id to handle a file with? */ + word attr; /* Attribute fields, DOS value */ + + time_t atime, mtime, ctime; /* Times, as seen by the server, normalized */ + /* to UTC. The ugly conversion happens in */ + /* proc.c */ + + unsigned long size; /* File size. */ + char *complete_path; /* Complete path, MS-DOS notation, with '\' */ + size_t complete_path_size; /* Number of bytes allocated for name */ + int len; /* Namelength. */ +}; + +#endif /* _LINUX_SMB_H */ diff --git a/source_code/include/smb/smb_fs.h b/source_code/include/smb/smb_fs.h new file mode 100644 index 0000000..89c15da --- /dev/null +++ b/source_code/include/smb/smb_fs.h @@ -0,0 +1,84 @@ +/* + * $Id: smb_fs.h,v 1.1.1.1 2005-05-24 13:22:13 obarthel Exp $ + * + * :ts=8 + * + * smb_fs.h + * + * Copyright (C) 1995 by Paal-Kr. Engstad and Volker Lendecke + * Modified for use with AmigaOS by Olaf Barthel + * Modified for supporting SMBlockingX packets by Peter Riede + */ + +#ifndef _LINUX_SMB_FS_H +#define _LINUX_SMB_FS_H + +#include + +#include +#include + +#include + +#define SMB_HEADER_LEN 37 /* includes everything up to, but not + including smb_bcc */ + +/* This structure is used to pass the arguments to smb_proc_lockingX + */ +struct smb_lkrng +{ + off_t offset; /* offset to first byte to be (un)locked */ + long len; /* bytesize of the block */ +}; + +/* Macros to get at offsets within smb_lkrng and smb_unlkrng + structures. We cannot define these as actual structures + due to possible differences in structure packing + on different machines/compilers. */ + +#define SMB_LPID_OFFSET(indx) (10 * (indx)) +#define SMB_LKOFF_OFFSET(indx) ( 2 + (10 * (indx))) +#define SMB_LKLEN_OFFSET(indx) ( 6 + (10 * (indx))) +#define SMB_LARGE_LKOFF_OFFSET_HIGH(indx) (4 + (20 * (indx))) +#define SMB_LARGE_LKOFF_OFFSET_LOW(indx) (8 + (20 * (indx))) +#define SMB_LARGE_LKLEN_OFFSET_HIGH(indx) (12 + (20 * (indx))) +#define SMB_LARGE_LKLEN_OFFSET_LOW(indx) (16 + (20 * (indx))) + +/*****************************************************************************/ + +/* proc.c */ +byte *smb_encode_smb_length(byte *p, dword len); +dword smb_len(byte *packet); +int smb_proc_open(struct smb_server *server, const char *pathname, int len, struct smb_dirent *entry); +int smb_proc_close(struct smb_server *server, word fileid, dword mtime); +int smb_proc_read(struct smb_server *server, struct smb_dirent *finfo, off_t offset, long count, char *data, int fs); +int smb_proc_read_raw(struct smb_server *server, struct smb_dirent *finfo, off_t offset, long count, char *data); +int smb_proc_write (struct smb_server *server, struct smb_dirent *finfo, off_t offset, long count, const char *data); +int smb_proc_write_raw(struct smb_server *server, struct smb_dirent *finfo, off_t offset, long count, const char *data); +int smb_proc_lseek (struct smb_server *server, struct smb_dirent *finfo, off_t offset, int mode, off_t * new_position_ptr); +int smb_proc_lockingX (struct smb_server *server, struct smb_dirent *finfo, struct smb_lkrng *locks, int num_entries, int mode, long timeout); +int smb_proc_create(struct smb_server *server, const char *path, int len, struct smb_dirent *entry); +int smb_proc_mv(struct smb_server *server, const char *opath, const int olen, const char *npath, const int nlen); +int smb_proc_mkdir(struct smb_server *server, const char *path, const int len); +int smb_proc_rmdir(struct smb_server *server, const char *path, const int len); +int smb_proc_unlink(struct smb_server *server, const char *path, const int len); +int smb_proc_trunc(struct smb_server *server, word fid, dword length); +int smb_proc_readdir(struct smb_server *server, char *path, int fpos, int cache_size, struct smb_dirent *entry); +int smb_proc_getattr_core(struct smb_server *server, const char *path, int len, struct smb_dirent *entry); +int smb_proc_getattrE(struct smb_server *server, struct smb_dirent *entry); +int smb_proc_setattr_core(struct smb_server *server, const char *path, int len, struct smb_dirent *new_finfo); +int smb_proc_setattrE(struct smb_server *server, word fid, struct smb_dirent *new_entry); +int smb_proc_dskattr (struct smb_server *server, struct smb_dskattr *attr); +int smb_proc_connect(struct smb_server *server); + +/* sock.c */ +int smb_catch_keepalive(struct smb_server *server); +int smb_dont_catch_keepalive(struct smb_server *server); +int smb_release(struct smb_server *server); +int smb_connect(struct smb_server *server); +int smb_request(struct smb_server *server); +int smb_trans2_request(struct smb_server *server, int *data_len, int *param_len, char **data, char **param); +int smb_request_read_raw(struct smb_server *server, unsigned char *target, int max_len); +int smb_request_write_raw(struct smb_server *server, unsigned const char *source, int length); + +#endif /* _LINUX_SMB_FS_H */ diff --git a/source_code/include/smb/smb_fs.h.1 b/source_code/include/smb/smb_fs.h.1 new file mode 100644 index 0000000..4dedfec --- /dev/null +++ b/source_code/include/smb/smb_fs.h.1 @@ -0,0 +1,60 @@ +/* + * $Id: smb_fs.h.1,v 1.1.1.1 2005-05-24 13:22:14 obarthel Exp $ + * + * :ts=8 + * + * smb_fs.h + * + * Copyright (C) 1995 by Paal-Kr. Engstad and Volker Lendecke + * Modified for use with AmigaOS by Olaf Barthel + */ + +#ifndef _LINUX_SMB_FS_H +#define _LINUX_SMB_FS_H + +#include + +#include +#include + +#include + +#define SMB_HEADER_LEN 37 /* includes everything up to, but not + including smb_bcc */ + +/*****************************************************************************/ + +/* proc.c */ +byte *smb_encode_smb_length(byte *p, dword len); +dword smb_len(byte *packet); +int smb_proc_open(struct smb_server *server, const char *pathname, int len, struct smb_dirent *entry); +int smb_proc_close(struct smb_server *server, word fileid, dword mtime); +int smb_proc_read(struct smb_server *server, struct smb_dirent *finfo, off_t offset, long count, char *data, int fs); +int smb_proc_read_raw(struct smb_server *server, struct smb_dirent *finfo, off_t offset, long count, char *data); +int smb_proc_write(struct smb_server *server, struct smb_dirent *finfo, off_t offset, int count, const char *data); +int smb_proc_write_raw(struct smb_server *server, struct smb_dirent *finfo, off_t offset, long count, const char *data); +int smb_proc_create(struct smb_server *server, const char *path, int len, struct smb_dirent *entry); +int smb_proc_mv(struct smb_server *server, const char *opath, const int olen, const char *npath, const int nlen); +int smb_proc_mkdir(struct smb_server *server, const char *path, const int len); +int smb_proc_rmdir(struct smb_server *server, const char *path, const int len); +int smb_proc_unlink(struct smb_server *server, const char *path, const int len); +int smb_proc_trunc(struct smb_server *server, word fid, dword length); +int smb_proc_readdir(struct smb_server *server, char *path, int fpos, int cache_size, struct smb_dirent *entry); +int smb_proc_getattr_core(struct smb_server *server, const char *path, int len, struct smb_dirent *entry); +int smb_proc_getattrE(struct smb_server *server, struct smb_dirent *entry); +int smb_proc_setattr_core(struct smb_server *server, const char *path, int len, struct smb_dirent *new_finfo); +int smb_proc_setattrE(struct smb_server *server, word fid, struct smb_dirent *new_entry); +int smb_proc_dskattr (struct smb_server *server, struct smb_dskattr *attr); +int smb_proc_connect(struct smb_server *server); + +/* sock.c */ +int smb_catch_keepalive(struct smb_server *server); +int smb_dont_catch_keepalive(struct smb_server *server); +int smb_release(struct smb_server *server); +int smb_connect(struct smb_server *server); +int smb_request(struct smb_server *server); +int smb_trans2_request(struct smb_server *server, int *data_len, int *param_len, char **data, char **param); +int smb_request_read_raw(struct smb_server *server, unsigned char *target, int max_len); +int smb_request_write_raw(struct smb_server *server, unsigned const char *source, int length); + +#endif /* _LINUX_SMB_FS_H */ diff --git a/source_code/include/smb/smb_fs_sb.h b/source_code/include/smb/smb_fs_sb.h new file mode 100644 index 0000000..9dba936 --- /dev/null +++ b/source_code/include/smb/smb_fs_sb.h @@ -0,0 +1,50 @@ +/* + * $Id: smb_fs_sb.h,v 1.1.1.1 2005-05-24 13:22:14 obarthel Exp $ + * + * :ts=8 + * + * smb_fs_sb.h + * + * Copyright (C) 1995 by Paal-Kr. Engstad and Volker Lendecke + * Modified for use with AmigaOS by Olaf Barthel + */ + +#ifndef _SMB_FS_SB +#define _SMB_FS_SB + +#include +#include + +struct smb_server +{ + enum smb_protocol protocol; /* The protocol this + connection accepts. */ + + word max_xmit; + int max_recv; /* added by CS */ + word server_uid; + word tid; + + struct smb_mount_data mount_data; /* We store the complete information here + to be able to reconnect. + */ + + unsigned short rcls; /* The error codes we received */ + unsigned short err; + unsigned char *packet; + + int security_mode; + unsigned char crypt_key[8]; + + struct smba_server * abstraction; + + enum smb_conn_state state; + + /* The following are LANMAN 1.0 options transferred to us in + SMBnegprot */ + word maxxmt; + word blkmode; + dword sesskey; +}; + +#endif diff --git a/source_code/include/smb/smb_mount.h b/source_code/include/smb/smb_mount.h new file mode 100644 index 0000000..9dea28c --- /dev/null +++ b/source_code/include/smb/smb_mount.h @@ -0,0 +1,34 @@ +/* + * $Id: smb_mount.h,v 1.1.1.1 2005-05-24 13:22:14 obarthel Exp $ + * + * :ts=8 + * + * smb_mount.h + * + * Copyright (C) 1995 by Paal-Kr. Engstad and Volker Lendecke + * Modified for use with AmigaOS by Olaf Barthel + */ + +#ifndef _SMB_MOUNT_H +#define _SMB_MOUNT_H + +#include + +struct smb_mount_data +{ + int fd; + + struct sockaddr_in addr; + + char workgroup_name[17]; + char server_name[17]; + char client_name[17]; + char service[64]; + + char username[64]; + char password[64]; + + unsigned short max_xmit; +}; + +#endif diff --git a/source_code/include/smb/smbno.h b/source_code/include/smb/smbno.h new file mode 100644 index 0000000..c5406f6 --- /dev/null +++ b/source_code/include/smb/smbno.h @@ -0,0 +1,269 @@ +#ifndef _SMBNO_H_ +#define _SMBNO_H_ + +/* these define the attribute byte as seen by DOS */ +#define aRONLY (1L<<0) +#define aHIDDEN (1L<<1) +#define aSYSTEM (1L<<2) +#define aVOLID (1L<<3) +#define aDIR (1L<<4) +#define aARCH (1L<<5) + +/* error classes */ +#define SUCCESS 0 /* The request was successful. */ +#define ERRDOS 0x01 /* Error is from the core DOS operating system set. */ +#define ERRSRV 0x02 /* Error is generated by the server network file manager. */ +#define ERRHRD 0x03 /* Error is an hardware error. */ +#define ERRCMD 0xFF /* Command was not in the "SMB" format. */ + +/* SMB X/Open error codes for the ERRdos error class */ + +#define ERRbadfunc 1 /* Invalid function (or system call) */ +#define ERRbadfile 2 /* File not found (pathname error) */ +#define ERRbadpath 3 /* Directory not found */ +#define ERRnofids 4 /* Too many open files */ +#define ERRnoaccess 5 /* Access denied */ +#define ERRbadfid 6 /* Invalid fid */ +#define ERRbadmcb 7 /* Memory control blocks destroyed */ +#define ERRnomem 8 /* Out of memory */ +#define ERRbadmem 9 /* Invalid memory block address */ +#define ERRbadenv 10 /* Invalid environment */ +#define ERRbadformat 11 /* Invalid format */ +#define ERRbadaccess 12 /* Invalid open mode */ +#define ERRbaddata 13 /* Invalid data (only from ioctl call) */ +#define ERRres 14 /* reserved */ +#define ERRbaddrive 15 /* Invalid drive */ +#define ERRremcd 16 /* Attempt to delete current directory */ +#define ERRdiffdevice 17 /* rename/move across different filesystems */ +#define ERRnofiles 18 /* no more files found in file search */ +#define ERRbadshare 32 /* Share mode on file conflict with open mode */ +#define ERRlock 33 /* Lock request conflicts with existing lock */ +#define ERRfilexists 80 /* File in operation already exists */ +#define ERRbadpipe 230 /* Named pipe invalid */ +#define ERRpipebusy 231 /* All instances of pipe are busy */ +#define ERRpipeclosing 232 /* named pipe close in progress */ +#define ERRnotconnected 233 /* No process on other end of named pipe */ +#define ERRmoredata 234 /* More data to be returned */ +#define ERROR_EAS_DIDNT_FIT 275 /* Extended attributes didn't fit */ +#define ERROR_EAS_NOT_SUPPORTED 282 /* Extended attributes not suppored */ + +/* Error codes for the ERRSRV class */ + +#define ERRerror 1 /* Non specific error code */ +#define ERRbadpw 2 /* Bad password */ +#define ERRbadtype 3 /* reserved */ +#define ERRaccess 4 /* No permissions to do the requested operation */ +#define ERRinvnid 5 /* tid invalid */ +#define ERRinvnetname 6 /* Invalid servername */ +#define ERRinvdevice 7 /* Invalid device */ +#define ERRqfull 49 /* Print queue full */ +#define ERRqtoobig 50 /* Queued item too big */ +#define ERRinvpfid 52 /* Invalid print file in smb_fid */ +#define ERRsmbcmd 64 /* Unrecognised command */ +#define ERRsrverror 65 /* smb server internal error */ +#define ERRfilespecs 67 /* fid and pathname invalid combination */ +#define ERRbadlink 68 /* reserved */ +#define ERRbadpermits 69 /* Access specified for a file is not valid */ +#define ERRbadpid 70 /* reserved */ +#define ERRsetattrmode 71 /* attribute mode invalid */ +#define ERRpaused 81 /* Message server paused */ +#define ERRmsgoff 82 /* Not receiving messages */ +#define ERRnoroom 83 /* No room for message */ +#define ERRrmuns 87 /* too many remote usernames */ +#define ERRtimeout 88 /* operation timed out */ +#define ERRnoresource 89 /* No resources currently available for request. */ +#define ERRtoomanyuids 90 /* too many userids */ +#define ERRbaduid 91 /* bad userid */ +#define ERRuseMPX 250 /* temporarily unable to use raw mode, use MPX mode */ +#define ERRuseSTD 251 /* temporarily unable to use raw mode, use std.mode */ +#define ERRcontMPX 252 /* resume MPX mode */ +#define ERRbadPW /* reserved */ +#define ERRnosupport 0xFFFF + +/* Error codes for the ERRHRD class */ + +#define ERRnowrite 19 /* read only media */ +#define ERRbadunit 20 /* Unknown device */ +#define ERRnotready 21 /* Drive not ready */ +#define ERRbadcmd 22 /* Unknown command */ +#define ERRdata 23 /* Data (CRC) error */ +#define ERRbadreq 24 /* Bad request structure length */ +#define ERRseek 25 +#define ERRbadmedia 26 +#define ERRbadsector 27 +#define ERRnopaper 28 +#define ERRwrite 29 /* write fault */ +#define ERRread 30 /* read fault */ +#define ERRgeneral 31 /* General hardware failure */ +#define ERRwrongdisk 34 +#define ERRFCBunavail 35 +#define ERRsharebufexc 36 /* share buffer exceeded */ +#define ERRdiskfull 39 + +/* offsets into message for common items */ +#define smb_com 8 +#define smb_rcls 9 +#define smb_reh 10 +#define smb_err 11 +#define smb_flg 13 +#define smb_flg2 14 +#define smb_reb 13 +#define smb_tid 28 +#define smb_pid 30 +#define smb_uid 32 +#define smb_mid 34 +#define smb_wct 36 +#define smb_vwv 37 +#define smb_vwv0 37 +#define smb_vwv1 39 +#define smb_vwv2 41 +#define smb_vwv3 43 +#define smb_vwv4 45 +#define smb_vwv5 47 +#define smb_vwv6 49 +#define smb_vwv7 51 +#define smb_vwv8 53 +#define smb_vwv9 55 +#define smb_vwv10 57 +#define smb_vwv11 59 +#define smb_vwv12 61 +#define smb_vwv13 63 +#define smb_vwv14 65 + +/* these are the trans2 sub fields for primary requests */ +#define smb_tpscnt smb_vwv0 +#define smb_tdscnt smb_vwv1 +#define smb_mprcnt smb_vwv2 +#define smb_mdrcnt smb_vwv3 +#define smb_msrcnt smb_vwv4 +#define smb_flags smb_vwv5 +#define smb_timeout smb_vwv6 +#define smb_pscnt smb_vwv9 +#define smb_psoff smb_vwv10 +#define smb_dscnt smb_vwv11 +#define smb_dsoff smb_vwv12 +#define smb_suwcnt smb_vwv13 +#define smb_setup smb_vwv14 +#define smb_setup0 smb_setup +#define smb_setup1 (smb_setup+2) +#define smb_setup2 (smb_setup+4) + +/* these are for the secondary requests */ +#define smb_spscnt smb_vwv2 +#define smb_spsoff smb_vwv3 +#define smb_spsdisp smb_vwv4 +#define smb_sdscnt smb_vwv5 +#define smb_sdsoff smb_vwv6 +#define smb_sdsdisp smb_vwv7 +#define smb_sfid smb_vwv8 + +/* and these for responses */ +#define smb_tprcnt smb_vwv0 +#define smb_tdrcnt smb_vwv1 +#define smb_prcnt smb_vwv3 +#define smb_proff smb_vwv4 +#define smb_prdisp smb_vwv5 +#define smb_drcnt smb_vwv6 +#define smb_droff smb_vwv7 +#define smb_drdisp smb_vwv8 + +/* the complete */ +#define SMBmkdir 0x00 /* create directory */ +#define SMBrmdir 0x01 /* delete directory */ +#define SMBopen 0x02 /* open file */ +#define SMBcreate 0x03 /* create file */ +#define SMBclose 0x04 /* close file */ +#define SMBflush 0x05 /* flush file */ +#define SMBunlink 0x06 /* delete file */ +#define SMBmv 0x07 /* rename file */ +#define SMBgetatr 0x08 /* get file attributes */ +#define SMBsetatr 0x09 /* set file attributes */ +#define SMBread 0x0A /* read from file */ +#define SMBwrite 0x0B /* write to file */ +#define SMBlock 0x0C /* lock byte range */ +#define SMBunlock 0x0D /* unlock byte range */ +#define SMBctemp 0x0E /* create temporary file */ +#define SMBmknew 0x0F /* make new file */ +#define SMBchkpth 0x10 /* check directory path */ +#define SMBexit 0x11 /* process exit */ +#define SMBlseek 0x12 /* seek */ +#define SMBtcon 0x70 /* tree connect */ +#define SMBtconX 0x75 /* tree connect and X */ +#define SMBtdis 0x71 /* tree disconnect */ +#define SMBnegprot 0x72 /* negotiate protocol */ +#define SMBdskattr 0x80 /* get disk attributes */ +#define SMBsearch 0x81 /* search directory */ +#define SMBsplopen 0xC0 /* open print spool file */ +#define SMBsplwr 0xC1 /* write to print spool file */ +#define SMBsplclose 0xC2 /* close print spool file */ +#define SMBsplretq 0xC3 /* return print queue */ +#define SMBsends 0xD0 /* send single block message */ +#define SMBsendb 0xD1 /* send broadcast message */ +#define SMBfwdname 0xD2 /* forward user name */ +#define SMBcancelf 0xD3 /* cancel forward */ +#define SMBgetmac 0xD4 /* get machine name */ +#define SMBsendstrt 0xD5 /* send start of multi-block message */ +#define SMBsendend 0xD6 /* send end of multi-block message */ +#define SMBsendtxt 0xD7 /* send text of multi-block message */ + +/* Core+ protocol */ +#define SMBlockread 0x13 /* Lock a range and read */ +#define SMBwriteunlock 0x14 /* Unlock a range then write */ +#define SMBreadbraw 0x1a /* read a block of data with no smb header */ +#define SMBwritebraw 0x1d /* write a block of data with no smb header */ +#define SMBwritec 0x20 /* secondary write request */ +#define SMBwriteclose 0x2c /* write a file then close it */ + +/* dos extended protocol */ +#define SMBreadBraw 0x1A /* read block raw */ +#define SMBreadBmpx 0x1B /* read block multiplexed */ +#define SMBreadBs 0x1C /* read block (secondary response) */ +#define SMBwriteBraw 0x1D /* write block raw */ +#define SMBwriteBmpx 0x1E /* write block multiplexed */ +#define SMBwriteBs 0x1F /* write block (secondary request) */ +#define SMBwriteC 0x20 /* write complete response */ +#define SMBsetattrE 0x22 /* set file attributes expanded */ +#define SMBgetattrE 0x23 /* get file attributes expanded */ +#define SMBlockingX 0x24 /* lock/unlock byte ranges and X */ +#define SMBtrans 0x25 /* transaction - name, bytes in/out */ +#define SMBtranss 0x26 /* transaction (secondary request/response) */ +#define SMBioctl 0x27 /* IOCTL */ +#define SMBioctls 0x28 /* IOCTL (secondary request/response) */ +#define SMBcopy 0x29 /* copy */ +#define SMBmove 0x2A /* move */ +#define SMBecho 0x2B /* echo */ +#define SMBopenX 0x2D /* open and X */ +#define SMBreadX 0x2E /* read and X */ +#define SMBwriteX 0x2F /* write and X */ +#define SMBsesssetupX 0x73 /* Session Set Up & X (including User Logon) */ +#define SMBtconX 0x75 /* tree connect and X */ +#define SMBffirst 0x82 /* find first */ +#define SMBfunique 0x83 /* find unique */ +#define SMBfclose 0x84 /* find close */ +#define SMBinvalid 0xFE /* invalid command */ + + +/* Extended 2.0 protocol */ +#define SMBtrans2 0x32 /* TRANS2 protocol set */ +#define SMBtranss2 0x33 /* TRANS2 protocol set, secondary command */ +#define SMBfindclose 0x34 /* Terminate a TRANSACT2_FINDFIRST */ +#define SMBfindnclose 0x35 /* Terminate a TRANSACT2_FINDNOTIFYFIRST */ +#define SMBulogoffX 0x74 /* user logoff */ + +/* these are the TRANS2 sub commands */ +#define TRANSACT2_OPEN 0 +#define TRANSACT2_FINDFIRST 1 +#define TRANSACT2_FINDNEXT 2 +#define TRANSACT2_QFSINFO 3 +#define TRANSACT2_SETFSINFO 4 +#define TRANSACT2_QPATHINFO 5 +#define TRANSACT2_SETPATHINFO 6 +#define TRANSACT2_QFILEINFO 7 +#define TRANSACT2_SETFILEINFO 8 +#define TRANSACT2_FSCTL 9 +#define TRANSACT2_IOCTL 10 +#define TRANSACT2_FINDNOTIFYFIRST 11 +#define TRANSACT2_FINDNOTIFYNEXT 12 +#define TRANSACT2_MKDIR 13 + +#endif /* _SMBNO_H_ */ diff --git a/source_code/main.c b/source_code/main.c new file mode 100644 index 0000000..caa3c71 --- /dev/null +++ b/source_code/main.c @@ -0,0 +1,6384 @@ +/* + * $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); +} diff --git a/source_code/proc.c b/source_code/proc.c new file mode 100644 index 0000000..854da7a --- /dev/null +++ b/source_code/proc.c @@ -0,0 +1,2616 @@ +/* + * $Id: proc.c,v 1.1.1.1 2005-05-24 13:22:12 obarthel Exp $ + * + * :ts=8 + * + * proc.c + * + * Copyright (C) 1995 by Paal-Kr. Engstad and Volker Lendecke + * + * 28/06/96 - Fixed long file name support (smb_proc_readdir_long) by Yuri Per + * + * Modified for big endian support by Christian Starkjohann. + * Modified for use with AmigaOS by Olaf Barthel + */ + +#include "smbfs.h" + +/*****************************************************************************/ + +#include +#include +#include + +/*****************************************************************************/ + +#define SMB_VWV(packet) ((packet) + SMB_HEADER_LEN) +#define SMB_CMD(packet) ((packet)[8]) +#define SMB_WCT(packet) ((packet)[SMB_HEADER_LEN - 1]) +#define SMB_BCC(packet) smb_bcc(packet) +#define SMB_BUF(packet) ((packet) + SMB_HEADER_LEN + SMB_WCT(packet) * 2 + 2) + +#define SMB_DIRINFO_SIZE 43 +#define SMB_STATUS_SIZE 21 + +/*****************************************************************************/ + +static INLINE byte * smb_encode_word(byte *p, word data); +static INLINE byte * smb_decode_word(byte *p, word *data); +static INLINE int utc2local(int time_value); +static INLINE int local2utc(int time_value); +static INLINE word smb_bcc(byte *packet); +static INLINE int smb_verify(byte *packet, int command, int wct, int bcc); +static byte *smb_encode_dialect(byte *p, const byte *name, int len); +static byte *smb_encode_ascii(byte *p, const byte *name, int len); +static void smb_encode_vblock(byte *p, const byte *data, word len, int unused_fs); +static byte *smb_decode_data(byte *p, byte *data, word *data_len, int fs); +static byte *smb_name_mangle(byte *p, const byte *name); +static int date_dos2unix(unsigned short time_value, unsigned short date); +static void date_unix2dos(int unix_date, unsigned short *time_value, unsigned short *date); +static INLINE int smb_valid_packet(byte *packet); +static int smb_errno(int errcls, int error); +static int smb_request_ok(struct smb_server *s, int command, int wct, int bcc); +static int smb_retry(struct smb_server *server); +static int smb_request_ok_unlock(struct smb_server *s, int command, int wct, int bcc); +static byte *smb_setup_header(struct smb_server *server, byte command, word wct, word bcc); +static byte *smb_setup_header_exclusive(struct smb_server *server, byte command, word wct, word bcc); +static int smb_proc_do_create(struct smb_server *server, const char *path, int len, struct smb_dirent *entry, word command); +static char *smb_decode_dirent(char *p, struct smb_dirent *entry); +static int smb_proc_readdir_short(struct smb_server *server, char *path, int fpos, int cache_size, struct smb_dirent *entry); +static char *smb_decode_long_dirent(char *p, struct smb_dirent *finfo, int level); +static int smb_proc_readdir_long(struct smb_server *server, char *path, int fpos, int cache_size, struct smb_dirent *entry); +static int smb_proc_reconnect(struct smb_server *server); +static void smb_printerr(int class, int num); + +/*****************************************************************************/ + +/***************************************************************************** + * + * Encoding/Decoding section + * + *****************************************************************************/ +static INLINE byte * +smb_encode_word (byte * p, word data) +{ + p[0] = data & 0x00ffU; + p[1] = (data & 0xff00U) >> 8; + return &p[2]; +} + +static INLINE byte * +smb_decode_word (byte * p, word * data) +{ + (*data) = (word) p[0] | p[1] << 8; + return &(p[2]); +} + +byte * +smb_encode_smb_length (byte * p, dword len) +{ + p[0] = p[1] = 0; + p[2] = (len & 0xFF00) >> 8; + p[3] = (len & 0xFF); + + if (len > 0xFFFF) + p[1] |= 0x01; + + return &p[4]; +} + +static byte * +smb_encode_dialect (byte * p, const byte * name, int len) +{ + (*p++) = 2; + strcpy (p, name); + + return p + len + 1; +} + +static byte * +smb_encode_ascii (byte * p, const byte * name, int len) +{ + (*p++) = 4; + strcpy (p, name); + + return p + len + 1; +} + +static void +smb_encode_vblock (byte * p, const byte * data, word len, int unused_fs) +{ + (*p++) = 5; + p = smb_encode_word (p, len); + memcpy (p, data, len); +} + +static byte * +smb_decode_data (byte * p, byte * data, word * data_len, int unused_fs) +{ + word len; + + if (!((*p) == 1 || (*p) == 5)) + LOG (("Warning! Data block not starting with 1 or 5\n")); + + len = WVAL (p, 1); + p += 3; + + memcpy (data, p, len); + + (*data_len) = len; + + return p + len; +} + +static byte * +smb_name_mangle (byte * p, const byte * name) +{ + int len, pad = 0; + + len = strlen (name); + + if (len < 16) + pad = 16 - len; + + (*p++) = 2 * (len + pad); + + while ((*name) != '\0') + { + (*p++) = ((*name) >> 4) + 'A'; + (*p++) = ((*name) & 0x0F) + 'A'; + + name++; + } + + while (pad-- > 0) + { + (*p++) = 'C'; + (*p++) = 'A'; + } + + (*p++) = '\0'; + + return p; +} + +static INLINE int +utc2local (int time_value) +{ + int result; + + result = time_value - GetTimeZoneDelta(); + + return result; +} + +static INLINE int +local2utc (int time_value) +{ + int result; + + result = time_value + GetTimeZoneDelta(); + + return result; +} + +/* Convert a MS-DOS time/date pair to a UNIX date (seconds since January 1st 1970). */ +static int +date_dos2unix (unsigned short time_value, unsigned short date) +{ + time_t seconds; + struct tm tm; + + memset(&tm,0,sizeof(tm)); + + tm.tm_sec = 2 * (time_value & 0x1F); + tm.tm_min = (time_value >> 5) & 0x3F; + tm.tm_hour = (time_value >> 11) & 0x1F; + tm.tm_mday = date & 0x1F; + tm.tm_mon = ((date >> 5) & 0xF) - 1; + tm.tm_year = ((date >> 9) & 0x7F) + 80; + + seconds = MakeTime(&tm); + + return(seconds); +} + +/* Convert linear UNIX date to a MS-DOS time/date pair. */ +static void +date_unix2dos (int unix_date, unsigned short *time_value, unsigned short *date) +{ + struct tm tm; + + LocalTime(unix_date,&tm); + + (*time_value) = (tm.tm_hour << 11) | (tm.tm_min << 5) | (tm.tm_sec / 2); + (*date) = ((tm.tm_year - 80) << 9) | ((tm.tm_mon + 1) << 5) | tm.tm_mday; +} + +/**************************************************************************** + * + * Support section. + * + ****************************************************************************/ +dword +smb_len (byte * packet) +{ + return (dword)( ((packet[1] & 0x1) << 16L) | (packet[2] << 8L) | (packet[3]) ); +} + +static INLINE word +smb_bcc (byte * packet) +{ + int pos = SMB_HEADER_LEN + SMB_WCT (packet) * sizeof (word); + + return (word)(packet[pos] | packet[pos + 1] << 8); +} + +/* smb_valid_packet: We check if packet fulfills the basic + requirements of a smb packet */ +static INLINE int +smb_valid_packet (byte * packet) +{ + int result; + + LOG (("len: %ld, wct: %ld, bcc: %ld\n", smb_len (packet), SMB_WCT (packet), SMB_BCC (packet))); + + result = (packet[4] == 0xff + && packet[5] == 'S' + && packet[6] == 'M' + && packet[7] == 'B' + && (smb_len (packet) + 4 == (dword)( + SMB_HEADER_LEN + SMB_WCT (packet) * 2 + SMB_BCC (packet) + 2))) ? 0 : (-EIO); + + return result; +} + +/* smb_verify: We check if we got the answer we expected, and if we + got enough data. If bcc == -1, we don't care. */ +static INLINE int +smb_verify (byte * packet, int command, int wct, int bcc) +{ + return (SMB_CMD (packet) == command && + SMB_WCT (packet) >= wct && + (bcc == -1 || SMB_BCC (packet) >= bcc)) ? 0 : -EIO; +} + +static int +smb_errno (int errcls, int error) +{ + int result,i; + + if (errcls) + smb_printerr (errcls, error); + + if (errcls == ERRDOS) + { + static const int map[][2] = + { + { ERRbadfunc,EINVAL }, + { ERRbadfile,ENOENT }, + { ERRbadpath,ENOENT }, + { ERRnofids,EMFILE }, + { ERRnoaccess,EACCES }, + { ERRbadfid,EBADF }, + { ERRbadmcb,EIO }, + { ERRnomem,ENOMEM }, + { ERRbadmem,EFAULT }, + { ERRbadenv,EIO }, + { ERRbadformat,EIO }, + { ERRbadaccess,EACCES }, + { ERRbaddata,E2BIG }, + { ERRbaddrive,ENXIO }, + { ERRremcd,EIO }, + { ERRdiffdevice,EXDEV }, + { ERRnofiles,0 }, + { ERRbadshare,ETXTBSY }, + { ERRlock,EDEADLK }, + { ERRfilexists,EEXIST }, + { 87,0 },/* Unknown error! */ + { 183,EEXIST },/* This next error seems to occur on an mv when + the destination exists */ + { -1,-1 } + }; + + result = EIO; + + for(i = 0 ; map[i][0] != -1 ; i++) + { + if(map[i][0] == error) + { + result = map[i][1]; + break; + } + } + } + else if (errcls == ERRSRV) + { + static const int map[][2] = + { + { ERRerror,ENFILE }, + { ERRbadpw,EINVAL }, + { ERRbadtype,EIO }, + { ERRaccess,EACCES }, + { -1, -1 } + }; + + result = EIO; + + for(i = 0 ; map[i][0] != -1 ; i++) + { + if(map[i][0] == error) + { + result = map[i][1]; + break; + } + } + } + else if (errcls == ERRHRD) + { + static const int map[][2] = + { + { ERRnowrite,EROFS }, + { ERRbadunit,ENODEV }, + { ERRnotready,EBUSY }, + { ERRbadcmd,EIO }, + { ERRdata,EIO }, + { ERRbadreq,ERANGE }, + { ERRbadshare,ETXTBSY }, + { ERRlock,EDEADLK }, + { -1, -1 } + }; + + result = EIO; + + for(i = 0 ; map[i][0] != -1 ; i++) + { + if(map[i][0] == error) + { + result = map[i][1]; + break; + } + } + } + else if (errcls == ERRCMD) + { + result = EIO; + } + else + { + result = 0; + } + + return(result); +} + +#if DEBUG + +static char +print_char (char c) +{ + if ((c < ' ') || (c > '~')) + return '.'; + + return c; +} + +static void +smb_dump_packet (byte * packet) +{ + int i, j, len; + int errcls, error; + + errcls = (int) packet[9]; + error = (int) (int) (packet[11] | packet[12] << 8); + + LOG (("smb_len = %ld valid = %ld \n", len = smb_len (packet), smb_valid_packet (packet))); + LOG (("smb_cmd = %ld smb_wct = %ld smb_bcc = %ld\n", packet[8], SMB_WCT (packet), SMB_BCC (packet))); + LOG (("smb_rcls = %ld smb_err = %ld\n", errcls, error)); + + if (errcls) + smb_printerr (errcls, error); + + if (len > 100) + len = 100; + + PRINTHEADER(); + + for (i = 0; i < len; i += 10) + { + PRINTF (("%03ld:", i)); + + for (j = i; j < i + 10; j++) + { + if (j < len) + PRINTF (("%02lx ", packet[j])); + else + PRINTF ((" ")); + } + + PRINTF ((": ")); + + for (j = i; j < i + 10; j++) + { + if (j < len) + PRINTF (("%lc", print_char (packet[j]))); + } + + PRINTF (("\n")); + } +} + +#endif /* DEBUG */ + +/* smb_request_ok: We expect the server to be locked. Then we do the + request and check the answer completely. When smb_request_ok + returns 0, you can be quite sure that everything went well. When + the answer is <=0, the returned number is a valid unix errno. */ +static int +smb_request_ok (struct smb_server *s, int command, int wct, int bcc) +{ + int result; + int error; + + s->rcls = 0; + s->err = 0; + + result = smb_request (s); + if (result < 0) + { + LOG (("smb_request failed\n")); + } + else if ((error = smb_valid_packet (s->packet)) != 0) + { + LOG (("not a valid packet!\n")); + result = error; + } + else if (s->rcls != 0) + { + result = -smb_errno (s->rcls, s->err); + } + else if ((error = smb_verify (s->packet, command, wct, bcc)) != 0) + { + LOG (("smb_verify failed\n")); + result = error; + } + + return(result); +} + +/* smb_retry: This function should be called when smb_request_ok has + indicated an error. If the error was indicated because the + connection was killed, we try to reconnect. If smb_retry returns 0, + the error was indicated for another reason, so a retry would not be + of any use. */ +static int +smb_retry (struct smb_server *server) +{ + int result = 0; + + if (server->state == CONN_VALID) + goto out; + + if (smb_release (server) < 0) + { + LOG (("smb_retry: smb_release failed\n")); + server->state = CONN_RETRIED; + goto out; + } + + if (smb_proc_reconnect (server) < 0) + { + LOG (("smb_proc_reconnect failed\n")); + server->state = CONN_RETRIED; + goto out; + } + + server->state = CONN_VALID; + result = 1; + + out: + + return result; +} + +static int +smb_request_ok_unlock (struct smb_server *s, int command, int wct, int bcc) +{ + int result; + + result = smb_request_ok (s, command, wct, bcc); + + return result; +} + +/* smb_setup_header: We completely set up the packet. You only have to + insert the command-specific fields */ +static byte * +smb_setup_header (struct smb_server *server, byte command, word wct, word bcc) +{ + dword xmit_len = SMB_HEADER_LEN + wct * sizeof (word) + bcc + 2; + byte *p = server->packet; + byte *buf = server->packet; + + p = smb_encode_smb_length (p, xmit_len); + + BSET (p, 0, 0xff); + BSET (p, 1, 'S'); + BSET (p, 2, 'M'); + BSET (p, 3, 'B'); + BSET (p, 4, command); + + p += 5; + memset (p, '\0', 19); + p += 19; + p += 8; + + WSET (buf, smb_tid, server->tid); + WSET (buf, smb_pid, 0); /* server->pid */ + WSET (buf, smb_uid, server->server_uid); + WSET (buf, smb_mid, 0); /* server->mid */ + + if (server->protocol > PROTOCOL_CORE) + { + BSET (buf, smb_flg, 0x8); + WSET (buf, smb_flg2, 0x3); + } + + (*p++) = wct; /* wct */ + p += 2 * wct; + WSET (p, 0, bcc); + + return p + 2; +} + +/* smb_setup_header_exclusive waits on server->lock and locks the + server, when it's free. You have to unlock it manually when you're + finished with server->packet! */ +static byte * +smb_setup_header_exclusive (struct smb_server *server, byte command, word wct, word bcc) +{ + byte * result; + + result = smb_setup_header (server, command, wct, bcc); + + return result; +} + +/***************************************************************************** + * + * File operation section. + * + ****************************************************************************/ + +int +smb_proc_open (struct smb_server *server, const char *pathname, int len, struct smb_dirent *entry) +{ + int error; + char *p; + char *buf = server->packet; + const word o_attr = aSYSTEM | aHIDDEN | aDIR; + + LOG (("path=%s\n", pathname)); + + retry: + + p = smb_setup_header (server, SMBopen, 2, 2 + len); + WSET (buf, smb_vwv0, 0x42); /* read/write */ + WSET (buf, smb_vwv1, o_attr); + smb_encode_ascii (p, pathname, len); + + if ((error = smb_request_ok (server, SMBopen, 7, 0)) < 0) + { + if (smb_retry (server)) + goto retry; + + if (error != -EACCES) + goto out; + + p = smb_setup_header (server, SMBopen, 2, 2 + len); + WSET (buf, smb_vwv0, 0x40); /* read only */ + WSET (buf, smb_vwv1, o_attr); + smb_encode_ascii (p, pathname, len); + + if ((error = smb_request_ok (server, SMBopen, 7, 0)) < 0) + { + if (smb_retry (server)) + goto retry; + + goto out; + } + } + + /* We should now have data in vwv[0..6]. */ + entry->fileid = WVAL (buf, smb_vwv0); + entry->attr = WVAL (buf, smb_vwv1); + entry->ctime = entry->atime = entry->mtime = local2utc (DVAL (buf, smb_vwv2)); + entry->size = DVAL (buf, smb_vwv4); + entry->opened = 1; + + out: + + return error; +} + +/* smb_proc_close: in finfo->mtime we can send a modification time to + the server */ +int +smb_proc_close (struct smb_server *server, word fileid, dword mtime) +{ + char *buf = server->packet; + int local_time; + int result; + + if(mtime != 0 && mtime != 0xffffffff) + { + /* 0 and 0xffffffff mean: do not set mtime */ + local_time = utc2local (mtime); + } + else + { + local_time = mtime; + } + + smb_setup_header_exclusive (server, SMBclose, 3, 0); + WSET (buf, smb_vwv0, fileid); + DSET (buf, smb_vwv1, local_time); + + result = smb_request_ok_unlock (server, SMBclose, 0, 0); + + return result; +} + +/* In smb_proc_read and smb_proc_write we do not retry, because the + file-id would not be valid after a reconnection. */ + +/* smb_proc_read: fs indicates if it should be copied with + memcpy_tofs. */ +int +smb_proc_read (struct smb_server *server, struct smb_dirent *finfo, off_t offset, long count, char *data, int fs) +{ + word returned_count, data_len; + char *buf = server->packet; + int result; + int error; + + smb_setup_header_exclusive (server, SMBread, 5, 0); + + WSET (buf, smb_vwv0, finfo->fileid); + WSET (buf, smb_vwv1, count); + DSET (buf, smb_vwv2, offset); + WSET (buf, smb_vwv4, 0); + + if ((error = smb_request_ok (server, SMBread, 5, -1)) < 0) + { + result = error; + goto out; + } + + returned_count = WVAL (buf, smb_vwv0); + + smb_decode_data (SMB_BUF (server->packet), data, &data_len, fs); + + if (returned_count != data_len) + { + LOG (("Warning, returned_count != data_len\n")); + LOG (("ret_c=%ld, data_len=%ld\n", returned_count, data_len)); + } + else + { + LOG (("ret_c=%ld, data_len=%ld\n", returned_count, data_len)); + } + + result = data_len; + + out: + + return result; +} + +/* count must be <= 65535. No error number is returned. A result of 0 + indicates an error, which has to be investigated by a normal read + call. */ +int +smb_proc_read_raw (struct smb_server *server, struct smb_dirent *finfo, off_t offset, long count, char *data) +{ + char *buf = server->packet; + int result; + + smb_setup_header_exclusive (server, SMBreadbraw, 8, 0); + + WSET (buf, smb_vwv0, finfo->fileid); + DSET (buf, smb_vwv1, offset); + WSET (buf, smb_vwv3, count); + WSET (buf, smb_vwv4, 0); + DSET (buf, smb_vwv5, 0); + + result = smb_request_read_raw (server, data, count); + + return result; +} + +int +smb_proc_write (struct smb_server *server, struct smb_dirent *finfo, off_t offset, long count, const char *data) +{ + int res; + char *buf = server->packet; + byte *p; + + p = smb_setup_header_exclusive (server, SMBwrite, 5, count + 3); + WSET (buf, smb_vwv0, finfo->fileid); + WSET (buf, smb_vwv1, count); + DSET (buf, smb_vwv2, offset); + WSET (buf, smb_vwv4, 0); + + (*p++) = 1; + WSET (p, 0, count); + memcpy (p + 2, data, count); + + if ((res = smb_request_ok (server, SMBwrite, 1, 0)) >= 0) + res = WVAL (buf, smb_vwv0); + + return res; +} + +/* count must be <= 65535 */ +int +smb_proc_write_raw (struct smb_server *server, struct smb_dirent *finfo, off_t offset, long count, const char *data) +{ + char *buf = server->packet; + int result; + long len = server->max_xmit - 4; + byte *p; + + if (len >= count) + len = 0; + else + len = count - len; /* transfer the larger part using the second packet */ + + p = smb_setup_header_exclusive (server, SMBwritebraw, 11, len); + + WSET (buf, smb_vwv0, finfo->fileid); + DSET (buf, smb_vwv1, count); + DSET (buf, smb_vwv3, offset); + DSET (buf, smb_vwv5, 0); /* timeout */ + WSET (buf, smb_vwv7, 1); /* send final result response */ + DSET (buf, smb_vwv8, 0); /* reserved */ + + if (server->protocol > PROTOCOL_COREPLUS) + { + WSET (buf, smb_vwv10, len); + WSET (buf, smb_vwv11, p - smb_base(buf)); + } + + if (len > 0) + memcpy (p, data, len); + + result = smb_request_ok (server, SMBwritebraw, 1, 0); + + LOG (("first request returned %ld\n", result)); + + if (result < 0) + goto out; + + result = smb_request_write_raw (server, data + len, count - len); + + LOG(("raw request returned %ld\n", result)); + + if (result > 0) + { + int error; + + /* We have to do the checks of smb_request_ok here as well */ + if ((error = smb_valid_packet (server->packet)) != 0) + { + LOG (("not a valid packet!\n")); + result = error; + } + else if (server->rcls != 0) + { + result = -smb_errno (server->rcls, server->err); + } + else if ((error = smb_verify (server->packet, SMBwritec, 1, 0)) != 0) + { + LOG (("smb_verify failed\n")); + result = error; + } + + /* If everything went fine, the whole block has been transfered. */ + if (result == (count - len)) + result = count; + } + + out: + + return result; +} + +int +smb_proc_lseek (struct smb_server *server, struct smb_dirent *finfo, off_t offset, int mode, off_t * new_position_ptr) +{ + char *buf = server->packet; + int error; + + retry: + + smb_setup_header (server, SMBlseek, 4,0); + + WSET (buf, smb_vwv0, finfo->fileid); + WSET (buf, smb_vwv1, mode); + DSET (buf, smb_vwv2, offset); + + if ((error = smb_request_ok (server, SMBlseek, 1, 0)) < 0) + { + if (smb_retry (server)) + goto retry; + } + else + { + (*new_position_ptr) = DVAL(buf, smb_vwv0); + } + + return error; +} + +/* smb_proc_lockingX: We don't chain any further packets to the initial one */ +int +smb_proc_lockingX (struct smb_server *server, struct smb_dirent *finfo, struct smb_lkrng *locks, int num_entries, int mode, long timeout) +{ + int result; + int num_locks, num_unlocks; + char *buf = server->packet; + char *data; + struct smb_lkrng *p; + int i; + + num_locks = num_unlocks = 0; + + if (mode & 2) + num_unlocks = num_entries; + else + num_locks = num_entries; + + retry: + + data = smb_setup_header(server, SMBlockingX, 8, num_entries * 10); + + BSET (buf, smb_vwv0, 0xFF); + WSET (buf, smb_vwv1, 0); + WSET (buf, smb_vwv2, finfo->fileid); + BSET (buf, smb_vwv3, mode & 1); + DSET (buf, smb_vwv4, timeout); + WSET (buf, smb_vwv6, num_unlocks); + WSET (buf, smb_vwv7, num_locks); + + for (i = 0, p = locks; i < num_entries; i++, p++) + { + WSET (data, SMB_LPID_OFFSET(i), 0); /* server->pid */ + DSET (data, SMB_LKOFF_OFFSET(i), p->offset); + DSET (data, SMB_LKLEN_OFFSET(i), p->len); + } + + if ((result = smb_request_ok (server, SMBlockingX, 0, 0)) < 0) + { + if (smb_retry (server)) + goto retry; + } + + return result; +} + +/* smb_proc_do_create: We expect entry->attry & entry->ctime to be set. */ +static int +smb_proc_do_create (struct smb_server *server, const char *path, int len, struct smb_dirent *entry, word command) +{ + int error; + char *p; + char *buf = server->packet; + int local_time; + + retry: + + p = smb_setup_header (server, command, 3, len + 2); + WSET (buf, smb_vwv0, entry->attr); + local_time = utc2local (entry->ctime); + DSET (buf, smb_vwv1, local_time); + smb_encode_ascii (p, path, len); + + if ((error = smb_request_ok (server, command, 1, 0)) < 0) + { + if (smb_retry (server)) + goto retry; + + goto out; + } + + entry->opened = 1; + entry->fileid = WVAL (buf, smb_vwv0); + + smb_proc_close (server, entry->fileid, entry->mtime); + + out: + + return error; +} + +int +smb_proc_create (struct smb_server *server, const char *path, int len, struct smb_dirent *entry) +{ + return smb_proc_do_create (server, path, len, entry, SMBcreate); +} + +int +smb_proc_mv (struct smb_server *server, const char *opath, const int olen, const char *npath, const int nlen) +{ + char *p; + char *buf = server->packet; + int result; + + retry: + + p = smb_setup_header (server, SMBmv, 1, olen + nlen + 4); + + WSET (buf, smb_vwv0, 0); + + p = smb_encode_ascii (p, opath, olen); + smb_encode_ascii (p, npath, olen); + + if ((result = smb_request_ok (server, SMBmv, 0, 0)) < 0) + { + if (smb_retry (server)) + goto retry; + } + + return result; +} + +int +smb_proc_mkdir (struct smb_server *server, const char *path, const int len) +{ + char *p; + int result; + + retry: + + p = smb_setup_header (server, SMBmkdir, 0, 2 + len); + + smb_encode_ascii (p, path, len); + + if ((result = smb_request_ok (server, SMBmkdir, 0, 0)) < 0) + { + if (smb_retry (server)) + goto retry; + } + + return result; +} + +int +smb_proc_rmdir (struct smb_server *server, const char *path, const int len) +{ + char *p; + int result; + + retry: + + p = smb_setup_header (server, SMBrmdir, 0, 2 + len); + + smb_encode_ascii (p, path, len); + + if ((result = smb_request_ok (server, SMBrmdir, 0, 0)) < 0) + { + if (smb_retry (server)) + goto retry; + } + + return result; +} + +int +smb_proc_unlink (struct smb_server *server, const char *path, const int len) +{ + char *p; + char *buf = server->packet; + int result; + + retry: + + p = smb_setup_header (server, SMBunlink, 1, 2 + len); + + WSET (buf, smb_vwv0, 0); + + smb_encode_ascii (p, path, len); + + if ((result = smb_request_ok (server, SMBunlink, 0, 0)) < 0) + { + if (smb_retry (server)) + goto retry; + } + + return result; +} + +int +smb_proc_trunc (struct smb_server *server, word fid, dword length) +{ + char *p; + char *buf = server->packet; + int result; + + p = smb_setup_header (server, SMBwrite, 5, 3); + WSET (buf, smb_vwv0, fid); + WSET (buf, smb_vwv1, 0); + DSET (buf, smb_vwv2, length); + WSET (buf, smb_vwv4, 0); + smb_encode_ascii (p, "", 0); + + result = smb_request_ok (server, SMBwrite, 1, 0); + if (result >= 0) + result = DVAL(buf, smb_vwv0); + + return result; +} + +static char * +smb_decode_dirent (char *p, struct smb_dirent *entry) +{ + size_t name_size; + + p += SMB_STATUS_SIZE; /* reserved (search_status) */ + + entry->attr = BVAL (p, 0); + entry->mtime = entry->atime = entry->ctime = date_dos2unix (WVAL (p, 1), WVAL (p, 3)); + entry->size = DVAL (p, 5); + + name_size = 13; + + if(name_size > entry->complete_path_size-1) + name_size = entry->complete_path_size-1; + + memcpy (entry->complete_path, p + 9, name_size); + + entry->complete_path[name_size] = '\0'; + + LOG (("smb_decode_dirent: path = %s\n", entry->complete_path)); + + return p + 22; +} + +/* This routine is used to read in directory entries from the network. + Note that it is for short directory name seeks, i.e.: protocol < PROTOCOL_LANMAN2 */ +static int +smb_proc_readdir_short (struct smb_server *server, char *path, int fpos, int cache_size, struct smb_dirent *entry) +{ + char *p; + char *buf; + int error; + int result = 0; + int i; + int first, total_count; + struct smb_dirent *current_entry; + word bcc; + word count; + char status[SMB_STATUS_SIZE]; + int entries_asked = (server->max_xmit - 100) / SMB_DIRINFO_SIZE; + int dirlen = strlen (path); + char * mask; + + mask = malloc(dirlen + 4 + 1); + if (mask == NULL) + { + result = (-ENOMEM); + goto out; + } + + strcpy (mask, path); + strcat (mask, "\\*.*"); + + LOG (("SMB call readdir %ld @ %ld\n", cache_size, fpos)); + LOG ((" mask = %s\n", mask)); + + buf = server->packet; + + retry: + + first = 1; + total_count = 0; + current_entry = entry; + + while (1) + { + if (first == 1) + { + p = smb_setup_header (server, SMBsearch, 2, 5 + strlen (mask)); + WSET (buf, smb_vwv0, entries_asked); + WSET (buf, smb_vwv1, aDIR); + p = smb_encode_ascii (p, mask, strlen (mask)); + (*p++) = 5; + (void) smb_encode_word (p, 0); + } + else + { + p = smb_setup_header (server, SMBsearch, 2, 5 + SMB_STATUS_SIZE); + WSET (buf, smb_vwv0, entries_asked); + WSET (buf, smb_vwv1, aDIR); + p = smb_encode_ascii (p, "", 0); + (void) smb_encode_vblock (p, status, SMB_STATUS_SIZE, 0); + } + + if ((error = smb_request_ok (server, SMBsearch, 1, -1)) < 0) + { + if ((server->rcls == ERRDOS) && (server->err == ERRnofiles)) + { + result = total_count - fpos; + goto unlock_return; + } + else + { + if (smb_retry (server)) + goto retry; + + result = error; + + goto unlock_return; + } + } + + p = SMB_VWV (server->packet); + p = smb_decode_word (p, &count); /* vwv[0] = count-returned */ + p = smb_decode_word (p, &bcc); + + first = 0; + + if (count <= 0) + { + result = total_count - fpos; + + goto unlock_return; + } + + if (bcc != count * SMB_DIRINFO_SIZE + 3) + { + result = -EIO; + + goto unlock_return; + } + + p += 3; /* Skipping VBLOCK header (5, length lo, length hi). */ + + /* Read the last entry into the status field. */ + memcpy (status, SMB_BUF (server->packet) + 3 + (count - 1) * SMB_DIRINFO_SIZE, SMB_STATUS_SIZE); + + /* Now we are ready to parse smb directory entries. */ + + for (i = 0; i < count; i++) + { + if (total_count < fpos) + { + p += SMB_DIRINFO_SIZE; + + LOG (("smb_proc_readdir: skipped entry; total_count = %ld, i = %ld, fpos = %ld\n", total_count, i, fpos)); + } + else if (total_count >= fpos + cache_size) + { + result = total_count - fpos; + + goto unlock_return; + } + else + { + p = smb_decode_dirent (p, current_entry); + + current_entry += 1; + } + + total_count += 1; + } + } + + unlock_return: + + if(mask != NULL) + free(mask); + + out: + + return result; +} + +/*****************************************************************************/ + +#define MAX_DIGITS 4 /* NOTE: this is actually a hard-coded value (see below) */ + +/* Multiprecision arithmetics for beginners; we use 64 bits, chopped + * into 16 bit pieces each. That allows us to comfortably execute all + * calculations with 'long' arguments and not get into trouble because + * of overflows and carry bits that don't exist. The assumption is that + * the size of a 'long' is twice as large as that of a 'short'. + */ +struct digits +{ + unsigned short d[MAX_DIGITS]; +}; + +/* Divide a multiprecision number by a positive integer. This is + * essentially the division by a single "digit", as described in + * Donald E. Knuth's "The Art of Computer Programming, Volume 2 + * Seminumerical Algorithms", 3rd edition, page 625 as the answer + * to exercise 16 on page 282 in section 4.3.1. We don't really + * need the more complex version since we just need to divide by + * 10,000 and then by 1,000. The complex version is much more + * complex than calling this division routine twice. + */ +static void +divide_by(struct digits * q,unsigned long d) +{ + int m; + + m = MAX_DIGITS; + while(m > 0 && q->d[m-1] == 0) + m--; + + if(m > 0) + { + unsigned long r,s; + int j; + + r = 0; + j = m - 1; + + do + { + r <<= 16; + + s = (r + q->d[j]); + + q->d[j] = s / d; + r = s % d; + } + while(--j >= 0); + } +} + +/* This is an application of algorithm S ('Subtraction of + * non-negative integers') described in Donald E. Knuth's + * "The Art of Computer Programming, Volume 2 / Seminumerical + * Algorithms", 3rd edition, page 267. + */ +static long +subtract_number(struct digits * u,unsigned long v) +{ + long d; + + /* This implementation takes advantage of the fact that + * the 'width' of 'v' (64 bits) is just half the size of + * 'u' (32 bits), which allows us to unroll the loop and + * leave out some of the arguments which would not + * contribute to the result anyway. + */ + + /* First round */ + d = ((long)u->d[0]) - (long)(v & 0xFFFF); + u->d[0] = d; + + /* Second round */ + d = ((long)u->d[1]) - ((long)((v >> 16) & 0xFFFF)) - (d < 0); + u->d[1] = d; + + /* Third round */ + d = ((long)u->d[2]) - (d < 0); + u->d[2] = d; + + /* Fourth round */ + d = ((long)u->d[3]) - (d < 0); + u->d[3] = d; + + return(d < 0); +} + +/* Interpret an 8 byte "filetime" structure to a 'time_t'. + * It's originally in "100ns units since jan 1st 1601". + * + * Unlike the Samba implementation of that date conversion + * algorithm this one tries to perform the entire + * calculation using integer operations only. + */ +static time_t +interpret_long_date(char * p) +{ + unsigned long hi,lo; + struct digits d; + time_t result; + int i,k; + + /* Extract the 64 bit time value. */ + lo = DVAL(p,0); + hi = DVAL(p,4); + + /* Chop the time into handy 16 bit chunks. */ + d.d[3] = hi >> 16; + d.d[2] = hi & 0xFFFF; + d.d[1] = lo >> 16; + d.d[0] = lo & 0xFFFF; + + /* Divide by 10,000,000 in two easy pieces. */ + divide_by(&d,10000); + divide_by(&d,1000); + + /* Adjust by 369 years (11,644,473,600 seconds). */ + k = 0; + for(i = 0 ; k == 0 && i < 4 ; i++) + k += subtract_number(&d,2911118400UL); + + /* If the result did not produce an underflow or overflow, + * return the number of seconds encoded in the two least + * significant 'digits'. + */ + if(k == 0 && d.d[3] == 0 && d.d[2] == 0) + result = (time_t)((((unsigned long)d.d[1]) << 16) + d.d[0]); + else + result = 0; + + return(result); +} + +/*****************************************************************************/ + +static void +smb_get_dirent_name(char *p,int level,char ** name_ptr,int * len_ptr) +{ + switch (level) + { + case 1: /* OS/2 understands this */ + (*name_ptr) = p + 27; + (*len_ptr) = strlen(p + 27); + break; + + case 2: /* this is what OS/2 uses */ + (*name_ptr) = p + 31; + (*len_ptr) = strlen(p + 31); + break; + + case 260: /* NT uses this, but also accepts 2 */ + (*name_ptr) = p + 94; + (*len_ptr) = min (DVAL (p+60, 0), SMB_MAXNAMELEN); + break; + + default: + (*name_ptr) = NULL; + (*len_ptr) = 0; + break; + } +} + +/* interpret a long filename structure - this is mostly guesses at the + moment. The length of the structure is returned. The structure of + a long filename depends on the info level. 260 is used by NT and 2 + is used by OS/2. */ +static char * +smb_decode_long_dirent (char *p, struct smb_dirent *finfo, int level) +{ + char *result; + + switch (level) + { + case 1: /* OS/2 understands this */ + + #if DEBUG + { + char buffer[255]; + + memcpy(buffer,p + 27,sizeof(buffer)-1); + buffer[sizeof(buffer)-1] = '\0'; + + LOG(("type=%ld, name='%s'\n",level,buffer)); + } + #endif /* DEBUG */ + + if (finfo != NULL) + { + strlcpy (finfo->complete_path, p + 27, finfo->complete_path_size); + finfo->len = strlen (finfo->complete_path); + finfo->size = DVAL (p, 16); + finfo->attr = BVAL (p, 24); + finfo->ctime = date_dos2unix (WVAL (p, 6), WVAL (p, 4)); + finfo->atime = date_dos2unix (WVAL (p, 10), WVAL (p, 8)); + finfo->mtime = date_dos2unix (WVAL (p, 14), WVAL (p, 12)); + } + + result = p + 28 + BVAL (p, 26); + + break; + + case 2: /* this is what OS/2 uses */ + + #if DEBUG + { + char buffer[255]; + + memcpy(buffer,p + 31,sizeof(buffer)-1); + buffer[sizeof(buffer)-1] = '\0'; + + LOG(("type=%ld, name='%s'\n",level,buffer)); + } + #endif /* DEBUG */ + + if (finfo != NULL) + { + strlcpy (finfo->complete_path, p + 31, finfo->complete_path_size); + finfo->len = strlen (finfo->complete_path); + finfo->size = DVAL (p, 16); + finfo->attr = BVAL (p, 24); + finfo->ctime = date_dos2unix (WVAL (p, 6), WVAL (p, 4)); + finfo->atime = date_dos2unix (WVAL (p, 10), WVAL (p, 8)); + finfo->mtime = date_dos2unix (WVAL (p, 14), WVAL (p, 12)); + } + + result = p + 32 + BVAL (p, 30); + + break; + + case 260: /* NT uses this, but also accepts 2 */ + + #if DEBUG + { + char buffer[255]; + int len; + + len = min (DVAL (p+60, 0), sizeof(buffer)-1); + + memcpy(buffer,p+94,len); + buffer[len] = '\0'; + + LOG(("type=%ld, name='%s'\n",level,buffer)); + } + #endif /* DEBUG */ + + result = p + WVAL (p, 0); + + if (finfo != NULL) + { + int namelen; + + p += 4; /* next entry offset */ + p += 4; /* fileindex */ + finfo->ctime = interpret_long_date(p); + p += 8; + finfo->atime = interpret_long_date(p); + p += 8; + p += 8; /* write time */ + finfo->mtime = interpret_long_date(p); + p += 8; + finfo->size = DVAL (p, 0); + p += 8; + p += 8; /* alloc size */ + finfo->attr = BVAL (p, 0); + p += 4; + namelen = min (DVAL (p, 0), SMB_MAXNAMELEN); + p += 4; + p += 4; /* EA size */ + p += 2; /* short name len? */ + p += 24; /* short name? */ + + if(namelen > (int)finfo->complete_path_size-1) + namelen = finfo->complete_path_size-1; + + memcpy (finfo->complete_path, p, namelen); + finfo->complete_path[namelen] = '\0'; + finfo->len = namelen; + } + + break; + + default: + + if (finfo != NULL) + { + /* I have to set times to 0 here, because I do not + have specs about this for all info levels. */ + finfo->ctime = finfo->mtime = finfo->atime = 0; + } + + LOG (("Unknown long filename format %ld\n", level)); + + result = p + WVAL (p, 0); + + break; + } + + return result; +} + +static int +smb_proc_readdir_long (struct smb_server *server, char *path, int fpos, int cache_size, struct smb_dirent *entry) +{ + int max_matches = 512; /* this should actually be based on the max_xmit value */ + + /* NT uses 260, OS/2 uses 2. Both accept 1. */ + int info_level = server->protocol < PROTOCOL_NT1 ? 1 : 260; + + char *p; + int i; + int first; + int total_count = 0; + struct smb_dirent *current_entry; + + char *resp_data; + char *resp_param; + int resp_data_len = 0; + int resp_param_len = 0; + + int attribute = aSYSTEM | aHIDDEN | aDIR; + int result; + int error = 0; + + int ff_searchcount; + int ff_eos = 0; + int ff_dir_handle = 0; + int ff_resume_key = 0; + int loop_count = 0; + + unsigned char *outbuf = server->packet; + + int dirlen = strlen (path) + 2 + 1; + char *mask; + int masklen; + + ENTER(); + + /* ZZZ experimental 'max_matches' adjustment */ + /* + if(info_level == 260) + max_matches = server->max_xmit / 360; + else + max_matches = server->max_xmit / 40; + */ + + SHOWVALUE(server->max_xmit); + SHOWVALUE(max_matches); + + mask = malloc (dirlen); + if (mask == NULL) + { + LOG (("Memory allocation failed\n")); + error = (-ENOMEM); + SHOWVALUE(error); + goto out; + } + + strcpy (mask, path); + strcat (mask, "\\*"); + masklen = strlen (mask); + + LOG (("SMB call lreaddir %ld @ %ld\n", cache_size, fpos)); + LOG ((" mask = %s\n", mask)); + + resp_param = NULL; + resp_data = NULL; + + retry: + + first = 1; + total_count = 0; + current_entry = entry; + + while (ff_eos == 0) + { + loop_count++; + if (loop_count > 200) + { + LOG (("smb_proc_readdir_long: Looping in FIND_NEXT???\n")); + error = -EIO; + SHOWVALUE(error); + break; + } + + memset (outbuf, 0, 39); + + smb_setup_header (server, SMBtrans2, 15, 5 + 12 + masklen + 1); + + WSET (outbuf, smb_tpscnt, 12 + masklen + 1); + WSET (outbuf, smb_tdscnt, 0); + WSET (outbuf, smb_mprcnt, 10); + WSET (outbuf, smb_mdrcnt, server->max_xmit); + WSET (outbuf, smb_msrcnt, 0); + WSET (outbuf, smb_flags, 0); + DSET (outbuf, smb_timeout, 0); + WSET (outbuf, smb_pscnt, WVAL (outbuf, smb_tpscnt)); + WSET (outbuf, smb_psoff, ((SMB_BUF (outbuf) + 3) - outbuf) - 4); + WSET (outbuf, smb_dscnt, 0); + WSET (outbuf, smb_dsoff, 0); + WSET (outbuf, smb_suwcnt, 1); + WSET (outbuf, smb_setup0, first == 1 ? TRANSACT2_FINDFIRST : TRANSACT2_FINDNEXT); + + p = SMB_BUF (outbuf); + (*p++) = 0; /* put in a null smb_name */ + (*p++) = 'D'; + (*p++) = ' '; /* this was added because OS/2 does it */ + + if (first != 0) + { + LOG (("first match\n")); + WSET (p, 0, attribute); /* attribute */ + WSET (p, 2, max_matches); /* max count */ + WSET (p, 4, 8 + 4 + 2); /* resume required + close on end + continue */ + WSET (p, 6, info_level); + DSET (p, 8, 0); + } + else + { + LOG (("next match; ff_dir_handle=0x%lx ff_resume_key=%ld mask='%s'\n", ff_dir_handle, ff_resume_key, mask)); + WSET (p, 0, ff_dir_handle); + WSET (p, 2, max_matches); /* max count */ + WSET (p, 4, info_level); + DSET (p, 6, ff_resume_key); + WSET (p, 10, 8 + 4 + 2); /* resume required + close on end + continue */ + } + + p += 12; + + if(masklen > 0) + memcpy (p, mask, masklen); + + p += masklen; + (*p++) = 0; + (*p) = 0; + + result = smb_trans2_request (server, &resp_data_len, &resp_param_len, &resp_data, &resp_param); + + LOG (("smb_proc_readdir_long: smb_trans2_request returns %ld\n", result)); + + if (result < 0) + { + if (smb_retry (server)) + goto retry; + + LOG (("smb_proc_readdir_long: got error from trans2_request\n")); + error = result; + SHOWVALUE(error); + break; + } + + /* Apparently, there is a bug in Windows 95 and friends which + causes the directory read attempt to fail if you're asking + for too much data too fast... */ + if(server->rcls == ERRSRV && server->err == ERRerror) + { + SHOWMSG("ouch; delaying and retrying"); + + Delay(TICKS_PER_SECOND / 5); + + continue; + } + + if (server->rcls != 0) + { + LOG (("server->rcls = %ld err = %ld\n",server->rcls, server->err)); + error = smb_errno (server->rcls, server->err); + SHOWVALUE(error); + break; + } + + /* ZZZ bail out if this is empty. */ + if (resp_param == NULL) + break; + + /* parse out some important return info */ + p = resp_param; + if (first != 0) + { + ff_dir_handle = WVAL (p, 0); + ff_searchcount = WVAL (p, 2); + ff_eos = WVAL (p, 4); + } + else + { + ff_searchcount = WVAL (p, 0); + ff_eos = WVAL (p, 2); + } + + LOG (("received %ld entries (eos=%ld)\n",ff_searchcount, ff_eos)); + if (ff_searchcount == 0) + break; + + /* ZZZ bail out if this is empty. */ + if (resp_data == NULL) + break; + + /* point to the data bytes */ + p = resp_data; + + /* Now we are ready to parse smb directory entries. */ + for (i = 0; i < ff_searchcount; i++) + { + if(i == ff_searchcount - 1) + { + char * last_name; + int len; + + ff_resume_key = DVAL(p, 0); + + smb_get_dirent_name(p,info_level,&last_name,&len); + if(len > 0) + { + #if DEBUG + { + char buffer[SMB_MAXNAMELEN+1]; + + memcpy(buffer,last_name,len); + buffer[len] = '\0'; + + LOG(("last name = '%s'\n",buffer)); + } + #endif /* DEBUG */ + + if(len + 1 > dirlen) + { + D(("increasing mask; old value = %ld new value = %ld",dirlen,len+1)); + + if(mask != NULL) + free (mask); + + dirlen = len + 1; + SHOWVALUE(dirlen); + + mask = malloc (dirlen); + if (mask == NULL) + { + LOG (("smb_proc_readdir_long: Memory allocation failed\n")); + + error = -ENOMEM; + + SHOWVALUE(error); + + goto fail; + } + } + + memcpy (mask, last_name, len); + mask[len] = '\0'; + masklen = len; + } + else + { + masklen = 0; + } + } + + if (total_count < fpos) + { + p = smb_decode_long_dirent (p, NULL, info_level); + + LOG (("smb_proc_readdir: skipped entry; total_count = %ld, i = %ld, fpos = %ld\n",total_count, i, fpos)); + } + else if (total_count >= fpos + cache_size) + { + p = smb_decode_long_dirent (p, NULL, info_level); + + LOG (("smb_proc_readdir: skipped entry; total_count = %ld, i = %ld, fpos = %ld\n",total_count, i, fpos)); + + continue; + } + else + { + p = smb_decode_long_dirent (p, current_entry, info_level); + + current_entry += 1; + } + + total_count += 1; + } + + SHOWVALUE(ff_resume_key); + + if (resp_data != NULL) + { + free (resp_data); + resp_data = NULL; + } + + if (resp_param != NULL) + { + free (resp_param); + resp_param = NULL; + } + + first = 0; + + if (ff_searchcount > 0) + loop_count = 0; + } + + fail: + + /* finished: not needed any more */ + if (mask != NULL) + free (mask); + + if (resp_data != NULL) + free (resp_data); + + if (resp_param != NULL) + free (resp_param); + + out: + + if(error < 0) + { + RETURN(error); + return(error); + } + else + { + RETURN (total_count - fpos); + return (total_count - fpos); + } +} + +int +smb_proc_readdir (struct smb_server *server, char *path, int fpos, int cache_size, struct smb_dirent *entry) +{ + int result; + + if (server->protocol >= PROTOCOL_LANMAN2) + result = smb_proc_readdir_long (server, path, fpos, cache_size, entry); + else + result = smb_proc_readdir_short (server, path, fpos, cache_size, entry); + + return result; +} + +int +smb_proc_getattr_core (struct smb_server *server, const char *path, int len, struct smb_dirent *entry) +{ + int result; + char *p; + char *buf = server->packet; + + LOG (("smb_proc_getattr: %s\n", path)); + + retry: + + p = smb_setup_header (server, SMBgetatr, 0, 2 + len); + smb_encode_ascii (p, path, len); + + if ((result = smb_request_ok (server, SMBgetatr, 10, 0)) < 0) + { + if (smb_retry (server)) + goto retry; + + goto out; + } + + entry->attr = WVAL (buf, smb_vwv0); + + /* The server only tells us 1 time */ + entry->ctime = entry->atime = entry->mtime = local2utc (DVAL (buf, smb_vwv1)); + + entry->size = DVAL (buf, smb_vwv3); + + out: + + return result; +} + +/* smb_proc_getattrE: entry->fid must be valid */ +int +smb_proc_getattrE (struct smb_server *server, struct smb_dirent *entry) +{ + char *buf = server->packet; + int result; + + smb_setup_header_exclusive (server, SMBgetattrE, 1, 0); + WSET (buf, smb_vwv0, entry->fileid); + + if ((result = smb_request_ok (server, SMBgetattrE, 11, 0)) < 0) + goto out; + + entry->ctime = date_dos2unix (WVAL (buf, smb_vwv1), WVAL (buf, smb_vwv0)); + entry->atime = date_dos2unix (WVAL (buf, smb_vwv3), WVAL (buf, smb_vwv2)); + entry->mtime = date_dos2unix (WVAL (buf, smb_vwv5), WVAL (buf, smb_vwv4)); + entry->size = DVAL (buf, smb_vwv6); + entry->attr = WVAL (buf, smb_vwv10); + + out: + + return result; +} + +/* In core protocol, there is only 1 time to be set, we use + entry->mtime, to make touch work. */ +int +smb_proc_setattr_core (struct smb_server *server, const char *path, int len, struct smb_dirent *new_finfo) +{ + char *p; + char *buf = server->packet; + int result; + int local_time; + + retry: + + LOG (("smb_proc_setattr_core\n")); + + p = smb_setup_header (server, SMBsetatr, 8, 4 + len); + WSET (buf, smb_vwv0, new_finfo->attr); + local_time = utc2local (new_finfo->mtime); + DSET (buf, smb_vwv1, local_time); + p = smb_encode_ascii (p, path, len); + (void) smb_encode_ascii (p, "", 0); + + if ((result = smb_request_ok (server, SMBsetatr, 0, 0)) < 0) + { + if (smb_retry (server)) + goto retry; + } + + return result; +} + +/* smb_proc_setattrE: we do not retry here, because we rely on fid, + which would not be valid after a retry. */ +int +smb_proc_setattrE (struct smb_server *server, word fid, struct smb_dirent *new_entry) +{ + char *buf = server->packet; + word date, time_value; + int result; + + LOG (("smb_proc_setattrE\n")); + + smb_setup_header_exclusive (server, SMBsetattrE, 7, 0); + + WSET (buf, smb_vwv0, fid); + + date_unix2dos (new_entry->ctime, &time_value, &date); + WSET (buf, smb_vwv1, date); + WSET (buf, smb_vwv2, time_value); + + date_unix2dos (new_entry->atime, &time_value, &date); + WSET (buf, smb_vwv3, date); + WSET (buf, smb_vwv4, time_value); + + date_unix2dos (new_entry->mtime, &time_value, &date); + WSET (buf, smb_vwv5, date); + WSET (buf, smb_vwv6, time_value); + + result = smb_request_ok_unlock (server, SMBsetattrE, 0, 0); + + return result; +} + +int +smb_proc_dskattr (struct smb_server *server, struct smb_dskattr *attr) +{ + int error; + char *p; + + retry: + + smb_setup_header (server, SMBdskattr, 0, 0); + + if ((error = smb_request_ok (server, SMBdskattr, 5, 0)) < 0) + { + if (smb_retry (server)) + goto retry; + + goto out; + } + + p = SMB_VWV (server->packet); + p = smb_decode_word (p, &attr->total); + p = smb_decode_word (p, &attr->allocblocks); + p = smb_decode_word (p, &attr->blocksize); + (void) smb_decode_word (p, &attr->free); + + out: + + return error; +} + +/***************************************************************************** + * + * Mount/umount operations. + * + ****************************************************************************/ +struct smb_prots +{ + enum smb_protocol prot; + const char *name; +}; + +/* smb_proc_reconnect: We expect the server to be locked, so that you + can call the routine from within smb_retry. The socket must be + created, like after a user-level socket()-call. It may not be + connected. */ +static int +smb_proc_reconnect (struct smb_server *server) +{ + static const struct smb_prots prots[] = + { + {PROTOCOL_CORE, "PC NETWORK PROGRAM 1.0"}, + {PROTOCOL_COREPLUS, "MICROSOFT NETWORKS 1.03"}, + {PROTOCOL_LANMAN1, "MICROSOFT NETWORKS 3.0"}, + {PROTOCOL_LANMAN1, "LANMAN1.0"}, + {PROTOCOL_LANMAN2, "LM1.2X002"}, + {PROTOCOL_NT1, "NT LM 0.12"}, + {PROTOCOL_NT1, "NT LANMAN 1.0"}, + + {-1, NULL} + }; + + char dev[] = "A:"; + int i, plength; + int max_xmit = 1024; /* Space needed for first request. */ + int given_max_xmit = server->mount_data.max_xmit; + int result; + word any_word; + byte *p; + unsigned char password[24]; + int password_len; + unsigned char nt_password[24]; + int nt_password_len; + unsigned char full_share[SMB_MAXNAMELEN+1]; + int full_share_len; + byte *packet; + + if (server->max_recv <= 0) + server->max_recv = given_max_xmit > 8000 ? given_max_xmit : 8000; + + if ((result = smb_connect (server)) < 0) + { + LOG (("smb_proc_reconnect: could not smb_connect\n")); + goto fail; + } + + /* Here we assume that the connection is valid */ + server->state = CONN_VALID; + + if (server->packet != NULL) + free (server->packet); + + server->packet = malloc (server->max_recv); + + if (server->packet == NULL) + { + LOG (("smb_proc_connect: No memory! Bailing out.\n")); + result = -ENOMEM; + goto fail; + } + + packet = server->packet; + + server->max_xmit = max_xmit; + + /* Start with an RFC1002 session request packet. */ + p = packet + 4; + + p = smb_name_mangle (p, server->mount_data.server_name); + p = smb_name_mangle (p, server->mount_data.client_name); + + smb_encode_smb_length (packet, (byte *) p - (byte *) (packet)); + + packet[0] = 0x81; /* SESSION REQUEST */ + + if ((result = smb_request (server)) < 0) + { + LOG (("smb_proc_connect: Failed to send SESSION REQUEST.\n")); + goto fail; + } + + if (packet[0] != 0x82) + { + LOG (("smb_proc_connect: Did not receive positive response (err = %lx)\n",packet[0])); + + #if DEBUG + { + smb_dump_packet (packet); + } + #endif /* DEBUG */ + + result = -EIO; + goto fail; + } + + LOG (("smb_proc_connect: Passed SESSION REQUEST.\n")); + + /* Now we are ready to send a SMB Negotiate Protocol packet. */ + memset (packet, 0, SMB_HEADER_LEN); + + plength = 0; + for (i = 0; prots[i].name != NULL; i++) + plength += strlen (prots[i].name) + 2; + + smb_setup_header (server, SMBnegprot, 0, plength); + + p = SMB_BUF (packet); + + for (i = 0; prots[i].name != NULL; i++) + p = smb_encode_dialect (p, prots[i].name, strlen (prots[i].name)); + + LOG (("smb_proc_connect: Request SMBnegprot...\n")); + if ((result = smb_request_ok (server, SMBnegprot, 1, -1)) < 0) + { + LOG (("smb_proc_connect: Failure requesting SMBnegprot\n")); + goto fail; + } + + LOG (("Verified!\n")); + + p = SMB_VWV (packet); + p = smb_decode_word (p, &any_word); + i = any_word; + + server->protocol = prots[i].prot; + + LOG (("smb_proc_connect: Server wants %s protocol.\n",prots[i].name)); + + if (server->protocol > PROTOCOL_LANMAN1) + { + int user_len = strlen (server->mount_data.username)+1; + + LOG (("smb_proc_connect: password = %s\n",server->mount_data.password)); + LOG (("smb_proc_connect: usernam = %s\n",server->mount_data.username)); + LOG (("smb_proc_connect: blkmode = %ld\n",WVAL (packet, smb_vwv5))); + + if (server->protocol >= PROTOCOL_NT1) + { + server->maxxmt = DVAL (packet, smb_vwv3 + 1); + server->blkmode = DVAL (packet, smb_vwv9 + 1); + server->sesskey = DVAL (packet, smb_vwv7 + 1); + + server->security_mode = BVAL(packet, smb_vwv1); + + memcpy(server->crypt_key,SMB_BUF(packet),8); + } + else + { + server->maxxmt = WVAL (packet, smb_vwv2); + server->blkmode = WVAL (packet, smb_vwv5); + server->sesskey = DVAL (packet, smb_vwv6); + + server->security_mode = BVAL(packet, smb_vwv1); + + memcpy(server->crypt_key,SMB_BUF(packet),8); + } + + SHOWVALUE(server->security_mode); + + if(server->security_mode & 2) + { + SHOWMSG("encrypted passwords required"); + + memset(password,0,sizeof(password)); + strlcpy(password,server->mount_data.password,sizeof(password)); + + smb_encrypt(password,server->crypt_key,password); + password_len = 24; + + PRINTHEADER(); + PRINTF(("password: ")); + for(i = 0 ; i < 24 ; i++) + PRINTF(("%02lx ",password[i])); + PRINTF(("\n")); + + memset(nt_password,0,sizeof(nt_password)); + strlcpy(nt_password,server->mount_data.password,sizeof(nt_password)); + + smb_nt_encrypt(nt_password,server->crypt_key,nt_password); + nt_password_len = 24; + + PRINTHEADER(); + PRINTF(("nt_password: ")); + for(i = 0 ; i < 24 ; i++) + PRINTF(("%02lx ",nt_password[i])); + PRINTF(("\n")); + + PRINTHEADER(); + PRINTF(("crypt_key: ")); + for(i = 0 ; i < 8 ; i++) + PRINTF(("%02lx ",server->crypt_key[i])); + PRINTF(("\n")); + } + else + { + SHOWMSG("plain text passwords sufficient"); + + password_len = strlen(server->mount_data.password)+1; + nt_password_len = 0; + } + + /* If in share level security then don't send a password now */ + if((server->security_mode & 1) == 0) + { + SHOWMSG("share level security; zapping passwords"); + strcpy(password,""); + password_len = 0; + + strcpy(nt_password,""); + nt_password_len = 0; + } + + SHOWVALUE(password_len); + SHOWVALUE(nt_password_len); + + LOG (("smb_proc_connect: workgroup = %s\n", server->mount_data.workgroup_name)); + if (server->protocol >= PROTOCOL_NT1) + { + char *OS_id = "AmigaOS"; + char *client_id = "smbfs"; + + SHOWMSG("server->protocol >= PROTOCOL_NT1"); + + smb_setup_header (server, SMBsesssetupX, 13, user_len + password_len + nt_password_len + strlen (server->mount_data.workgroup_name)+1 + strlen (OS_id)+1 + strlen (client_id)+1); + + WSET (packet, smb_vwv0, 0xff); + WSET (packet, smb_vwv2, given_max_xmit); + WSET (packet, smb_vwv3, 2); + WSET (packet, smb_vwv4, 0); /* server->pid */ + DSET (packet, smb_vwv5, server->sesskey); + WSET (packet, smb_vwv7, password_len); + WSET (packet, smb_vwv8, nt_password_len); + + p = SMB_BUF (packet); + + if(nt_password_len != 0) + { + SHOWMSG("adding encrypted passwords"); + + memcpy (p, password, password_len); + p += password_len; + + memcpy (p, nt_password, nt_password_len); + p += nt_password_len; + } + else + { + SHOWMSG("adding plain text password"); + + memcpy (p, server->mount_data.password, password_len); + p += password_len; + } + + memcpy (p, server->mount_data.username, user_len); + p += user_len; + + strcpy (p, server->mount_data.workgroup_name); + p += strlen (p) + 1; + + strcpy (p, OS_id); + p += strlen (p) + 1; + + strcpy (p, client_id); + } + else + { + smb_setup_header (server, SMBsesssetupX, 10, user_len + password_len); + + WSET (packet, smb_vwv0, 0xff); + WSET (packet, smb_vwv1, 0); + WSET (packet, smb_vwv2, given_max_xmit); + WSET (packet, smb_vwv3, 2); + WSET (packet, smb_vwv4, 0); /* server->pid */ + DSET (packet, smb_vwv5, server->sesskey); + WSET (packet, smb_vwv7, password_len); + WSET (packet, smb_vwv8, 0); + WSET (packet, smb_vwv9, 0); + + p = SMB_BUF (packet); + memcpy (p, server->mount_data.password, password_len); + p += password_len; + memcpy (p, server->mount_data.username, user_len); + } + + if ((result = smb_request_ok (server, SMBsesssetupX, 3, 0)) < 0) + { + LOG (("smb_proc_connect: SMBsessetupX failed\n")); + goto fail; + } + + smb_decode_word (packet + 32, &(server->server_uid)); + } + else + { + server->maxxmt = 0; + server->blkmode = 0; + server->sesskey = 0; + + password_len = strlen(server->mount_data.password)+1; + + nt_password_len = 0; + } + + if(nt_password_len > 0) + { + strlcpy(full_share,"//",sizeof(full_share)); + strlcat(full_share,server->mount_data.server_name,sizeof(full_share)); + strlcat(full_share,"/",sizeof(full_share)); + strlcat(full_share,server->mount_data.service,sizeof(full_share)); + + full_share_len = strlen(full_share); + + for(i = 0 ; i < full_share_len ; i++) + { + if(full_share[i] == '/') + full_share[i] = '\\'; + } + + StringToUpper(full_share); + + SHOWSTRING(full_share); + + memset (packet, 0, SMB_HEADER_LEN); + + smb_setup_header (server, SMBtconX, 4, password_len + full_share_len+1 + strlen(dev)+1); + + WSET (packet, smb_vwv0, 0xFF); + WSET (packet, smb_vwv3, password_len); + + p = SMB_BUF (packet); + + if(nt_password_len > 0) + memcpy(p,password,password_len); + else + memcpy (p, server->mount_data.password, password_len); + + p += password_len; + + memcpy(p,full_share,full_share_len+1); + p += full_share_len+1; + + strcpy(p,dev); + + BSET(packet,smb_rcls,1); + + if ((result = smb_request_ok (server, SMBtconX, 3, 0)) < 0) + { + SHOWVALUE(SMB_WCT(packet)); + + LOG (("smb_proc_connect: SMBtconX not verified.\n")); + goto fail; + } + + SHOWVALUE(SMB_WCT(packet)); + + /* Changed, max_xmit hasn't been updated if a tconX message was send instead of tcon. */ + if (server->maxxmt) + server->max_xmit = server->maxxmt; + + server->tid = WVAL(packet,smb_tid); + } + else + { + /* Fine! We have a connection, send a tcon message. */ + smb_setup_header (server, SMBtcon, 0, 6 + strlen (server->mount_data.service) + strlen (server->mount_data.password) + strlen (dev)); + + p = SMB_BUF (packet); + p = smb_encode_ascii (p, server->mount_data.service, strlen (server->mount_data.service)); + p = smb_encode_ascii (p, server->mount_data.password, strlen (server->mount_data.password)); + (void) smb_encode_ascii (p, dev, strlen (dev)); + + if ((result = smb_request_ok (server, SMBtcon, 2, 0)) < 0) + { + LOG (("smb_proc_connect: SMBtcon not verified.\n")); + goto fail; + } + + LOG (("OK! Managed to set up SMBtcon!\n")); + + p = SMB_VWV (packet); + p = smb_decode_word (p, &server->max_xmit); + + SHOWVALUE(server->max_xmit); + + /* Added by Brian Willette - We were ignoring the server's initial + maxbuf value */ + if (server->maxxmt != 0 && server->max_xmit > server->maxxmt) + server->max_xmit = server->maxxmt; + + (void) smb_decode_word (p, &server->tid); + } + + SHOWVALUE(server->max_xmit); + + /* Changed, max_xmit hasn't been updated if a tconX message was send instead of tcon. */ + if (server->max_xmit > given_max_xmit) + server->max_xmit = given_max_xmit; + + /* Ok, everything is fine. max_xmit does not include + the TCP-SMB header of 4 bytes. */ + if (server->max_xmit < 65535 - 4) + server->max_xmit += 4; + + LOG (("max_xmit = %ld, tid = %ld\n", server->max_xmit, server->tid)); + + LOG (("smb_proc_connect: Normal exit\n")); + + return 0; + + fail: + + server->state = CONN_INVALID; + + return result; +} + +/* smb_proc_reconnect: server->packet is allocated with + server->max_xmit bytes if and only if we return >= 0 */ +int +smb_proc_connect (struct smb_server *server) +{ + int result; + + result = smb_proc_reconnect (server); + + if ((result < 0) && (server->packet != NULL)) + { + free (server->packet); + server->packet = NULL; + } + + return result; +} + +/* error code stuff - put together by Merik Karman + merik@blackadder.dsh.oz.au */ +typedef struct +{ + char *name; + int code; + char *message; +} err_code_struct; + +/* Dos Error Messages */ +static const err_code_struct dos_msgs[] = +{ + {"ERRbadfunc", 1, "Invalid function"}, + {"ERRbadfile", 2, "File not found"}, + {"ERRbadpath", 3, "Directory invalid"}, + {"ERRnofids", 4, "No file descriptors available"}, + {"ERRnoaccess", 5, "Access denied"}, + {"ERRbadfid", 6, "Invalid file handle"}, + {"ERRbadmcb", 7, "Memory control blocks destroyed"}, + {"ERRnomem", 8, "Insufficient server memory to perform the requested function"}, + {"ERRbadmem", 9, "Invalid memory block address"}, + {"ERRbadenv", 10, "Invalid environment"}, + {"ERRbadformat", 11, "Invalid format"}, + {"ERRbadaccess", 12, "Invalid open mode"}, + {"ERRbaddata", 13, "Invalid data"}, + {"ERR", 14, "reserved"}, + {"ERRbaddrive", 15, "Invalid drive specified"}, + {"ERRremcd", 16, "A Delete Directory request attempted to remove the server's current directory"}, + {"ERRdiffdevice", 17, "Not same device"}, + {"ERRnofiles", 18, "A File Search command can find no more files matching the specified criteria"}, + {"ERRbadshare", 32, "The sharing mode specified for an Open conflicts with existing FIDs on the file"}, + {"ERRlock", 33, "A Lock request conflicted with an existing lock or specified an invalid mode, or an Unlock requested attempted to remove a lock held by another process"}, + {"ERRfilexists", 80, "The file named in a Create Directory, Make New File or Link request already exists"}, + {"ERRbadpipe", 230, "Pipe invalid"}, + {"ERRpipebusy", 231, "All instances of the requested pipe are busy"}, + {"ERRpipeclosing", 232, "Pipe close in progress"}, + {"ERRnotconnected", 233, "No process on other end of pipe"}, + {"ERRmoredata", 234, "There is more data to be returned"}, + + {NULL, -1, NULL} +}; + +/* Server Error Messages */ +static const err_code_struct server_msgs[] = +{ + {"ERRerror", 1, "Non-specific error code"}, + {"ERRbadpw", 2, "Bad password - name/password pair in a Tree Connect or Session Setup are invalid"}, + {"ERRbadtype", 3, "reserved"}, + {"ERRaccess", 4, "The requester does not have the necessary access rights within the specified context for the requested function. The context is defined by the TID or the UID"}, + {"ERRinvnid", 5, "The tree ID (TID) specified in a command was invalid"}, + {"ERRinvnetname", 6, "Invalid network name in tree connect"}, + {"ERRinvdevice", 7, "Invalid device - printer request made to non-printer connection or non-printer request made to printer connection"}, + {"ERRqfull", 49, "Print queue full (files) -- returned by open print file"}, + {"ERRqtoobig", 50, "Print queue full -- no space"}, + {"ERRqeof", 51, "EOF on print queue dump"}, + {"ERRinvpfid", 52, "Invalid print file FID"}, + {"ERRsmbcmd", 64, "The server did not recognize the command received"}, + {"ERRsrverror", 65, "The server encountered an internal error, e.g., system file unavailable"}, + {"ERRfilespecs", 67, "The file handle (FID) and pathname parameters contained an invalid combination of values"}, + {"ERRreserved", 68, "reserved"}, + {"ERRbadpermits", 69, "The access permissions specified for a file or directory are not a valid combination. The server cannot set the requested attribute"}, + {"ERRreserved", 70, "reserved"}, + {"ERRsetattrmode", 71, "The attribute mode in the Set File Attribute request is invalid"}, + {"ERRpaused", 81, "Server is paused"}, + {"ERRmsgoff", 82, "Not receiving messages"}, + {"ERRnoroom", 83, "No room to buffer message"}, + {"ERRrmuns", 87, "Too many remote user names"}, + {"ERRtimeout", 88, "Operation timed out"}, + {"ERRnoresource", 89, "No resources currently available for request"}, + {"ERRtoomanyuids", 90, "Too many UIDs active on this session"}, + {"ERRbaduid", 91, "The UID is not known as a valid ID on this session"}, + {"ERRusempx", 250, "Temp unable to support Raw, use MPX mode"}, + {"ERRusestd", 251, "Temp unable to support Raw, use standard read/write"}, + {"ERRcontmpx", 252, "Continue in MPX mode"}, + {"ERRreserved", 253, "reserved"}, + {"ERRreserved", 254, "reserved"}, + {"ERRnosupport", 0xFFFF, "Function not supported"}, + + {NULL, -1, NULL} +}; + +/* Hard Error Messages */ +static const err_code_struct hard_msgs[] = +{ + {"ERRnowrite", 19, "Attempt to write on write-protected diskette"}, + {"ERRbadunit", 20, "Unknown unit"}, + {"ERRnotready", 21, "Drive not ready"}, + {"ERRbadcmd", 22, "Unknown command"}, + {"ERRdata", 23, "Data error (CRC)"}, + {"ERRbadreq", 24, "Bad request structure length"}, + {"ERRseek", 25, "Seek error"}, + {"ERRbadmedia", 26, "Unknown media type"}, + {"ERRbadsector", 27, "Sector not found"}, + {"ERRnopaper", 28, "Printer out of paper"}, + {"ERRwrite", 29, "Write fault"}, + {"ERRread", 30, "Read fault"}, + {"ERRgeneral", 31, "General failure"}, + {"ERRbadshare", 32, "A open conflicts with an existing open"}, + {"ERRlock", 33, "A Lock request conflicted with an existing lock or specified an invalid mode, or an Unlock requested attempted to remove a lock held by another process"}, + {"ERRwrongdisk", 34, "The wrong disk was found in a drive"}, + {"ERRFCBUnavail", 35, "No FCBs are available to process request"}, + {"ERRsharebufexc", 36, "A sharing buffer has been exceeded"}, + + {NULL, -1, NULL} +}; + +typedef struct +{ + int code; + char *class; + const err_code_struct *err_msgs; +} err_class_struct; + +static const err_class_struct err_classes[] = +{ + { 0, "SUCCESS", NULL }, + { 0x01, "ERRDOS", dos_msgs }, + { 0x02, "ERRSRV", server_msgs }, + { 0x03, "ERRHRD", hard_msgs }, + { 0x04, "ERRXOS", NULL }, + { 0xE1, "ERRRMX1", NULL }, + { 0xE2, "ERRRMX2", NULL }, + { 0xE3, "ERRRMX3", NULL }, + { 0xFF, "ERRCMD", NULL }, + + { -1, NULL, NULL } +}; + +static void +smb_printerr (int class, int num) +{ + int i, j; + err_code_struct *err; + + for (i = 0; err_classes[i].class; i++) + { + if (err_classes[i].code != class) + continue; + + if (!err_classes[i].err_msgs) + { + ReportError("%s - %ld.", err_classes[i].class, num); + + LOG (("%s - %ld\n", err_classes[i].class, num)); + return; + } + + err = (err_code_struct *)err_classes[i].err_msgs; + + for (j = 0; err[j].name; j++) + { + if (num != err[j].code) + continue; + + ReportError ("%s - %s (%s).", err_classes[i].class, err[j].name, err[j].message); + + LOG (("%s - %s (%s)\n",err_classes[i].class, err[j].name,err[j].message)); + return; + } + } + + ReportError ("Unknown error - (%ld, %ld).", class, num); + + LOG (("Unknown error - (%ld, %ld)\n", class, num)); +} diff --git a/source_code/smakefile b/source_code/smakefile new file mode 100644 index 0000000..c8a1203 --- /dev/null +++ b/source_code/smakefile @@ -0,0 +1,118 @@ +# +# $Id: smakefile,v 1.1.1.1 2005-05-24 13:22:12 obarthel Exp $ +# +# :ts=8 +# +# 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. +# + +.c.o: + sc $(CFLAGS) $< + @ctags >tagfiles/$* $< + +.asm.o: + asm $(ASMFLAGS) $< + +############################################################################### + +PROJECT = smbfs + +############################################################################### + +############################################################################# +# +# Program version +# +############################################################################# + +VERSION = 1 + +############################################################################### + + OPTIMIZE = optimize opttime optinline optinlocal optschedule + CPU = any +#CPU = 060 + DEBUG = line +#DEBUG = symbolflush noopt define=DEBUG + +############################################################################### + +CFLAGS = idlen=64 comnest streq strmerge nostkchk \ + $(OPTIMIZE) cpu=$(CPU) debug=$(DEBUG) data=faronly \ + params=register idir=netinclude idir=include \ + gst=system_headers.gst + +LFLAGS = smallcode smalldata noicons batch + +############################################################################### + +OBJS = main.o proc.o smb_abstraction.o sock.o crypt.o + +############################################################################### + +LIBS = lib:scnb.lib lib:amiga.lib lib:debug.lib + +############################################################################### + +all: tagfiles system_headers.gst $(PROJECT) + +tagfiles: + makedir $@ + +$(PROJECT): $(OBJS) system_headers.gst Assert.o + slink $(OBJS) to $@.debug lib $(LIBS) Assert.o $(LFLAGS) \ + map $(PROJECT).map,fhx fwidth 32 pwidth 32 swidth 32 + slink $@.debug to $@ noicons nodebug + @type tagfiles/\#? >t:tags + @copy t:tags "" + @delete >nil: t:tags + +system_headers.gst: system_headers.h system_headers.c + gst unload $@ + sc $(CFLAGS) nodebug noobjname nogst makegst=$@ system_headers.c + +############################################################################### + +clean: + -delete \#?.o $(PROJECT)(%|.debug) + +realclean: clean + -delete system_headers.gst tags tagfiles \#?.map all + +############################################################################### + +mkid: + mkid -v \#?.(c|h|a|asm|i) include/smb/\#?.h \ + +# netinclude/\#?/\#?.h \ +# netinclude/\#?.h + +update: + mkid -v -u + +version: + bumprev $(VERSION) $(PROJECT) + +############################################################################### + +cvs-tag: + cvs -q tag V$(VERSION)_`type $(PROJECT)_rev.rev` + +cvs-update: + cvs update diff --git a/source_code/smb_abstraction.c b/source_code/smb_abstraction.c new file mode 100644 index 0000000..e32c57e --- /dev/null +++ b/source_code/smb_abstraction.c @@ -0,0 +1,1488 @@ +/* + * $Id: smb_abstraction.c,v 1.1.1.1 2005-05-24 13:22:13 obarthel Exp $ + * + * :ts=8 + * + * Name: smb_abstraction.c + * Description: Smb abstraction layer. + * Author: Christian Starkjohann + * Date: 1996-12-31 + * Copyright: GNU-GPL + * + * Modified for use with AmigaOS by Olaf Barthel + */ + +#include "smbfs.h" + +/*****************************************************************************/ + +#include +#include +#include + +/*****************************************************************************/ + +#define ATTR_CACHE_TIME 5 /* cache attributes for this time */ +#define DIR_CACHE_TIME 5 /* cache directories for this time */ +#define DIRCACHE_SIZE 170 +#define DOS_PATHSEP '\\' + +/*****************************************************************************/ + +typedef struct dircache +{ + int base; + int len; + int eof; /* cache end is eof */ + long created_at; /* for invalidation */ + struct smba_file *cache_for; /* owner of this cache */ + int cache_size; + struct smb_dirent cache[1]; +} dircache_t; + +/* opaque structures for server and files: */ +struct smba_server +{ + struct smb_server server; + struct MinList open_files; + dircache_t * dircache; + unsigned supports_E:1; + unsigned supports_E_known:1; +}; + +struct smba_file +{ + struct MinNode node; + struct smba_server *server; + struct smb_dirent dirent; + long attr_time; /* time when dirent was read */ + dircache_t *dircache; /* content cache for directories */ + unsigned attr_dirty:1; /* attribute cache is dirty */ + unsigned is_valid:1; /* server was down, entry removed, ... */ +}; + +/*****************************************************************************/ + +#include "smb_abstraction.h" + +/*****************************************************************************/ + +static int smba_connect(smba_connect_parameters_t *p, unsigned int ip_addr, int use_E, char *workgroup_name, int cache_size, smba_server_t **result); +static INLINE int make_open(smba_file_t *f, int need_fid); +static int write_attr(smba_file_t *f); +static void invalidate_dircache(struct smba_server *server, char *path); +static void close_path(smba_server_t *s, char *path); +static void smba_cleanup_dircache(struct smba_server *server); +static int smba_setup_dircache(struct smba_server *server, int cache_size); +static int extract_service (char *service, char *server, size_t server_size, char *share, size_t share_size); + +/*****************************************************************************/ + +static int +smba_connect (smba_connect_parameters_t * p, unsigned int ip_addr, int use_E, char * workgroup_name, int cache_size, smba_server_t ** result) +{ + smba_server_t *res; + struct smb_mount_data data; + int errnum; + char hostname[MAXHOSTNAMELEN], *s; + struct servent * servent; + + (*result) = NULL; + + res = malloc (sizeof(*res)); + if(res == NULL) + { + ReportError("Not enough memory."); + errnum = -ENOMEM; + goto error_occured; + } + + memset (res, 0, sizeof(*res)); + memset (&data, 0, sizeof (data)); + memset (hostname, 0, sizeof (hostname)); + + errnum = smba_setup_dircache (res,cache_size); + if(errnum < 0) + { + ReportError("Directory cache initialization failed (%ld, %s).",-errnum,amitcp_strerror(-errnum)); + goto error_occured; + } + + strlcpy(data.workgroup_name,workgroup_name,sizeof(data.workgroup_name)); + + res->server.abstraction = res; + + gethostname (hostname, MAXHOSTNAMELEN); + + if ((s = strchr (hostname, '.')) != NULL) + (*s) = '\0'; + + data.addr.sin_family = AF_INET; + data.addr.sin_addr.s_addr = ip_addr; + + servent = getservbyname("netbios-ssn","tcp"); + if(servent != NULL) + data.addr.sin_port = htons (servent->s_port); + else + data.addr.sin_port = htons (SMB_PORT); + + data.fd = socket (AF_INET, SOCK_STREAM, 0); + if (data.fd < 0) + { + errnum = (-errno); + ReportError("socket() call failed (%ld, %s).", errno, amitcp_strerror (errno)); + goto error_occured; + } + + strlcpy (data.service, p->service, sizeof(data.service)); + StringToUpper (data.service); + strlcpy (data.username, p->username, sizeof(data.username)); + strlcpy (data.password, p->password, sizeof(data.password)); + + if (p->max_xmit > 0) + data.max_xmit = p->max_xmit; + else + data.max_xmit = 65530 /*8300*/; + + strlcpy (data.server_name, p->server_name, sizeof(data.server_name)); + strlcpy (data.client_name, p->client_name, sizeof(data.client_name)); + + if (data.server_name[0] == '\0') + { + if (strlen (p->server_ipname) > 16) + { + errnum = -ENAMETOOLONG; + ReportError("Server name '%s' is too long for NetBIOS (max %ld characters).",p->server_ipname,16); + goto error_occured; + } + + strlcpy (data.server_name, p->server_ipname, sizeof(data.server_name)); + } + + StringToUpper (data.server_name); + + if (data.client_name[0] == '\0') + { + if (strlen (hostname) > 16) + { + errnum = -ENAMETOOLONG; + ReportError("Local host name '%s' is too long for NetBIOS (max %ld characters).", hostname, 16); + goto error_occured; + } + + strlcpy (data.client_name, hostname, sizeof(data.client_name)); + StringToUpper (data.client_name); + } + + res->server.mount_data = data; + + NewList((struct List *)&res->open_files); + + if ((errnum = smb_proc_connect (&res->server)) < 0) + { + ReportError("Cannot connect to server (%ld, %s).", -errnum,amitcp_strerror(-errnum)); + goto error_occured; + } + + if (!use_E) + res->supports_E_known = 1; + + (*result) = res; + + return 0; + + error_occured: + + if(res != NULL) + { + smba_cleanup_dircache (res); + free (res); + } + + return errnum; +} + +/*****************************************************************************/ + +void +smba_disconnect (smba_server_t * server) +{ + CloseSocket (server->server.mount_data.fd); + + smba_cleanup_dircache(server); + + free (server); +} + +/*****************************************************************************/ + +static INLINE int +make_open (smba_file_t * f, int need_fid) +{ + int errnum = 0; + smba_server_t *s; + + if (!f->is_valid || (need_fid && !f->dirent.opened)) + { + s = f->server; + + if (!f->is_valid || f->attr_time == -1 || GetCurrentTime() - f->attr_time > ATTR_CACHE_TIME) + { + if ((errnum = smb_proc_getattr_core (&s->server, f->dirent.complete_path, f->dirent.len, &f->dirent)) < 0) + goto error_occured; + } + + if ((f->dirent.attr & aDIR) == 0) /* a regular file */ + { + if (need_fid || !s->supports_E_known || s->supports_E) + { + LOG (("opening file %s\n", f->dirent.complete_path)); + if ((errnum = smb_proc_open (&s->server, f->dirent.complete_path, f->dirent.len, &f->dirent)) < 0) + goto error_occured; + + if (s->supports_E || !s->supports_E_known) + { + if (smb_proc_getattrE (&s->server, &f->dirent) < 0) + { + if (!s->supports_E_known) + { + s->supports_E_known = 1; + s->supports_E = 0; + } /* ignore errors here */ + } + else + { + s->supports_E_known = 1; + s->supports_E = 1; + } + } + } + } + else + { + /* don't open directory, initialize directory cache */ + if (f->dircache != NULL) + { + f->dircache->cache_for = NULL; + f->dircache->len = 0; + f->dircache = NULL; + } + } + + f->attr_time = GetCurrentTime(); + f->is_valid = 1; + } + + error_occured: + + return errnum; +} + +/*****************************************************************************/ + +int +smba_open (smba_server_t * s, char *name, size_t name_size, smba_file_t ** file) +{ + smba_file_t *f; + int errnum; + + (*file) = NULL; + + f = malloc (sizeof(*f)); + if(f == NULL) + { + errnum = -ENOMEM; + goto error_occured; + } + + memset(f,0,sizeof(*f)); + + f->dirent.complete_path = name; + f->dirent.complete_path_size = name_size; + f->dirent.len = strlen (name); + f->server = s; + + errnum = make_open (f, 0); + if (errnum < 0) + goto error_occured; + + AddTail ((struct List *)&s->open_files, (struct Node *)f); + + (*file) = f; + + return 0; + + error_occured: + + if (f != NULL) + free (f); + + return errnum; +} + +/*****************************************************************************/ + +static int +write_attr (smba_file_t * f) +{ + int errnum; + + LOG (("file %s\n", f->dirent.complete_path)); + + errnum = make_open (f, 0); + if (errnum < 0) + goto out; + + if (f->dirent.opened && f->server->supports_E) + errnum = smb_proc_setattrE (&f->server->server, f->dirent.fileid, &f->dirent); + else + errnum = smb_proc_setattr_core (&f->server->server, f->dirent.complete_path, f->dirent.len, &f->dirent); + + if (errnum < 0) + f->attr_time = -1; + else + f->attr_dirty = 0; + + out: + + return errnum; +} + +/*****************************************************************************/ + +void +smba_close (smba_file_t * f) +{ + if(f->node.mln_Succ != NULL || f->node.mln_Pred != NULL) + Remove((struct Node *)f); + + if(f->attr_dirty) + write_attr(f); + + if (f->dirent.opened) + { + LOG (("closing file %s\n", f->dirent.complete_path)); + smb_proc_close (&f->server->server, f->dirent.fileid, f->dirent.mtime); + } + + if (f->dircache != NULL) + { + f->dircache->cache_for = NULL; + f->dircache->len = 0; + f->dircache = NULL; + } + + free (f); +} + +/*****************************************************************************/ + +int +smba_read (smba_file_t * f, char *data, long len, long offset) +{ + int maxsize, count, totalcount, result; + int bytes_read = 0; + char *rpos; + + result = make_open (f, 1); + if (result < 0) + goto out; + + D(("read %ld bytes from offset %ld",len,offset)); + + if (f->server->server.blkmode & 1) + { + SHOWVALUE(f->server->server.max_xmit); + + if(len <= 65535) + { + result = smb_proc_read_raw (&f->server->server, &f->dirent, offset, len, data); + + LOG (("smb_proc_read_raw(%s)->%ld\n", f->dirent.complete_path, result)); + } + else if (len > 0) + { + int n; + + totalcount = 0; + + do + { + n = min(len,65535); + + result = smb_proc_read_raw (&f->server->server, &f->dirent, offset, n, data); + if(result <= 0) + { + D(("!!! wanted to read %ld bytes, got %ld",n,result)); + totalcount = -1; + break; + } + + data += result; + offset += result; + len -= result; + totalcount += result; + + if(result < n) + { + D(("read returned fewer characters than expected (%ld < %ld)",result,n)); + break; + } + } + while(len > 0); + + if(totalcount != -1) + { + result = totalcount; + goto out; + } + } + } + + if (result <= 0) + { + totalcount = len; + rpos = data; + + maxsize = f->server->server.max_xmit - SMB_HEADER_LEN - 5 * 2 - 5; + + do + { + count = totalcount > maxsize ? maxsize : totalcount; + + result = smb_proc_read (&f->server->server, &f->dirent, offset, count, rpos, 0); + if (result <= 0) + break; + + bytes_read += result; + totalcount -= result; + offset += result; + rpos += result; + + if(result < count) + { + D(("read returned fewer characters than expected (%ld < %ld)",result,count)); + break; + } + } + while (totalcount > 0); + + if(result >= 0) + result = bytes_read; + } + + out: + + return result; +} + +/*****************************************************************************/ + +int +smba_write (smba_file_t * f, char *data, long len, long offset) +{ + int newlen, maxsize, totalcount, count, result; + + if ((result = make_open (f, 1)) < 0) + return result; + + /* Calculate maximum number of bytes that could be tranfered with + a single SMBwrite packet... */ + maxsize = f->server->server.max_xmit - (SMB_HEADER_LEN + 5 * sizeof (word) + 5) - 4; + + if (len <= maxsize) + { + /* Use a single SMBwrite packet whenever possible instead of a SMBwritebraw + because that requires two packets to be send. */ + result = smb_proc_write (&f->server->server, &f->dirent, offset, len, data); + } + else + { + /* Added by Brian Willette - We were always checking bit 2 here, but + according to the documentation I have, the newer versions of SMB put + the SMB_RAW_WRITE AND SMB_RAW_READ capability in bit 1 */ + if ((f->server->server.protocol >= PROTOCOL_NT1 + && f->server->server.blkmode & 1) + || (f->server->server.protocol < PROTOCOL_NT1 + && f->server->server.blkmode & 2)) + { + long maxxmit; + int n; + + totalcount = 0; + + /* Try to send the maximum number of bytes with the two SMBwritebraw packets. */ + maxxmit = 2 * f->server->server.max_xmit - (SMB_HEADER_LEN + 12 * sizeof (word) + 4) - 8; + + /* If the number of bytes that should be transfered exceed the number of + bytes that could be transfered by a single call to smb_proc_write_raw, + the data is transfered as before: Only by the second packet, this + prevents the CPU from copying the data into the transferbuffer. */ + if (maxxmit < len) + maxxmit = f->server->server.max_xmit - 4; + + do + { + n = min(len,maxxmit); + + if (n <= maxsize) + { + /* Use a single SMBwrite packet whenever possible instead of a + SMBwritebraw because that requires two packets to be send. */ + result = smb_proc_write (&f->server->server, &f->dirent, offset, n, data); + } + else + { + result = smb_proc_write_raw (&f->server->server, &f->dirent, offset, n, data); + } + + if(result <= 0) + { + totalcount = -1; + break; + } + + data += result; + offset += result; + len -= result; + totalcount += result; + + if(result < n) + break; + } + while(len > 0); + + if(totalcount != -1) + { + len = totalcount; + goto out; + } + } + if (result <= 0) + { + /* Failed to use the SMBwritebraw packet, fallback to SMBwrite... */ + totalcount = len; + len = 0; + + do + { + count = totalcount > maxsize ? maxsize : totalcount; + result = smb_proc_write (&f->server->server, &f->dirent, offset, count, data); + if (result < 0) + break; + + totalcount -= result; + offset += result; + data += result; + len += result; + + if(result < count) + break; + } + while (totalcount > 0); + } + } + + out: + + if (result < 0) + { + f->attr_time = -1; + return result; + } + + f->dirent.mtime = GetCurrentTime(); + + newlen = f->dirent.size; + + if (offset + len > newlen) + newlen = offset + len; + + f->dirent.size = newlen; + return len; +} + +/*****************************************************************************/ + +long +smba_seek (smba_file_t *f, long offset, long mode, off_t * new_position_ptr) +{ + long result; + + D(("seek %ld bytes from position %s",offset,mode > 0 ? (mode == 2 ? "SEEK_END" : "SEEK_CUR") : "SEEK_SET")); + + if ((result = make_open (f, 1)) >= 0) + result = smb_proc_lseek (&f->server->server, &f->dirent, offset, mode, new_position_ptr); + + return result; +} + +/*****************************************************************************/ + +/* perform a single record-lock */ +int +smba_lockrec (smba_file_t *f, long offset, long len, long mode, int unlocked, long timeout) +{ + int errnum; + struct smb_lkrng *rec_lock; + + if ((errnum = make_open (f, 1)) >= 0) + { + if (unlocked) + mode |= 2; + + rec_lock = malloc (sizeof (struct smb_lkrng)); + if (rec_lock != NULL) + { + rec_lock->offset = offset, + rec_lock->len = len; + + errnum = smb_proc_lockingX (&f->server->server, &f->dirent, rec_lock, 1, mode, timeout); + + free (rec_lock); + } + else + { + errnum = -ENOMEM; + } + } + + return errnum; +} + +/*****************************************************************************/ + +int +smba_getattr (smba_file_t * f, smba_stat_t * data) +{ + long now = GetCurrentTime(); + int errnum; + + errnum = make_open (f, 0); + if (errnum < 0) + goto out; + + if (f->attr_time == -1 || (now - f->attr_time) > ATTR_CACHE_TIME) + { + LOG (("file %s\n", f->dirent.complete_path)); + + if (f->dirent.opened && f->server->supports_E) + errnum = smb_proc_getattrE (&f->server->server, &f->dirent); + else + errnum = smb_proc_getattr_core (&f->server->server, f->dirent.complete_path, f->dirent.len, &f->dirent); + + if (errnum >= 0) + f->attr_time = now; + } + + data->is_dir = (f->dirent.attr & aDIR) != 0; + data->is_wp = (f->dirent.attr & aRONLY) != 0; + data->is_hidden = (f->dirent.attr & aHIDDEN) != 0; + data->is_system = (f->dirent.attr & aSYSTEM) != 0; + data->is_archive = (f->dirent.attr & aARCH) != 0; + + data->size = f->dirent.size; + data->atime = f->dirent.atime; + data->ctime = f->dirent.ctime; + data->mtime = f->dirent.mtime; + + out: + + return errnum; +} + +/*****************************************************************************/ + +int +smba_setattr (smba_file_t * f, smba_stat_t * data) +{ + int errnum; + + if (data->atime != -1) + f->dirent.atime = data->atime; + + if (data->ctime != -1) + f->dirent.ctime = data->ctime; + + if (data->mtime != -1) + f->dirent.mtime = data->mtime; + + if (data->is_wp) + f->dirent.attr |= aRONLY; + else + f->dirent.attr &= ~aRONLY; + + if (data->is_archive) + f->dirent.attr |= aARCH; + else + f->dirent.attr &= ~aARCH; + + if (data->is_system) + f->dirent.attr |= aSYSTEM; + else + f->dirent.attr &= ~aSYSTEM; + + f->attr_dirty = 1; + + if ((errnum = write_attr (f)) < 0) + goto error_occured; + + if (data->size != -1 && + data->size != (int)f->dirent.size) + { + if ((errnum = make_open (f, 1)) < 0) + goto error_occured; + + if ((errnum = smb_proc_trunc (&f->server->server, f->dirent.fileid, data->size)) < 0) + goto error_occured; + + f->dirent.size = data->size; + } + + error_occured: + + return errnum; +} + +/*****************************************************************************/ + +int +smba_readdir (smba_file_t * f, long offs, void *d, smba_callback_t callback) +{ + int cache_index, rval, o, eof, count = 0; + long now = GetCurrentTime(); + smba_stat_t data; + + rval = make_open (f, 0); + if (rval < 0) + goto out; + + if (f->dircache == NULL) /* get a cache */ + { + dircache_t * dircache = f->server->dircache; + + if (dircache->cache_for != NULL) + dircache->cache_for->dircache = NULL; /* steal it */ + + dircache->eof = dircache->len = dircache->base = 0; + dircache->cache_for = f; + + f->dircache = dircache; + + LOG (("stealing cache\n")); + } + else + { + if ((now - f->dircache->created_at) >= DIR_CACHE_TIME) + { + f->dircache->eof = f->dircache->len = f->dircache->base = 0; + + LOG (("cache outdated\n")); + } + } + + for (cache_index = offs ; ; cache_index++) + { + if (cache_index < f->dircache->base /* fill cache if necessary */ + || cache_index >= f->dircache->base + f->dircache->len) + { + if (cache_index >= f->dircache->base + f->dircache->len && f->dircache->eof) + break; /* nothing more to read */ + + LOG (("cachefill for %s\n", f->dirent.complete_path)); + LOG (("\tbase was: %ld, len was: %ld, newbase=%ld\n", f->dircache->base, f->dircache->len, cache_index)); + + f->dircache->len = 0; + f->dircache->base = cache_index; + + rval = smb_proc_readdir (&f->server->server, f->dirent.complete_path, cache_index, f->dircache->cache_size, f->dircache->cache); + if (rval <= 0) + break; + + /* Avoid some hits if restart/retry occured. Should fix the real root + of this problem really, but I am not bored enough atm. -Piru + ZZZ this needs further investigation. */ + if (f->dircache == NULL) + { + LOG (("lost dircache due to an error, bailing out!\n")); + rval = -1; + break; + } + + f->dircache->len = rval; + f->dircache->eof = (rval < f->dircache->cache_size); + f->dircache->created_at = now; + + LOG (("cachefill with %ld entries\n", rval)); + } + + o = cache_index - f->dircache->base; + eof = (o >= (f->dircache->len - 1) && f->dircache->eof); + count++; + + LOG (("delivering '%s', cache_index=%ld, eof=%ld\n", f->dircache->cache[o].complete_path, cache_index, eof)); + + data.is_dir = (f->dircache->cache[o].attr & aDIR) != 0; + data.is_wp = (f->dircache->cache[o].attr & aRONLY) != 0; + data.is_hidden = (f->dircache->cache[o].attr & aHIDDEN) != 0; + data.is_system = (f->dircache->cache[o].attr & aSYSTEM) != 0; + data.is_archive = (f->dircache->cache[o].attr & aARCH) != 0; + data.size = f->dircache->cache[o].size; + data.atime = f->dircache->cache[o].atime; + data.ctime = f->dircache->cache[o].ctime; + data.mtime = f->dircache->cache[o].mtime; + + if ((*callback) (d, cache_index, cache_index + 1, f->dircache->cache[o].complete_path, eof, &data)) + break; + } + + out: + + if (rval < 0) + return rval; + else + return count; +} + +/*****************************************************************************/ + +static void +invalidate_dircache (struct smba_server * server, char * path) +{ + dircache_t * dircache = server->dircache; + char other_path[SMB_MAXNAMELEN + 1]; + + ENTER(); + + if(path != NULL) + { + int len,i; + + strlcpy(other_path,path,sizeof(other_path)); + + len = strlen(other_path); + for(i = len-1 ; i >= 0 ; i--) + { + if(i == 0) + { + other_path[0] = DOS_PATHSEP; + other_path[1] = '\0'; + } + else if (other_path[i] == DOS_PATHSEP) + { + other_path[i] = '\0'; + break; + } + } + } + else + { + other_path[0] = '\0'; + } + + SHOWSTRING(other_path); + + if(dircache->cache_for != NULL) + SHOWSTRING(dircache->cache_for->dirent.complete_path); + else + SHOWMSG("-- directory cache is empty --"); + + if(path == NULL || (dircache->cache_for != NULL && CompareNames(other_path,dircache->cache_for->dirent.complete_path) == SAME)) + { + SHOWMSG("clearing directory cache"); + + dircache->eof = dircache->len = dircache->base = 0; + if(dircache->cache_for != NULL) + { + dircache->cache_for->dircache = NULL; + dircache->cache_for = NULL; + } + } + + LEAVE(); +} + +/*****************************************************************************/ + +int +smba_create (smba_file_t * dir, const char *name, smba_stat_t * attr) +{ + struct smb_dirent entry; + char *path; + int errnum; + + errnum = make_open (dir, 0); + if (errnum < 0) + goto out; + + memset (&entry, 0, sizeof (entry)); + + if (attr->is_wp) + entry.attr |= aRONLY; + + if (attr->is_archive) + entry.attr |= aARCH; + + if (attr->is_system) + entry.attr |= aSYSTEM; + + entry.atime = entry.mtime = entry.ctime = GetCurrentTime(); + + path = malloc (strlen (name) + dir->dirent.len + 2); + if(path == NULL) + { + errnum = -ENOMEM; + goto out; + } + + memcpy (path, dir->dirent.complete_path, dir->dirent.len); + path[dir->dirent.len] = DOS_PATHSEP; + strcpy (&path[dir->dirent.len + 1], name); + + errnum = smb_proc_create (&dir->server->server, path, strlen (path), &entry); + if(errnum >= 0) + invalidate_dircache (dir->server, path); + + free (path); + + out: + + return errnum; +} + +/*****************************************************************************/ + +int +smba_mkdir (smba_file_t * dir, const char *name) +{ + char *path; + int errnum; + + errnum = make_open (dir, 0); + if (errnum < 0) + goto out; + + path = malloc (strlen (name) + dir->dirent.len + 2); + if(path == NULL) + { + errnum = -ENOMEM; + goto out; + } + + memcpy (path, dir->dirent.complete_path, dir->dirent.len); + path[dir->dirent.len] = DOS_PATHSEP; + strcpy (&path[dir->dirent.len + 1], name); + + errnum = smb_proc_mkdir (&dir->server->server, path, strlen (path)); + if(errnum >= 0) + invalidate_dircache (dir->server, path); + + free (path); + + out: + + return errnum; +} + +/*****************************************************************************/ + +static void +close_path (smba_server_t * s, char *path) +{ + smba_file_t *p; + + for (p = (smba_file_t *)s->open_files.mlh_Head; + p->node.mln_Succ != NULL; + p = (smba_file_t *)p->node.mln_Succ) + { + if (p->is_valid && CompareNames(p->dirent.complete_path, path) == SAME) + { + if (p->dirent.opened) + smb_proc_close (&s->server, p->dirent.fileid, p->dirent.mtime); + + p->dirent.opened = 0; + p->is_valid = 0; + } + } +} + +/*****************************************************************************/ + +int +smba_remove (smba_server_t * s, char *path) +{ + int result; + + close_path (s, path); + + result = smb_proc_unlink (&s->server, path, strlen (path)); + if(result >= 0) + invalidate_dircache (s, path); + + return result; +} + +/*****************************************************************************/ + +int +smba_rmdir (smba_server_t * s, char *path) +{ + int result; + + close_path (s, path); + + result = smb_proc_rmdir (&s->server, path, strlen (path)); + if(result >= 0) + invalidate_dircache (s, path); + + return result; +} + +/*****************************************************************************/ + +int +smba_rename (smba_server_t * s, char *from, char *to) +{ + int result; + + close_path (s, from); + + result = smb_proc_mv (&s->server, from, strlen (from), to, strlen (to)); + if(result >= 0) + invalidate_dircache (s, from); + + return result; +} + +/*****************************************************************************/ + +int +smba_statfs (smba_server_t * s, long *bsize, long *blocks, long *bfree) +{ + struct smb_dskattr dskattr; + int errnum; + + errnum = smb_proc_dskattr (&s->server, &dskattr); + if (errnum < 0) + goto out; + + (*bsize) = dskattr.blocksize * dskattr.allocblocks; + (*blocks) = dskattr.total; + (*bfree) = dskattr.free; + + out: + + return errnum; +} + +/*****************************************************************************/ + +void +smb_invalidate_all_inodes (struct smb_server *server) +{ + smba_file_t *f; + + invalidate_dircache (server->abstraction, NULL); + + for (f = (smba_file_t *)server->abstraction->open_files.mlh_Head; + f->node.mln_Succ != NULL; + f = (smba_file_t *)f->node.mln_Succ) + { + f->dirent.opened = 0; + f->is_valid = 0; + } +} + +/*****************************************************************************/ + +static void +smba_cleanup_dircache(struct smba_server * server) +{ + dircache_t * the_dircache = server->dircache; + + if(the_dircache != NULL) + { + int i; + + for (i = 0; i < the_dircache->cache_size; i++) + { + if(the_dircache->cache[i].complete_path != NULL) + free(the_dircache->cache[i].complete_path); + } + + free(the_dircache); + server->dircache = NULL; + } +} + +static int +smba_setup_dircache (struct smba_server * server,int cache_size) +{ + dircache_t * the_dircache; + int error = (-ENOMEM); + int i; + + the_dircache = malloc(sizeof(*the_dircache) + (cache_size-1) * sizeof(the_dircache->cache)); + if(the_dircache == NULL) + goto out; + + memset(the_dircache,0,sizeof(*the_dircache)); + the_dircache->cache_size = cache_size; + + for (i = 0; i < the_dircache->cache_size; i++) + { + the_dircache->cache[i].complete_path = malloc (SMB_MAXNAMELEN + 1); + if(the_dircache->cache[i].complete_path == NULL) + goto out; + + the_dircache->cache[i].complete_path_size = SMB_MAXNAMELEN + 1; + } + + server->dircache = the_dircache; + + error = 0; + + out: + + if(error < 0) + { + if(the_dircache != NULL) + { + for (i = 0; i < the_dircache->cache_size; i++) + { + if(the_dircache->cache[i].complete_path != NULL) + free(the_dircache->cache[i].complete_path); + } + + free(the_dircache); + } + } + + return(error); +} + +/*****************************************************************************/ + +static int +extract_service (char *service, char *server, size_t server_size, char *share, size_t share_size) +{ + char *share_start; + char *root_start; + char *complete_service; + char * service_copy; + int result = 0; + + service_copy = malloc(strlen(service)+1); + if(service_copy == NULL) + { + result = -ENOMEM; + ReportError("Not enough memory."); + goto out; + } + + strcpy (service_copy, service); + complete_service = service_copy; + + if (strlen (complete_service) < 4 || complete_service[0] != '/') + { + result = -EINVAL; + ReportError("Invalid service name '%s'.",complete_service); + goto out; + } + + while (complete_service[0] == '/') + complete_service += 1; + + share_start = strchr (complete_service, '/'); + if (share_start == NULL) + { + result = -EINVAL; + ReportError("Invalid share name '%s'.",complete_service); + goto out; + } + + (*share_start++) = '\0'; + root_start = strchr (share_start, '/'); + + if (root_start != NULL) + (*root_start) = '\0'; + + if ((strlen (complete_service) > 63) || (strlen (share_start) > 63)) + { + result = -ENAMETOOLONG; + ReportError("Server or share name is too long in '%s' (max %ld characters).",service,63); + goto out; + } + + strlcpy (server, complete_service, server_size); + strlcpy (share, share_start, share_size); + + out: + + if(service_copy != NULL) + free(service_copy); + + return(result); +} + +int +smba_start(char * service,char *opt_workgroup,char *opt_username,char *opt_password,char *opt_clientname,char *opt_servername,int opt_cachesize,smba_server_t ** result) +{ + smba_connect_parameters_t par; + smba_server_t *the_server = NULL; + int i; + struct hostent *h; + int max_xmit = -1; + int use_extended = 0; + char server_name[17], client_name[17]; + char username[64], password[64]; + char workgroup[20]; + char server[64], share[64]; + unsigned int ipAddr; + int error; + + (*result) = NULL; + (*username) = (*password) = (*server_name) = (*client_name) = '\0'; + + error = extract_service (service, server, sizeof(server), share, sizeof(share)); + if(error < 0) + goto out; + + ipAddr = inet_addr (server); + if (ipAddr == INADDR_NONE) /* name was given, not numeric */ + { + int lookup_error; + + h = gethostbyname (server); + lookup_error = h_errno; + + if (h != NULL) + { + ipAddr = ((struct in_addr *)(h->h_addr))->s_addr; + } + else if (BroadcastNameQuery(server,"",(UBYTE *)&ipAddr) != 0) + { + ReportError("Unknown host '%s' (%ld, %s).",server,lookup_error,host_strerror(lookup_error)); + error = (-ENOENT); + goto out; + } + } + else + { + char hostName[MAXHOSTNAMELEN+1]; + + h = gethostbyaddr ((char *) &ipAddr, sizeof (ipAddr), AF_INET); + if (h == NULL) + { + ReportError("Unknown host '%s' (%ld, %s).",server,h_errno,host_strerror(errno)); + error = (-ENOENT); + goto out; + } + + /* Brian Willette: Now we will set the server name to the dns + hostname, hopefully this will be the same as the netbios name for + the server. + We do this because the user supplied no hostname, and we + need one for netbios, this is the best guess choice we have + NOTE: If the names are different between DNS and netbios on + the windows side, the user MUST use the -s option. */ + for (i = 0; h->h_name[i] != '.' && h->h_name[i] != '\0' && i < 255; i++) + hostName[i] = h->h_name[i]; + + hostName[i] = '\0'; + + /* Make sure the hostname is 16 characters or less (for Netbios) */ + if (strlen (hostName) > 16) + { + ReportError("Server host name '%s' is too long (max %ld characters).", hostName, 16); + error = (-ENAMETOOLONG); + goto out; + } + + strlcpy (server_name, hostName, sizeof(server_name)); + } + + if(opt_password != NULL) + { + if(strlen(opt_password) >= sizeof(password)) + { + ReportError("Password is too long (max %ld characters).", sizeof(password)-1); + error = (-ENAMETOOLONG); + goto out; + } + + strlcpy(password,opt_password,sizeof(password)); + } + + if(strlen(opt_username) >= sizeof(username)) + { + ReportError("User name '%s' is too long (max %ld characters).", username,sizeof(username)-1); + error = (-ENAMETOOLONG); + goto out; + } + + strlcpy(username,opt_username,sizeof(username)); + StringToUpper(username); + + if (strlen(opt_workgroup) > 15) + { + ReportError("Workgroup/domain name '%s' is too long (max %ld characters).", opt_workgroup,15); + error = (-ENAMETOOLONG); + goto out; + } + + strlcpy (workgroup, opt_workgroup, sizeof(workgroup)); + StringToUpper (workgroup); + + if(opt_servername != NULL) + { + if (strlen (opt_servername) > 16) + { + ReportError("Server name '%s' is too long (max %ld characters).", opt_servername,16); + error = (-ENAMETOOLONG); + goto out; + } + + strlcpy (server_name, opt_servername, sizeof(server_name)); + } + + if(opt_clientname != NULL) + { + if (strlen (opt_clientname) > 16) + { + ReportError("Client name '%s' is too long (max %ld characters).", opt_clientname,16); + error = (-ENAMETOOLONG); + goto out; + } + + strlcpy (client_name, opt_clientname, sizeof(client_name)); + } + + if(opt_cachesize < 1) + opt_cachesize = DIRCACHE_SIZE; + else if (opt_cachesize < 10) + opt_cachesize = 10; + + strlcpy(par.server_ipname,server,sizeof(par.server_ipname)); + par.server_name = server_name; + par.client_name = client_name; + + strlcpy(par.service,share,sizeof(par.service)); + par.username = username; + par.password = password; + par.max_xmit = max_xmit; + + error = smba_connect (&par, ipAddr, use_extended, workgroup, opt_cachesize, &the_server); + if(error < 0) + { + ReportError("Could not connect to server (%ld, %s).",-error,amitcp_strerror(-error)); + goto out; + } + + (*result) = the_server; + + error = 0; + + out: + + return(error); +} + +/*****************************************************************************/ + +int +smba_get_dircache_size(struct smba_server * server) +{ + int result; + + result = server->dircache->cache_size; + + return(result); +} + +/*****************************************************************************/ + +int +smba_change_dircache_size(struct smba_server * server,int cache_size) +{ + dircache_t * new_cache; + dircache_t * the_dircache = server->dircache; + int result; + int i; + + result = the_dircache->cache_size; + + if(cache_size < 10) + cache_size = 10; + + if(cache_size == the_dircache->cache_size) + goto out; + + new_cache = malloc(sizeof(*new_cache) + (cache_size-1) * sizeof(new_cache->cache)); + if(new_cache == NULL) + goto out; + + memset(new_cache,0,sizeof(*new_cache)); + new_cache->cache_size = cache_size; + + /* If the new cache is to be larger than the old one, + allocate additional file name slots. */ + if(cache_size > the_dircache->cache_size) + { + for(i = the_dircache->cache_size ; i < cache_size ; i++) + { + new_cache->cache[i].complete_path = malloc (SMB_MAXNAMELEN + 1); + if(new_cache->cache[i].complete_path == NULL) + { + int j; + + for(j = the_dircache->cache_size ; j < i ; j++) + free(new_cache->cache[j].complete_path); + + free(new_cache); + + goto out; + } + + new_cache->cache[i].complete_path_size = SMB_MAXNAMELEN + 1; + } + + /* Reuse the file name buffers allocated for + the old cache. */ + for(i = 0 ; i < the_dircache->cache_size ; i++) + { + new_cache->cache[i].complete_path = the_dircache->cache[i].complete_path; + new_cache->cache[i].complete_path_size = the_dircache->cache[i].complete_path_size; + + the_dircache->cache[i].complete_path = NULL; + } + } + else + { + /* Reuse the file name buffers allocated for + the old cache. */ + for(i = 0 ; i < cache_size ; i++) + { + new_cache->cache[i].complete_path = the_dircache->cache[i].complete_path; + new_cache->cache[i].complete_path_size = the_dircache->cache[i].complete_path_size; + the_dircache->cache[i].complete_path = NULL; + } + } + + invalidate_dircache(server, NULL); + + for (i = 0; i < the_dircache->cache_size; i++) + { + if(the_dircache->cache[i].complete_path != NULL) + free(the_dircache->cache[i].complete_path); + } + + free(the_dircache); + + server->dircache = new_cache; + result = cache_size; + + out: + + return(result); +} diff --git a/source_code/smb_abstraction.h b/source_code/smb_abstraction.h new file mode 100644 index 0000000..b27fcb4 --- /dev/null +++ b/source_code/smb_abstraction.h @@ -0,0 +1,86 @@ +/* + * $Id: smb_abstraction.h,v 1.1.1.1 2005-05-24 13:22:13 obarthel Exp $ + * + * :ts=8 + * + * Name: smb_abstraction.h + * Description: Interface to the smb abstraction layer. + * Author: Christian Starkjohann + * Date: 1996-12-31 + * Copyright: GNU-GPL + * + * Modified for use with AmigaOS by Olaf Barthel + * Modified to support record locking by Peter Riede + */ + +#ifndef _SMB_ABSTRACTION_H +#define _SMB_ABSTRACTION_H 1 + +/****************************************************************************/ + +/* Forward declaration to keep the compiler happy. */ +#ifndef _SMB_FS_SB +struct smb_server; +#endif /* _SMB_FS_SB */ + +/****************************************************************************/ + +typedef struct smba_connect_parameters +{ + char server_ipname[64]; + char service[64]; + char *server_name; + char *client_name; + char *username; + char *password; + int max_xmit; +} smba_connect_parameters_t; + +typedef struct smba_stat +{ + unsigned is_dir:1; + unsigned is_wp:1; + unsigned is_hidden:1; + unsigned is_system:1; + unsigned is_archive:1; + int size; + long atime; + long ctime; + long mtime; +} smba_stat_t; + +/****************************************************************************/ + +typedef struct smba_server smba_server_t; +typedef struct smba_file smba_file_t; + +/****************************************************************************/ + +typedef int (*smba_callback_t) (void *d, int fpos, int nextpos, char *name, int eof, smba_stat_t * st); + +/****************************************************************************/ + +int smba_open(smba_server_t *s, char *name, size_t name_size, smba_file_t **file); +void smba_close(smba_file_t *f); +int smba_read(smba_file_t *f, char *data, long len, long offset); +int smba_write(smba_file_t *f, char *data, long len, long offset); +long smba_seek (smba_file_t *f, long offset, long mode, off_t * new_position_ptr); +int smba_lockrec (smba_file_t *f, long offset, long len, long mode, int unlocked, long timeout); +int smba_getattr(smba_file_t *f, smba_stat_t *data); +int smba_setattr(smba_file_t *f, smba_stat_t *data); +int smba_readdir(smba_file_t *f, long offs, void *d, smba_callback_t callback); +int smba_create(smba_file_t *dir, const char *name, smba_stat_t *attr); +int smba_mkdir(smba_file_t *dir, const char *name); +int smba_remove(smba_server_t *s, char *path); +int smba_rmdir(smba_server_t *s, char *path); +int smba_rename(smba_server_t *s, char *from, char *to); +int smba_statfs(smba_server_t *s, long *bsize, long *blocks, long *bfree); +void smb_invalidate_all_inodes(struct smb_server *server); +int smba_start(char *service, char *opt_workgroup, char *opt_username, char *opt_password, char *opt_clientname, char *opt_servername, int opt_cachesize, smba_server_t **result); +void smba_disconnect(smba_server_t *server); +int smba_get_dircache_size(struct smba_server * server); +int smba_change_dircache_size(struct smba_server * server,int cache_size); + +/****************************************************************************/ + +#endif /* _SMB_ABSTRACTION_H */ diff --git a/source_code/smbfs.h b/source_code/smbfs.h new file mode 100644 index 0000000..c5874c8 --- /dev/null +++ b/source_code/smbfs.h @@ -0,0 +1,159 @@ +/* + * $Id: smbfs.h,v 1.1.1.1 2005-05-24 13:22:12 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. + */ + +#ifndef _SMBFS_H +#define _SMBFS_H 1 + +/****************************************************************************/ + +#ifndef _SYSTEM_HEADERS_H +#include "system_headers.h" +#endif /* _SYSTEM_HEADERS_H */ + +#ifndef _ASSERT_H +#include "assert.h" +#endif /* _ASSERT_H */ + +/****************************************************************************/ + +#define SAME (0) +#define OK (0) +#define NOT ! + +/****************************************************************************/ + +#ifndef ZERO +#define ZERO ((BPTR)NULL) +#endif /* ZERO */ + +/****************************************************************************/ + +#ifndef AMIGA_COMPILER_H + +/****************************************************************************/ + +#if defined(__SASC) +#define FAR __far +#define ASM __asm +#define REG(r,p) register __##r p +#define INLINE __inline +#endif /* __SASC */ + +#if defined(__GNUC__) +#define FAR +#define ASM +#define REG(r,p) p __asm(#r) +#define INLINE __inline__ +#endif /* __GNUC__ */ + +/****************************************************************************/ + +#ifndef VARARGS68K +#define VARARGS68K +#endif /* VARARGS68K */ + +/*****************************************************************************/ + +#endif /* AMIGA_COMPILER_H */ + +/*****************************************************************************/ + +#ifndef min +#define min(a,b) ((a) < (b) ? (a) : (b)) +#endif /* min */ + +/****************************************************************************/ + +#if defined(__SASC) +extern struct Library * FAR AbsExecBase; +#else +#ifndef AbsExecBase +#define AbsExecBase (*(struct Library **)4) +#endif /* AbsExecBase */ +#endif /* __SASC */ + +/****************************************************************************/ + +extern struct Library * SocketBase; +extern struct Library * SysBase; +extern struct Library * DOSBase; + +/****************************************************************************/ + +#if defined(__amigaos4__) + +/****************************************************************************/ + +extern struct ExecIFace * IExec; +extern struct DOSIFace * IDOS; +extern struct SocketIFace * ISocket; + +/****************************************************************************/ + +#endif /* __amigaos4__ */ + +/****************************************************************************/ + +extern int h_errno; + +/****************************************************************************/ + +extern int BroadcastNameQuery(char *name, char *scope, UBYTE *address); +extern LONG CompareNames(STRPTR a,STRPTR b); +extern LONG GetTimeZoneDelta(VOID); +extern STRPTR amitcp_strerror(int error); +extern STRPTR host_strerror(int error); +extern time_t MakeTime(const struct tm * const tm); +extern ULONG GetCurrentTime(VOID); +extern VOID LocalTime(time_t seconds,struct tm * tm); +extern VOID VARARGS68K ReportError(STRPTR fmt,...); +extern VOID StringToUpper(STRPTR s); +extern VOID VARARGS68K SPrintf(STRPTR buffer, STRPTR formatString,...); + +/****************************************************************************/ + +size_t strlcpy(char *dst, const char *src, size_t siz); +size_t strlcat(char *dst, const char *src, size_t siz); + +/****************************************************************************/ + +extern void smb_encrypt(unsigned char *passwd, unsigned char *c8, unsigned char *p24); +extern void smb_nt_encrypt(unsigned char *passwd, unsigned char *c8, unsigned char *p24); + +/****************************************************************************/ + +extern VOID FreeMemory(APTR address); +extern APTR AllocateMemory(ULONG size); + +#define malloc(s) AllocateMemory(s) +#define free(m) FreeMemory(m) + +/****************************************************************************/ + +#undef memcpy +#define memcpy(to,from,size) ((void)CopyMem((APTR)(from),(APTR)(to),(ULONG)(size))) + +/****************************************************************************/ + +#endif /* _SMBFS_H */ diff --git a/source_code/smbfs_rev.h b/source_code/smbfs_rev.h new file mode 100644 index 0000000..84ae4bf --- /dev/null +++ b/source_code/smbfs_rev.h @@ -0,0 +1,6 @@ +#define VERSION 1 +#define REVISION 66 +#define DATE "10.6.2004" +#define VERS "smbfs 1.66" +#define VSTRING "smbfs 1.66 (10.6.2004)\r\n" +#define VERSTAG "\0$VER: smbfs 1.66 (10.6.2004)" diff --git a/source_code/smbfs_rev.rev b/source_code/smbfs_rev.rev new file mode 100644 index 0000000..69a893a --- /dev/null +++ b/source_code/smbfs_rev.rev @@ -0,0 +1 @@ +66 diff --git a/source_code/sock.c b/source_code/sock.c new file mode 100644 index 0000000..a4127f1 --- /dev/null +++ b/source_code/sock.c @@ -0,0 +1,524 @@ +/* + * $Id: sock.c,v 1.1.1.1 2005-05-24 13:22:13 obarthel Exp $ + * + * :ts=8 + * + * sock.c + * + * Copyright (C) 1995 by Paal-Kr. Engstad and Volker Lendecke + * Modified by Christian Starkjohann + * Modified for use with AmigaOS by Olaf Barthel + */ + +#include "smbfs.h" + +/*****************************************************************************/ + +#include +#include +#include + +/*****************************************************************************/ + +#include "smb_abstraction.h" + +/*****************************************************************************/ + +/* smb_receive_raw + fs points to the correct segment, sock != NULL, target != NULL + The smb header is only stored if want_header != 0. */ +static int +smb_receive_raw (int sock_fd, unsigned char *target, int max_raw_length, int want_header) +{ + int len, result; + int already_read; + unsigned char peek_buf[4]; + + re_recv: + + result = recvfrom (sock_fd, (void *) peek_buf, 4, 0, NULL, NULL); + if (result < 0) + { + LOG (("smb_receive_raw: recv error = %ld\n", errno)); + result = (-errno); + goto out; + } + + if (result < 4) + { + LOG (("smb_receive_raw: got less than 4 bytes\n")); + result = -EIO; + goto out; + } + + switch (peek_buf[0]) + { + case 0x00: + case 0x82: + break; + + case 0x85: + LOG (("smb_receive_raw: Got SESSION KEEP ALIVE\n")); + goto re_recv; + + default: + LOG (("smb_receive_raw: Invalid packet 0x%02lx\n", peek_buf[0])); + result = -EIO; + goto out; + } + + /* The length in the RFC NB header is the raw data length */ + len = smb_len (peek_buf); + if (len > max_raw_length) + { + LOG (("smb_receive_raw: Received length (%ld) > max_xmit (%ld)!\n", len, max_raw_length)); + result = -EIO; + goto out; + } + + if (want_header != 0) + { + memcpy (target, peek_buf, 4); + target += 4; + } + + already_read = 0; + + while (already_read < len) + { + result = recvfrom (sock_fd, (void *) (target + already_read), len - already_read, 0, NULL, NULL); + if (result < 0) + { + LOG (("smb_receive_raw: recvfrom error = %ld\n", errno)); + + result = (-errno); + + goto out; + } + + already_read += result; + } + + result = already_read; + + out: + + return result; +} + +/* smb_receive + fs points to the correct segment, server != NULL, sock!=NULL */ +static int +smb_receive (struct smb_server *server, int sock_fd) +{ + byte * packet = server->packet; + int result; + + result = smb_receive_raw (sock_fd, packet, + server->max_recv - 4, /* max_xmit in server includes NB header */ + 1); /* We want the header */ + if (result < 0) + { + LOG (("smb_receive: receive error: %ld\n", result)); + goto out; + } + + server->rcls = *((unsigned char *) (packet + 9)); + server->err = WVAL (packet, 11); + + if (server->rcls != 0) + LOG (("smb_receive: rcls=%ld, err=%ld\n", server->rcls, server->err)); + + out: + + return result; +} + +/* smb_receive's preconditions also apply here. */ +static int +smb_receive_trans2 (struct smb_server *server, int sock_fd, int *data_len, int *param_len, char **data, char **param) +{ + unsigned char *inbuf = server->packet; + int total_data; + int total_param; + int result; + + LOG (("smb_receive_trans2: enter\n")); + + (*data_len) = (*param_len) = 0; + (*param) = (*data) = NULL; + + result = smb_receive (server, sock_fd); + if (result < 0) + goto fail; + + if (server->rcls != 0) + goto fail; + + /* parse out the lengths */ + total_data = WVAL (inbuf, smb_tdrcnt); + total_param = WVAL (inbuf, smb_tprcnt); + + if ((total_data > server->max_xmit) || (total_param > server->max_xmit)) + { + LOG (("smb_receive_trans2: data/param too long\n")); + + result = -EIO; + goto fail; + } + + /* Allocate it, but only if there is something to allocate + in the first place. ZZZ this may not be the proper approach, + and we should return an error for 'no data'. */ + if(total_data > 0) + { + (*data) = malloc (total_data); + if ((*data) == NULL) + { + LOG (("smb_receive_trans2: could not alloc data area\n")); + + result = -ENOMEM; + goto fail; + } + } + else + { + (*data) = NULL; + } + + /* Allocate it, but only if there is something to allocate + in the first place. ZZZ this may not be the proper approach, + and we should return an error for 'no parameters'. */ + if(total_param > 0) + { + (*param) = malloc(total_param); + if ((*param) == NULL) + { + LOG (("smb_receive_trans2: could not alloc param area\n")); + + result = -ENOMEM; + goto fail; + } + } + else + { + (*param) = NULL; + } + + LOG (("smb_rec_trans2: total_data/param: %ld/%ld\n", total_data, total_param)); + + while (1) + { + if (WVAL (inbuf, smb_prdisp) + WVAL (inbuf, smb_prcnt) > (unsigned int)total_param) + { + LOG (("smb_receive_trans2: invalid parameters\n")); + result = -EIO; + goto fail; + } + + if((*param) != NULL) + memcpy ((*param) + WVAL (inbuf, smb_prdisp), smb_base (inbuf) + WVAL (inbuf, smb_proff), WVAL (inbuf, smb_prcnt)); + + (*param_len) += WVAL (inbuf, smb_prcnt); + + if (WVAL (inbuf, smb_drdisp) + WVAL (inbuf, smb_drcnt) > (unsigned int)total_data) + { + LOG (("smb_receive_trans2: invalid data block\n")); + result = -EIO; + goto fail; + } + + if((*data) != NULL) + memcpy ((*data) + WVAL (inbuf, smb_drdisp), smb_base (inbuf) + WVAL (inbuf, smb_droff), WVAL (inbuf, smb_drcnt)); + + (*data_len) += WVAL (inbuf, smb_drcnt); + + LOG (("smb_rec_trans2: drcnt/prcnt: %ld/%ld\n", WVAL (inbuf, smb_drcnt), WVAL (inbuf, smb_prcnt))); + + /* parse out the total lengths again - they can shrink! */ + if ((WVAL (inbuf, smb_tdrcnt) > (unsigned int)total_data) || (WVAL (inbuf, smb_tprcnt) > (unsigned int)total_param)) + { + LOG (("smb_receive_trans2: data/params grew!\n")); + result = -EIO; + goto fail; + } + + total_data = WVAL (inbuf, smb_tdrcnt); + total_param = WVAL (inbuf, smb_tprcnt); + if (total_data <= (*data_len) && total_param <= (*param_len)) + break; + + result = smb_receive (server, sock_fd); + if (result < 0) + goto fail; + + if (server->rcls != 0) + { + result = -EIO; + goto fail; + } + } + + LOG (("smb_receive_trans2: normal exit\n")); + return 0; + + fail: + + LOG (("smb_receive_trans2: failed exit\n")); + + if((*param) != NULL) + free (*param); + + if((*data) != NULL) + free (*data); + + (*param) = (*data) = NULL; + + return result; +} + +int +smb_release (struct smb_server *server) +{ + int result; + + if (server->mount_data.fd >= 0) + CloseSocket (server->mount_data.fd); + + server->mount_data.fd = socket (AF_INET, SOCK_STREAM, 0); + if (server->mount_data.fd < 0) + { + result = (-errno); + goto out; + } + + result = 0; + + out: + + return result; +} + +int +smb_connect (struct smb_server *server) +{ + int sock_fd = server->mount_data.fd; + int result; + + if (sock_fd < 0) + { + result = (-EBADF); + goto out; + } + + result = connect (sock_fd, (struct sockaddr *)&server->mount_data.addr, sizeof(struct sockaddr_in)); + if(result < 0) + result = (-errno); + + out: + + return(result); +} + +/***************************************************************************** + * + * This routine was once taken from nfs, which is for udp. Here TCP does + * most of the ugly stuff for us (thanks, Alan!) + * + ****************************************************************************/ + +int +smb_request (struct smb_server *server) +{ + int len, result; + int sock_fd = server->mount_data.fd; + unsigned char *buffer = server->packet; + + if ((sock_fd < 0) || (buffer == NULL)) + { + LOG (("smb_request: Bad server!\n")); + result = -EBADF; + goto out; + } + + if (server->state != CONN_VALID) + { + result = -EIO; + goto out; + } + + len = smb_len (buffer) + 4; + + LOG (("smb_request: len = %ld cmd = 0x%lx\n", len, buffer[8])); + + result = send (sock_fd, (void *) buffer, len, 0); + if (result < 0) + { + LOG (("smb_request: send error = %ld\n", errno)); + + result = (-errno); + } + else + { + result = smb_receive (server, sock_fd); + } + + out: + + if (result < 0) + { + server->state = CONN_INVALID; + smb_invalidate_all_inodes (server); + } + + LOG (("smb_request: result = %ld\n", result)); + + return (result); +} + +/* This is not really a trans2 request, we assume that you only have + one packet to send. */ +int +smb_trans2_request (struct smb_server *server, int *data_len, int *param_len, char **data, char **param) +{ + int len, result; + int sock_fd = server->mount_data.fd; + unsigned char *buffer = server->packet; + + if (server->state != CONN_VALID) + { + result = -EIO; + goto out; + } + + len = smb_len (buffer) + 4; + + LOG (("smb_request: len = %ld cmd = 0x%02lx\n", len, buffer[8])); + + result = send (sock_fd, (void *) buffer, len, 0); + if (result < 0) + { + LOG (("smb_trans2_request: send error = %ld\n", errno)); + + result = (-errno); + } + else + { + result = smb_receive_trans2 (server, sock_fd, data_len, param_len, data, param); + } + + out: + + if (result < 0) + { + server->state = CONN_INVALID; + smb_invalidate_all_inodes (server); + } + + LOG (("smb_trans2_request: result = %ld\n", result)); + + return result; +} + +/* target must be in user space */ +int +smb_request_read_raw (struct smb_server *server, unsigned char *target, int max_len) +{ + int len, result; + int sock_fd = server->mount_data.fd; + unsigned char *buffer = server->packet; + + if (server->state != CONN_VALID) + { + result = -EIO; + goto out; + } + + len = smb_len (buffer) + 4; + + LOG (("smb_request_read_raw: len = %ld cmd = 0x%02lx\n", len, buffer[8])); + LOG (("smb_request_read_raw: target=%lx, max_len=%ld\n", (unsigned int) target, max_len)); + LOG (("smb_request_read_raw: buffer=%lx, sock=%lx\n", (unsigned int) buffer, (unsigned int) sock_fd)); + + result = send (sock_fd, (void *) buffer, len, 0); + + LOG (("smb_request_read_raw: send returned %ld\n", result)); + + if (result < 0) + { + LOG (("smb_request_read_raw: send error = %ld\n", errno)); + + result = (-errno); + } + else + { + result = smb_receive_raw (sock_fd, target, max_len, 0); + } + + out: + + if (result < 0) + { + server->state = CONN_INVALID; + smb_invalidate_all_inodes (server); + } + + LOG (("smb_request_read_raw: result = %ld\n", result)); + + return result; +} + +/* Source must be in user space. smb_request_write_raw assumes that + the request SMBwriteBraw has been completed successfully, so that + we can send the raw data now. */ +int +smb_request_write_raw (struct smb_server *server, unsigned const char *source, int length) +{ + int result; + byte nb_header[4]; + int sock_fd = server->mount_data.fd; + + if (server->state != CONN_VALID) + { + result = -EIO; + goto out; + } + + smb_encode_smb_length (nb_header, length); + + result = send (sock_fd, (void *) nb_header, 4, 0); + if (result == 4) + { + result = send (sock_fd, (void *) source, length, 0); + if(result < 0) + result = (-errno); + } + else + { + if(result < 0) + result = (-errno); + else + result = -EIO; + } + + LOG (("smb_request_write_raw: send returned %ld\n", result)); + + if (result == length) + result = smb_receive (server, sock_fd); + + out: + + if (result < 0) + { + server->state = CONN_INVALID; + + smb_invalidate_all_inodes (server); + } + + if (result > 0) + result = length; + + LOG (("smb_request_write_raw: result = %ld\n", result)); + + return result; +} diff --git a/source_code/system_headers.c b/source_code/system_headers.c new file mode 100644 index 0000000..09cb951 --- /dev/null +++ b/source_code/system_headers.c @@ -0,0 +1,29 @@ +/* + * $Id: system_headers.c,v 1.1.1.1 2005-05-24 13:22:13 obarthel Exp $ + * + * :ts=4 + * + * SMB file system wrapper for AmigaOS, using the AmiTCP V3 API + * + * Copyright (C) 2000-2003 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. + */ + +/*****************************************************************************/ + +#ifndef _SYSTEM_HEADERS_H +#include "system_headers.h" +#endif /* _SYSTEM_HEADERS_H */ diff --git a/source_code/system_headers.h b/source_code/system_headers.h new file mode 100644 index 0000000..b4fa440 --- /dev/null +++ b/source_code/system_headers.h @@ -0,0 +1,121 @@ +/* + * $Id: system_headers.h,v 1.1.1.1 2005-05-24 13:22:13 obarthel Exp $ + * + * :ts=4 + * + * SMB file system wrapper for AmigaOS, using the AmiTCP V3 API + * + * Copyright (C) 2000-2003 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. + */ + +#ifndef _SYSTEM_HEADERS_H +#define _SYSTEM_HEADERS_H 1 + +/*****************************************************************************/ + +#define __USE_INLINE__ +#define __NOGLOBALIFACE__ +#define __NOLIBBASE__ + +/****************************************************************************/ + +#if defined(__SASC) +#define USE_BUILTIN_MATH +#endif /* __SASC */ + +/*****************************************************************************/ + +#define NULL ((APTR)0L) +#include + +/*****************************************************************************/ + +#define byte IGNORE_THIS + +/*****************************************************************************/ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include + +/*#include */ + +#include +#include + +/*****************************************************************************/ + +#include + +/*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +/*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +/*#include */ +/*#include */ +#include + +#include +#include + +#include +#include +#include + +/*****************************************************************************/ + +#undef byte + +/*****************************************************************************/ + +#endif /* _SYSTEM_HEADERS_H */