diff --git docs-xml/manpages/smbclient.1.xml docs-xml/manpages/smbclient.1.xml index d531390..4bc9ecd 100644 --- docs-xml/manpages/smbclient.1.xml +++ docs-xml/manpages/smbclient.1.xml @@ -80,171 +80,171 @@ This tool is part of the samba 7 suite. - smbclient is a client that can + smbclient is a client that can 'talk' to an SMB/CIFS server. It offers an interface similar to that of the ftp program (see ftp - 1). - Operations include things like getting files from the server - to the local machine, putting files from the local machine to - the server, retrieving directory information from the server + 1). + Operations include things like getting files from the server + to the local machine, putting files from the local machine to + the server, retrieving directory information from the server and so on. OPTIONS - + servicename - servicename is the name of the service + servicename is the name of the service you want to use on the server. A service name takes the form //server/service where server - is the NetBIOS name of the SMB/CIFS server - offering the desired service and service - is the name of the service offered. Thus to connect to + is the NetBIOS name of the SMB/CIFS server + offering the desired service and service + is the name of the service offered. Thus to connect to the service "printer" on the SMB/CIFS server "smbserver", you would use the servicename //smbserver/printer - Note that the server name required is NOT necessarily - the IP (DNS) host name of the server ! The name required is + Note that the server name required is NOT necessarily + the IP (DNS) host name of the server ! The name required is a NetBIOS server name, which may or may not be the same as the IP hostname of the machine running the server. - The server name is looked up according to either - the -R parameter to smbclient or - using the name resolve order parameter in + The server name is looked up according to either + the -R parameter to smbclient or + using the name resolve order parameter in the smb.conf - 5 file, - allowing an administrator to change the order and methods + 5 file, + allowing an administrator to change the order and methods by which server names are looked up. password - The password required to access the specified - service on the specified server. If this parameter is - supplied, the -N option (suppress + The password required to access the specified + service on the specified server. If this parameter is + supplied, the -N option (suppress password prompt) is assumed. - There is no default password. If no password is supplied - on the command line (either by using this parameter or adding - a password to the -U option (see - below)) and the -N option is not - specified, the client will prompt for a password, even if - the desired service does not require one. (If no password is + There is no default password. If no password is supplied + on the command line (either by using this parameter or adding + a password to the -U option (see + below)) and the -N option is not + specified, the client will prompt for a password, even if + the desired service does not require one. (If no password is required, simply press ENTER to provide a null password.) - Note: Some servers (including OS/2 and Windows for - Workgroups) insist on an uppercase password. Lowercase - or mixed case passwords may be rejected by these servers. + Note: Some servers (including OS/2 and Windows for + Workgroups) insist on an uppercase password. Lowercase + or mixed case passwords may be rejected by these servers. Be cautious about including passwords in scripts. - + -R|--name-resolve <name resolve order> - This option is used by the programs in the Samba - suite to determine what naming services and in what order to resolve - host names to IP addresses. The option takes a space-separated + This option is used by the programs in the Samba + suite to determine what naming services and in what order to resolve + host names to IP addresses. The option takes a space-separated string of different name resolution options. - The options are :"lmhosts", "host", "wins" and "bcast". They + The options are :"lmhosts", "host", "wins" and "bcast". They cause names to be resolved as follows: - lmhosts: Lookup an IP - address in the Samba lmhosts file. If the line in lmhosts has - no name type attached to the NetBIOS name (see + lmhosts: Lookup an IP + address in the Samba lmhosts file. If the line in lmhosts has + no name type attached to the NetBIOS name (see the lmhosts 5 for details) then any name type matches for lookup. - - host: Do a standard host + + host: Do a standard host name to IP address resolution, using the system /etc/hosts - , NIS, or DNS lookups. This method of name resolution - is operating system dependent, for instance on IRIX or Solaris this - may be controlled by the /etc/nsswitch.conf - file). Note that this method is only used if the NetBIOS name - type being queried is the 0x20 (server) name type, otherwise + , NIS, or DNS lookups. This method of name resolution + is operating system dependent, for instance on IRIX or Solaris this + may be controlled by the /etc/nsswitch.conf + file). Note that this method is only used if the NetBIOS name + type being queried is the 0x20 (server) name type, otherwise it is ignored. - - wins: Query a name with + + wins: Query a name with the IP address listed in the wins server - parameter. If no WINS server has + parameter. If no WINS server has been specified this method will be ignored. - - bcast: Do a broadcast on - each of the known local interfaces listed in the + + bcast: Do a broadcast on + each of the known local interfaces listed in the interfaces - parameter. This is the least reliable of the name resolution - methods as it depends on the target host being on a locally + parameter. This is the least reliable of the name resolution + methods as it depends on the target host being on a locally connected subnet. - If this parameter is not set then the name resolve order + If this parameter is not set then the name resolve order defined in the smb.conf - 5 file parameter + 5 file parameter (name resolve order) will be used. - The default order is lmhosts, host, wins, bcast and without + The default order is lmhosts, host, wins, bcast and without this parameter or any entry in the name resolve order parameter of the smb.conf 5 file the name resolution methods will be attempted in this order. - - + + -M|--message NetBIOS name - This options allows you to send messages, using - the "WinPopup" protocol, to another computer. Once a connection is - established you then type your message, pressing ^D (control-D) to + This options allows you to send messages, using + the "WinPopup" protocol, to another computer. Once a connection is + established you then type your message, pressing ^D (control-D) to end. - If the receiving computer is running WinPopup the user will - receive the message and probably a beep. If they are not running - WinPopup the message will be lost, and no error message will + If the receiving computer is running WinPopup the user will + receive the message and probably a beep. If they are not running + WinPopup the message will be lost, and no error message will occur. - The message is also automatically truncated if the message - is over 1600 bytes, as this is the limit of the protocol. + The message is also automatically truncated if the message + is over 1600 bytes, as this is the limit of the protocol. - One useful trick is to pipe the message through smbclient. - For example: smbclient -M FRED < mymessage.txt will send the - message in the file mymessage.txt to the + One useful trick is to pipe the message through smbclient. + For example: smbclient -M FRED < mymessage.txt will send the + message in the file mymessage.txt to the machine FRED. - You may also find the -U and - -I options useful, as they allow you to + You may also find the -U and + -I options useful, as they allow you to control the FROM and TO parts of the message. See the message command parameter in the smb.conf - 5 for a description of how to handle incoming + 5 for a description of how to handle incoming WinPopup messages in Samba. - Note: Copy WinPopup into the startup group - on your WfWg PCs if you want them to always be able to receive + Note: Copy WinPopup into the startup group + on your WfWg PCs if you want them to always be able to receive messages. -p|--port port - This number is the TCP port number that will be used + This number is the TCP port number that will be used when making connections to the server. The standard (well-known) - TCP port number for an SMB/CIFS server is 139, which is the + TCP port number for an SMB/CIFS server is 139, which is the default. @@ -281,40 +281,40 @@ IP address is the address of the server to connect to. It should be specified in standard "a.b.c.d" notation. - Normally the client would attempt to locate a named - SMB/CIFS server by looking it up via the NetBIOS name resolution - mechanism described above in the name resolve order + Normally the client would attempt to locate a named + SMB/CIFS server by looking it up via the NetBIOS name resolution + mechanism described above in the name resolve order parameter above. Using this parameter will force the client - to assume that the server is on the machine with the specified IP - address and the NetBIOS name component of the resource being + to assume that the server is on the machine with the specified IP + address and the NetBIOS name component of the resource being connected to will be ignored. - There is no default for this parameter. If not supplied, - it will be determined automatically by the client as described + There is no default for this parameter. If not supplied, + it will be determined automatically by the client as described above. - + -E|--stderr - This parameter causes the client to write messages - to the standard error stream (stderr) rather than to the standard + This parameter causes the client to write messages + to the standard error stream (stderr) rather than to the standard output stream. - - By default, the client writes messages to standard output + + By default, the client writes messages to standard output - typically the user's tty. - + -L|--list - This option allows you to look at what services - are available on a server. You use it as smbclient -L + This option allows you to look at what services + are available on a server. You use it as smbclient -L host and a list should appear. The -I - option may be useful if your NetBIOS names don't - match your TCP/IP DNS host names or if you are trying to reach a + option may be useful if your NetBIOS names don't + match your TCP/IP DNS host names or if you are trying to reach a host on another network. - - + + -b|--send-buffer buffersize When sending or receiving files, smbclient uses an @@ -328,7 +328,7 @@ using the iosize command inside smbclient. - + -B|--browse Browse SMB servers using DNS. @@ -340,7 +340,7 @@ &popt.common.credentials; &popt.common.connection; &popt.autohelp; - + -t|--timeout <timeout-seconds> This allows the user to tune the default @@ -357,144 +357,142 @@ -T|--tar tar options smbclient may be used to create tar(1) compatible backups of all the files on an SMB/CIFS - share. The secondary tar flags that can be given to this option - are : - + share. The secondary tar flags that can be given to this option + are: + - c - Create a tar file on UNIX. - Must be followed by the name of a tar file, tape device - or "-" for standard output. If using standard output you must - turn the log level to its lowest value -d0 to avoid corrupting - your tar file. This flag is mutually exclusive with the + c - Create a tar + backup archive on the local system. Must be followed by + the name of a tar file, tape device or "-" for standard + output. If using standard output you must turn the log + level to its lowest value -d0 to avoid corrupting your tar + file. This flag is mutually exclusive with the x flag. - - x - Extract (restore) a local - tar file back to a share. Unless the -D option is given, the tar - files will be restored from the top level of the share. Must be - followed by the name of the tar file, device or "-" for standard - input. Mutually exclusive with the c flag. + + x - Extract (restore) a local + tar file back to a share. Unless the -D option is given, the tar + files will be restored from the top level of the share. Must be + followed by the name of the tar file, device or "-" for standard + input. Mutually exclusive with the c flag. Restored files have their creation times (mtime) set to the - date saved in the tar file. Directories currently do not get + date saved in the tar file. Directories currently do not get their creation dates restored properly. - - I - Include files and directories. - Is the default behavior when filenames are specified above. Causes - files to be included in an extract or create (and therefore - everything else to be excluded). See example below. Filename globbing - works in one of two ways. See r below. - - X - Exclude files and directories. - Causes files to be excluded from an extract or create. See - example below. Filename globbing works in one of two ways now. + + I - Include files and directories. + Is the default behavior when filenames are specified above. Causes + files to be included in an extract or create (and therefore + everything else to be excluded). See example below. Filename globbing + works in one of two ways. See r below. + + X - Exclude files and directories. + Causes files to be excluded from an extract or create. See + example below. Filename globbing works in one of two ways. See r below. - + F - File containing a list of files and directories. The F causes the name following the tarfile to - create to be read as a filename that contains a list of files and directories to + create to be read as a filename that contains a list of files and directories to be included in an extract or create (and therefore everything else to be excluded). See example below. Filename globbing works in one of two ways. See r below. - - b - Blocksize. Must be followed - by a valid (greater than zero) blocksize. Causes tar file to be - written out in blocksize*TBLOCK (usually 512 byte) blocks. + + b - Blocksize. Must be followed + by a valid (greater than zero) blocksize. Causes tar file to be + written out in blocksize*TBLOCK (512 byte) blocks. - - g - Incremental. Only back up - files that have the archive bit set. Useful only with the + + g - Incremental. Only back up + files that have the archive bit set. Useful only with the c flag. - q - Quiet. Keeps tar from printing - diagnostics as it works. This is the same as tarmode quiet. + q - Quiet. Keeps tar from printing + diagnostics as it works. This is the same as tarmode quiet. - - r - Regular expression include - or exclude. Uses regular expression matching for - excluding or excluding files if compiled with HAVE_REGEX_H. - However this mode can be very slow. If not compiled with - HAVE_REGEX_H, does a limited wildcard match on '*' and '?'. + + r - Use wildcard + matching to include or exclude. Deprecated. - - N - Newer than. Must be followed - by the name of a file whose date is compared against files found - on the share during a create. Only files newer than the file - specified are backed up to the tar file. Useful only with the + + N - Newer than. Must be followed + by the name of a file whose date is compared against files found + on the share during a create. Only files newer than the file + specified are backed up to the tar file. Useful only with the c flag. - - a - Set archive bit. Causes the - archive bit to be reset when a file is backed up. Useful with the - g and c flags. + + a - Set archive bit. Causes the + archive bit to be reset when a file is backed up. Useful with the + g and c flags. - + Tar Long File Names - - smbclient's tar option now supports long - file names both on backup and restore. However, the full path - name of the file must be less than 1024 bytes. Also, when - a tar archive is created, smbclient's tar option places all - files in the archive with relative names, not absolute names. + + smbclient's tar option now supports long + file names both on backup and restore. However, the full path + name of the file must be less than 1024 bytes. Also, when + a tar archive is created, smbclient's tar option places all + files in the archive with relative names, not absolute names. Tar Filenames - - All file names can be given as DOS path names (with '\\' - as the component separator) or as UNIX path names (with '/' as + + All file names can be given as DOS path names (with '\\' + as the component separator) or as UNIX path names (with '/' as the component separator). - + Examples - - Restore from tar file backup.tar into myshare on mypc + + Restore from tar file backup.tar into myshare on mypc (no password on share). - + smbclient //mypc/myshare "" -N -Tx backup.tar - + Restore everything except users/docs - - smbclient //mypc/myshare "" -N -TXx backup.tar + + smbclient //mypc/myshare "" -N -TXx backup.tar users/docs - + Create a tar file of the files beneath users/docs. - + smbclient //mypc/myshare "" -N -Tc backup.tar users/docs - - Create the same tar file as above, but now use + + Create the same tar file as above, but now use a DOS path name. - + smbclient //mypc/myshare "" -N -Tc backup.tar users\edocs - + Create a tar file of the files listed in the file tarlist. - + smbclient //mypc/myshare "" -N -TcF backup.tar tarlist - - Create a tar file of all the files and directories in + + Create a tar file of all the files and directories in the share. - + smbclient //mypc/myshare "" -N -Tc backup.tar * - + -D|--directory initial directory - Change to initial directory before starting. Probably + Change to initial directory before starting. Probably only of any use with the tar -T option. - + -c|--command command string - command string is a semicolon-separated list of + command string is a semicolon-separated list of commands to be executed instead of prompting from stdin. -N is implied by -c. - This is particularly useful in scripts and for printing stdin + This is particularly useful in scripts and for printing stdin to the server, e.g. -c 'print -'. @@ -505,35 +503,35 @@ OPERATIONS - Once the client is running, the user is presented with + Once the client is running, the user is presented with a prompt : smb:\> - The backslash ("\\") indicates the current working directory - on the server, and will change if the current working directory + The backslash ("\\") indicates the current working directory + on the server, and will change if the current working directory is changed. - The prompt indicates that the client is ready and waiting to - carry out a user command. Each command is a single word, optionally - followed by parameters specific to that command. Command and parameters + The prompt indicates that the client is ready and waiting to + carry out a user command. Each command is a single word, optionally + followed by parameters specific to that command. Command and parameters are space-delimited unless these notes specifically - state otherwise. All commands are case-insensitive. Parameters to - commands may or may not be case sensitive, depending on the command. + state otherwise. All commands are case-insensitive. Parameters to + commands may or may not be case sensitive, depending on the command. - You can specify file names which have spaces in them by quoting + You can specify file names which have spaces in them by quoting the name with double quotes, for example "a long file name". - Parameters shown in square brackets (e.g., "[parameter]") are - optional. If not given, the command will use suitable defaults. Parameters + Parameters shown in square brackets (e.g., "[parameter]") are + optional. If not given, the command will use suitable defaults. Parameters shown in angle brackets (e.g., "<parameter>") are required. - Note that all commands operating on the server are actually - performed by issuing a request to the server. Thus the behavior may - vary from server to server, depending on how the server was implemented. + Note that all commands operating on the server are actually + performed by issuing a request to the server. Thus the behavior may + vary from server to server, depending on how the server was implemented. The commands available are given here in alphabetical order. @@ -778,8 +776,8 @@ lowercase Toggle lowercasing of filenames for the get and - mget commands. - + mget commands. + When lowercasing is toggled ON, local filenames are converted to lowercase when using the get and mget commands. This is @@ -994,7 +992,7 @@ rmdir <directory name> - Remove the specified directory (user access + Remove the specified directory (user access privileges permitting) from the server. @@ -1038,29 +1036,44 @@ tar <c|x>[IXbgNa] - Performs a tar operation - see the -T - command line option above. Behavior may be affected - by the tarmode command (see below). Using g (incremental) and N - (newer) will affect tarmode settings. Note that using the "-" option - with tar x may not work - use the command line option instead. - + Performs a tar operation - see the + -T command line option above. Behavior + may be affected by the tarmode command (see below). Using g + (incremental) and N (newer) will affect tarmode settings. Note + that using the "-" option with tar x may not work - use the + command line option instead. blocksize <blocksize> Blocksize. Must be followed by a valid (greater than zero) blocksize. Causes tar file to be written out in - blocksize*TBLOCK (usually 512 byte) blocks. + blocksize*TBLOCK (512 byte) blocks. - tarmode <full|inc|reset|noreset> - Changes tar's behavior with regard to archive - bits. In full mode, tar will back up everything regardless of the - archive bit setting (this is the default mode). In incremental mode, - tar will only back up files with the archive bit set. In reset mode, - tar will reset the archive bit on all files it backs up (implies - read/write share). + tarmode <full|inc|reset|noreset|system|nosystem|hidden|nohidden> + Changes tar's behavior with regard to DOS + attributes. There are 4 modes which can be turned on or + off. + + Incremental mode (default off). When off (using + full) tar will back up everything + regardless of the archive bit + setting. When on (using inc), tar will only + back up files with the archive bit set. + + Reset mode (default off). When on (using + reset), tar will remove the archive bit on + all files it backs up (implies read/write share). Use + noreset to turn off. + + System mode (default on). When off, tar will not backup + system files. Use nosystem to turn off. + + Hidden mode (default on). When off, tar will not backup + hidden files. Use nohidden to turn off. + @@ -1135,52 +1148,52 @@ on a valid NetBIOS name being used, so you need to supply a valid name that would be known to the server. - smbclient supports long file names where the server + smbclient supports long file names where the server supports the LANMAN2 protocol or above. ENVIRONMENT VARIABLES - The variable USER may contain the - username of the person using the client. This information is - used only if the protocol level is high enough to support + The variable USER may contain the + username of the person using the client. This information is + used only if the protocol level is high enough to support session-level passwords. - The variable PASSWD may contain - the password of the person using the client. This information is - used only if the protocol level is high enough to support + The variable PASSWD may contain + the password of the person using the client. This information is + used only if the protocol level is high enough to support session-level passwords. - The variable LIBSMB_PROG may contain - the path, executed with system(), which the client should connect - to instead of connecting to a server. This functionality is primarily - intended as a development aid, and works best when using a LMHOSTS - file + The variable LIBSMB_PROG may contain + the path, executed with system(), which the client should connect + to instead of connecting to a server. This functionality is primarily + intended as a development aid, and works best when using a LMHOSTS + file INSTALLATION - The location of the client program is a matter for + The location of the client program is a matter for individual system administrators. The following are thus suggestions only. It is recommended that the smbclient software be installed in the /usr/local/samba/bin/ or - /usr/samba/bin/ directory, this directory readable - by all, writeable only by root. The client program itself should - be executable by all. The client should NOT be + /usr/samba/bin/ directory, this directory readable + by all, writeable only by root. The client program itself should + be executable by all. The client should NOT be setuid or setgid! - The client log files should be put in a directory readable + The client log files should be put in a directory readable and writeable only by the user. - To test the client, you will need to know the name of a + To test the client, you will need to know the name of a running SMB/CIFS server. It is possible to run smbd - 8 as an ordinary user - running that server as a daemon + 8 as an ordinary user - running that server as a daemon on a user-accessible port (typically any port number over 1024) would provide a suitable test server. @@ -1189,12 +1202,12 @@ DIAGNOSTICS - Most diagnostics issued by the client are logged in a - specified log file. The log file name is specified at compile time, + Most diagnostics issued by the client are logged in a + specified log file. The log file name is specified at compile time, but may be overridden on the command line. - The number and nature of diagnostics available depends - on the debug level used by the client. If you have problems, + The number and nature of diagnostics available depends + on the debug level used by the client. If you have problems, set the debug level to 3 and peruse the log files. @@ -1208,17 +1221,17 @@ AUTHOR - - The original Samba software and related utilities + + The original Samba software and related utilities were created by Andrew Tridgell. Samba is now developed - by the Samba Team as an Open Source project similar + by the Samba Team as an Open Source project similar to the way the Linux kernel is developed. - - The original Samba man pages were written by Karl Auer. - The man page sources were converted to YODL format (another + + The original Samba man pages were written by Karl Auer. + The man page sources were converted to YODL format (another excellent piece of Open Source software, available at - ftp://ftp.icce.rug.nl/pub/unix/) and updated for the Samba 2.0 - release by Jeremy Allison. The conversion to DocBook for + ftp://ftp.icce.rug.nl/pub/unix/) and updated for the Samba 2.0 + release by Jeremy Allison. The conversion to DocBook for Samba 2.2 was done by Gerald Carter. The conversion to DocBook XML 4.2 for Samba 3.0 was done by Alexander Bokovoy. diff --git source3/client/client.c source3/client/client.c index afa5847..e04b4b2 100644 --- source3/client/client.c +++ source3/client/client.c @@ -26,6 +26,7 @@ #include "popt_common.h" #include "rpc_client/cli_pipe.h" #include "client/client_proto.h" +#include "client/clitar_proto.h" #include "../librpc/gen_ndr/ndr_srvsvc_c.h" #include "../lib/util/select.h" #include "system/readline.h" @@ -46,7 +47,6 @@ extern int do_smb_browse(void); /* mDNS browsing */ extern bool override_logfile; -extern char tar_type; static int port = 0; static char *service; @@ -75,12 +75,6 @@ static int archive_level = 0; static bool translation = false; static bool have_ip; -/* clitar bits insert */ -extern int blocksize; -extern bool tar_inc; -extern bool tar_reset; -/* clitar bits end */ - static bool prompt = true; static bool recurse = false; @@ -4642,7 +4636,7 @@ static struct { {COMPL_NONE,COMPL_NONE}}, {"altname",cmd_altname," show alt name",{COMPL_NONE,COMPL_NONE}}, {"archive",cmd_archive,"\n0=ignore archive bit\n1=only get archive files\n2=only get archive files and reset archive bit\n3=get all files and reset archive bit",{COMPL_NONE,COMPL_NONE}}, - {"backup",cmd_backup,"toggle backup intent state",{COMPL_NONE,COMPL_NONE}}, + {"backup",cmd_backup,"toggle backup intent state",{COMPL_NONE,COMPL_NONE}}, {"blocksize",cmd_block,"blocksize (default 20)",{COMPL_NONE,COMPL_NONE}}, {"cancel",cmd_cancel," cancel a print queue entry",{COMPL_NONE,COMPL_NONE}}, {"case_sensitive",cmd_setcase,"toggle the case sensitive flag to server",{COMPL_NONE,COMPL_NONE}}, @@ -4666,14 +4660,14 @@ static struct { {"lcd",cmd_lcd,"[directory] change/report the local current working directory",{COMPL_LOCAL,COMPL_NONE}}, {"link",cmd_link," create a UNIX hard link",{COMPL_REMOTE,COMPL_REMOTE}}, {"lock",cmd_lock,"lock [r|w] : set a POSIX lock",{COMPL_REMOTE,COMPL_REMOTE}}, - {"lowercase",cmd_lowercase,"toggle lowercasing of filenames for get",{COMPL_NONE,COMPL_NONE}}, + {"lowercase",cmd_lowercase,"toggle lowercasing of filenames for get",{COMPL_NONE,COMPL_NONE}}, {"ls",cmd_dir," list the contents of the current directory",{COMPL_REMOTE,COMPL_NONE}}, {"l",cmd_dir," list the contents of the current directory",{COMPL_REMOTE,COMPL_NONE}}, {"mask",cmd_select," mask all filenames against this",{COMPL_REMOTE,COMPL_NONE}}, {"md",cmd_mkdir," make a directory",{COMPL_NONE,COMPL_NONE}}, {"mget",cmd_mget," get all the matching files",{COMPL_REMOTE,COMPL_NONE}}, {"mkdir",cmd_mkdir," make a directory",{COMPL_NONE,COMPL_NONE}}, - {"more",cmd_more," view a remote file with your pager",{COMPL_REMOTE,COMPL_NONE}}, + {"more",cmd_more," view a remote file with your pager",{COMPL_REMOTE,COMPL_NONE}}, {"mput",cmd_mput," put all matching files",{COMPL_REMOTE,COMPL_NONE}}, {"newer",cmd_newer," only mget files newer than the specified local file",{COMPL_LOCAL,COMPL_NONE}}, {"notify",cmd_notify,"Get notified of dir changes",{COMPL_REMOTE,COMPL_NONE}}, @@ -4685,7 +4679,7 @@ static struct { {"posix_rmdir",cmd_posix_rmdir," removes a directory using POSIX interface",{COMPL_REMOTE,COMPL_NONE}}, {"posix_unlink",cmd_posix_unlink," removes a file using POSIX interface",{COMPL_REMOTE,COMPL_NONE}}, {"print",cmd_print," print a file",{COMPL_NONE,COMPL_NONE}}, - {"prompt",cmd_prompt,"toggle prompting for filenames for mget and mput",{COMPL_NONE,COMPL_NONE}}, + {"prompt",cmd_prompt,"toggle prompting for filenames for mget and mput",{COMPL_NONE,COMPL_NONE}}, {"put",cmd_put," [remote name] put a file",{COMPL_LOCAL,COMPL_REMOTE}}, {"pwd",cmd_pwd,"show current remote directory (same as 'cd' with no args)",{COMPL_NONE,COMPL_NONE}}, {"q",cmd_quit,"logoff the server",{COMPL_NONE,COMPL_NONE}}, @@ -4693,13 +4687,13 @@ static struct { {"quit",cmd_quit,"logoff the server",{COMPL_NONE,COMPL_NONE}}, {"readlink",cmd_readlink,"filename Do a UNIX extensions readlink call on a symlink",{COMPL_REMOTE,COMPL_REMOTE}}, {"rd",cmd_rmdir," remove a directory",{COMPL_NONE,COMPL_NONE}}, - {"recurse",cmd_recurse,"toggle directory recursion for mget and mput",{COMPL_NONE,COMPL_NONE}}, + {"recurse",cmd_recurse,"toggle directory recursion for mget and mput",{COMPL_NONE,COMPL_NONE}}, {"reget",cmd_reget," [local name] get a file restarting at end of local file",{COMPL_REMOTE,COMPL_LOCAL}}, {"rename",cmd_rename," rename some files",{COMPL_REMOTE,COMPL_REMOTE}}, {"reput",cmd_reput," [remote name] put a file restarting at end of remote file",{COMPL_LOCAL,COMPL_REMOTE}}, {"rm",cmd_del," delete all matching files",{COMPL_REMOTE,COMPL_NONE}}, {"rmdir",cmd_rmdir," remove a directory",{COMPL_REMOTE,COMPL_NONE}}, - {"showacls",cmd_showacls,"toggle if ACLs are shown or not",{COMPL_NONE,COMPL_NONE}}, + {"showacls",cmd_showacls,"toggle if ACLs are shown or not",{COMPL_NONE,COMPL_NONE}}, {"setea", cmd_setea, " Set an EA of a file", {COMPL_REMOTE, COMPL_LOCAL}}, {"setmode",cmd_setmode," change modes of file",{COMPL_REMOTE,COMPL_NONE}}, @@ -5325,7 +5319,8 @@ static int do_host_query(const char *query_host) static int do_tar_op(const char *base_directory) { - int ret; + struct tar *tar_ctx = tar_get_ctx(); + int ret = 0; /* do we already have a connection? */ if (!cli) { @@ -5336,26 +5331,27 @@ static int do_tar_op(const char *base_directory) service, auth_info, true, smb_encrypt, max_protocol, port, name_type, &cli); if (!NT_STATUS_IS_OK(status)) { - return 1; + ret = 1; + goto out; } cli_set_timeout(cli, io_timeout*1000); } - recurse=true; + recurse = true; if (base_directory && *base_directory) { ret = do_cd(base_directory); if (ret) { - cli_shutdown(cli); - return ret; + goto out_cli; } } - ret=process_tar(); + ret = tar_process(tar_ctx); + out_cli: cli_shutdown(cli); - - return(ret); + out: + return ret; } /**************************************************************************** @@ -5385,7 +5381,7 @@ static int do_message_op(struct user_auth_info *a_info) main program ****************************************************************************/ - int main(int argc,char *argv[]) +int main(int argc,char *argv[]) { const char **const_argv = discard_const_p(const char *, argv); char *base_directory = NULL; @@ -5398,6 +5394,8 @@ static int do_message_op(struct user_auth_info *a_info) int rc = 0; bool tar_opt = false; bool service_opt = false; + struct tar *tar_ctx = tar_get_ctx(); + struct poptOption long_options[] = { POPT_AUTOHELP @@ -5409,7 +5407,7 @@ static int do_message_op(struct user_auth_info *a_info) { "max-protocol", 'm', POPT_ARG_STRING, NULL, 'm', "Set the max protocol level", "LEVEL" }, { "tar", 'T', POPT_ARG_STRING, NULL, 'T', "Command line tar", "IXFqgbNan" }, { "directory", 'D', POPT_ARG_STRING, NULL, 'D', "Start from directory", "DIR" }, - { "command", 'c', POPT_ARG_STRING, &cmdstr, 'c', "Execute semicolon separated commands" }, + { "command", 'c', POPT_ARG_STRING, &cmdstr, 'c', "Execute semicolon separated commands" }, { "send-buffer", 'b', POPT_ARG_INT, &io_bufsize, 'b', "Changes the transmit/send buffer", "BYTES" }, { "timeout", 't', POPT_ARG_INT, &io_timeout, 'b', "Changes the per-operation timeout", "SECONDS" }, { "port", 'p', POPT_ARG_INT, &port, 'p', "Port to connect to", "PORT" }, @@ -5514,12 +5512,14 @@ static int do_message_op(struct user_auth_info *a_info) * position of the -T option in the raw argv[]. */ { int i; + for (i = 1; i < argc; i++) { if (strncmp("-T", argv[i],2)==0) break; } i++; - if (!tar_parseargs(argc, argv, poptGetOptArg(pc), i)) { + if (tar_parse_args(tar_ctx, poptGetOptArg(pc), + const_argv + i, argc - i)) { poptPrintUsage(pc, stderr, 0); exit(1); } @@ -5612,7 +5612,7 @@ static int do_message_op(struct user_auth_info *a_info) if(new_name_resolve_order) lp_set_cmdline("name resolve order", new_name_resolve_order); - if (!tar_type && !query_host && !service && !message) { + if (!tar_to_process(tar_ctx) && !query_host && !service && !message) { poptPrintUsage(pc, stderr, 0); exit(1); } @@ -5627,7 +5627,7 @@ static int do_message_op(struct user_auth_info *a_info) max_protocol = lp_cli_maxprotocol(); - if (tar_type) { + if (tar_to_process(tar_ctx)) { if (cmdstr) process_command_string(cmdstr); rc = do_tar_op(base_directory); diff --git source3/client/client_proto.h source3/client/client_proto.h index d119014..3e91ff0 100644 --- source3/client/client_proto.h +++ source3/client/client_proto.h @@ -38,15 +38,6 @@ NTSTATUS do_list(const char *mask, bool dirs); int cmd_iosize(void); -/* The following definitions come from client/clitar.c */ - -int cmd_block(void); -int cmd_tarmode(void); -int cmd_setmode(void); -int cmd_tar(void); -int process_tar(void); -int tar_parseargs(int argc, char *argv[], const char *Optarg, int Optind); - /* The following definitions come from client/dnsbrowse.c */ int do_smb_browse(void); diff --git source3/client/clitar.c source3/client/clitar.c index 7bbd6ad..e4d391a 100644 --- source3/client/clitar.c +++ source3/client/clitar.c @@ -1,8 +1,7 @@ /* Unix SMB/CIFS implementation. - Tar Extensions - Copyright (C) Ricky Poulten 1995-1998 - Copyright (C) Richard Sharpe 1998 + Tar backup command extension + Copyright (C) Aurélien Aptel 2013 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 @@ -17,1972 +16,1954 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -/* The following changes developed by Richard Sharpe for Canon Information - Systems Research Australia (CISRA) - - 1. Restore can now restore files with long file names - 2. Save now saves directory information so that we can restore - directory creation times - 3. tar now accepts both UNIX path names and DOS path names. I prefer - those lovely /'s to those UGLY \'s :-) - 4. the files to exclude can be specified as a regular expression by adding - an r flag to the other tar flags. Eg: - - -TcrX file.tar "*.(obj|exe)" - - will skip all .obj and .exe files -*/ +/** + * # General overview of the tar extension + * + * All tar_xxx() functions work on a `struct tar` which store most of + * the context of the backup process. + * + * The current tar context can be accessed via the global variable + * `tar_ctx`. It's publicly exported as an opaque handle via + * tar_get_ctx(). + * + * A tar context is first configured through tar_parse_args() which + * can be called from either the CLI (in client.c) or the interactive + * session (via the cmd_tar() callback). + * + * Once the configuration is done (successfully), the context is ready + * for processing and tar_to_process() returns true. + * + * The next step is to call tar_process() which dispatch the + * processing to either tar_create() or tar_extract(), depending on + * the context. + * + * ## Archive creation + * + * tar_create() creates an archive using the libarchive API then + * + * - iterates on the requested paths if the context is in inclusion + * mode with tar_create_from_list() + * + * - or iterates on the whole share (starting from the current dir) if + * in exclusion mode or if no specific path were requested + * + * The do_list() function from client.c is used to list recursively + * the share. In particular it takes a DOS path mask (eg. \mydir\*) + * and a callback function which will be called with each file name + * and attributes. The tar callback function is get_file_callback(). + * + * The callback function checks whether the file should be skipped + * according the the configuration via tar_create_skip_path(). If it's + * not skipped it's downloaded and written to the archive in + * tar_get_file(). + * + * ## Archive extraction + * + * tar_extract() opens the archive and iterates on each file in + * it. For each file tar_extract_skip_path() checks whether it should + * be skipped according to the config. If it's not skipped it's + * uploaded on the server in tar_send_file(). + */ #include "includes.h" #include "system/filesys.h" -#include "clitar.h" #include "client/client_proto.h" +#include "client/clitar_proto.h" #include "libsmb/libsmb.h" -static int clipfind(char **aret, int ret, char *tok); - -typedef struct file_info_struct file_info2; - -struct file_info_struct { - off_t size; - uint16 mode; - uid_t uid; - gid_t gid; - /* These times are normally kept in GMT */ - struct timespec mtime_ts; - struct timespec atime_ts; - struct timespec ctime_ts; - char *name; /* This is dynamically allocated */ - file_info2 *next, *prev; /* Used in the stack ... */ -}; - -typedef struct { - file_info2 *top; - int items; -} stack; - -#define SEPARATORS " \t\n\r" -extern time_t newer_than; -extern struct cli_state *cli; - -/* These defines are for the do_setrattr routine, to indicate - * setting and reseting of file attributes in the function call */ -#define ATTRSET 1 -#define ATTRRESET 0 - -static uint16 attribute = FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN; - - -static char *tarbuf, *buffer_p; -static int tp, ntarf, tbufsiz; -static double ttarf; -/* Incremental mode */ -static bool tar_inc=False; -/* Reset archive bit */ -static bool tar_reset=False; -/* Include / exclude mode (true=include, false=exclude) */ -static bool tar_excl=True; -/* use regular expressions for search on file names */ -static bool tar_re_search=False; -/* Do not dump anything, just calculate sizes */ -static bool dry_run=False; -/* Dump files with System attribute */ -static bool tar_system=True; -/* Dump files with Hidden attribute */ -static bool tar_hidden=True; -/* Be noisy - make a catalogue */ -static bool tar_noisy=True; -static bool tar_real_noisy=False; /* Don't want to be really noisy by default */ - -char tar_type='\0'; -static char **cliplist=NULL; -static int clipn=0; -static bool must_free_cliplist = False; -extern char *cmd_ptr; - -extern bool lowercase; -extern int get_total_time_ms; -extern int get_total_size; - -static int blocksize=20; -static int tarhandle; - -static void writetarheader(int f, const char *aname, uint64_t size, time_t mtime, - const char *amode, unsigned char ftype); -static NTSTATUS do_atar(const char *rname_in, char *lname, - struct file_info *finfo1); -static NTSTATUS do_tar(struct cli_state *cli_state, struct file_info *finfo, - const char *dir); -static void oct_it(uint64_t value, int ndgs, char *p); -static void fixtarname(char *tptr, const char *fp, size_t l); -static int dotarbuf(int f, char *b, int n); -static void dozerobuf(int f, int n); -static void dotareof(int f); -static void initarbuf(void); - -/* restore functions */ -static long readtarheader(union hblock *hb, file_info2 *finfo, const char *prefix); -static long unoct(char *p, int ndgs); -static void do_tarput(void); -static void unfixtarname(char *tptr, char *fp, int l, bool first); +#ifdef HAVE_LIBARCHIVE -/* - * tar specific utitlities - */ +#include +#include -/**************************************************************************** -Write a tar header to buffer -****************************************************************************/ - -static void writetarheader(int f, const char *aname, uint64_t size, time_t mtime, - const char *amode, unsigned char ftype) -{ - union hblock hb; - int i, chk, l; - char *jp; +/* prepend module name and line number to debug messages */ +#define DBG(a, b) (DEBUG(a, ("tar:%-4d ", __LINE__)), DEBUG(a, b)) - DEBUG(5, ("WriteTarHdr, Type = %c, Size= %.0f, Name = %s\n", ftype, (double)size, aname)); +/* preprocessor magic to stringify __LINE__ (int) */ +#define STR1(x) #x +#define STR2(x) STR1(x) - memset(hb.dummy, 0, sizeof(hb.dummy)); +/** + * Number of byte in a block unit. + */ +#define TAR_BLOCK_UNIT 512 - l=strlen(aname); - /* We will be prepending a '.' in fixtarheader so use +2 to - * take care of the . and terminating zero. JRA. - */ - if (l+2 >= NAMSIZ) { - /* write a GNU tar style long header */ - char *b; - b = (char *)SMB_MALLOC(l+TBLOCK+100); - if (!b) { - DEBUG(0,("out of memory\n")); - exit(1); - } - writetarheader(f, "/./@LongLink", l+2, 0, " 0 \0", 'L'); - memset(b, 0, l+TBLOCK+100); - fixtarname(b, aname, l+2); - i = strlen(b)+1; - DEBUG(5, ("File name in tar file: %s, size=%d, \n", b, (int)strlen(b))); - dotarbuf(f, b, TBLOCK*(((i-1)/TBLOCK)+1)); - SAFE_FREE(b); - } - - fixtarname(hb.dbuf.name, aname, (l+2 >= NAMSIZ) ? NAMSIZ : l + 2); - - if (lowercase) - (void)strlower_m(hb.dbuf.name); - - /* write out a "standard" tar format header */ - - hb.dbuf.name[NAMSIZ-1]='\0'; - strlcpy(hb.dbuf.mode, amode ? amode : "", sizeof(hb.dbuf.mode)); - oct_it((uint64_t)0, 8, hb.dbuf.uid); - oct_it((uint64_t)0, 8, hb.dbuf.gid); - oct_it((uint64_t) size, 13, hb.dbuf.size); - if (size > (uint64_t)077777777777LL) { - /* This is a non-POSIX compatible extention to store files - greater than 8GB. */ - - memset(hb.dbuf.size, 0, 4); - hb.dbuf.size[0]=128; - for (i = 8; i; i--) { - hb.dbuf.size[i+3] = size & 0xff; - size >>= 8; - } - } - oct_it((uint64_t) mtime, 13, hb.dbuf.mtime); - memcpy(hb.dbuf.chksum, " ", sizeof(hb.dbuf.chksum)); - memset(hb.dbuf.linkname, 0, NAMSIZ); - hb.dbuf.linkflag=ftype; +/** + * Default tar block size in TAR_BLOCK_UNIT. + */ +#define TAR_DEFAULT_BLOCK_SIZE 20 - for (chk=0, i=sizeof(hb.dummy), jp=hb.dummy; --i>=0;) - chk+=(0xFF & *jp++); +/** + * Maximum value for the blocksize field + */ +#define TAR_MAX_BLOCK_SIZE 0xffff - oct_it((uint64_t) chk, 8, hb.dbuf.chksum); - hb.dbuf.chksum[6] = '\0'; +/** + * Size of the buffer used when downloading a file + */ +#define TAR_CLI_READ_SIZE 0xff00 - (void) dotarbuf(f, hb.dummy, sizeof(hb.dummy)); -} +#define TAR_DO_LIST_ATTR (FILE_ATTRIBUTE_DIRECTORY \ + | FILE_ATTRIBUTE_SYSTEM \ + | FILE_ATTRIBUTE_HIDDEN) -/**************************************************************************** -Read a tar header into a hblock structure, and validate -***************************************************************************/ -static long readtarheader(union hblock *hb, file_info2 *finfo, const char *prefix) -{ - long chk, fchk; - int i; - char *jp; +enum tar_operation { + TAR_NO_OPERATION, + TAR_CREATE, /* c flag */ + TAR_EXTRACT, /* x flag */ +}; - /* - * read in a "standard" tar format header - we're not that interested - * in that many fields, though - */ +enum tar_selection { + TAR_NO_SELECTION, + TAR_INCLUDE, /* I and F flag, default */ + TAR_EXCLUDE, /* X flag */ +}; - /* check the checksum */ - for (chk=0, i=sizeof(hb->dummy), jp=hb->dummy; --i>=0;) - chk+=(0xFF & *jp++); +enum { + ATTR_UNSET, + ATTR_SET, +}; - if (chk == 0) - return chk; +struct tar { + TALLOC_CTX *talloc_ctx; + + /* in state that needs/can be processed? */ + bool to_process; + + /* flags */ + struct tar_mode { + enum tar_operation operation; /* create, extract */ + enum tar_selection selection; /* include, exclude */ + int blocksize; /* size in TAR_BLOCK_UNIT of a tar file block */ + bool hidden; /* backup hidden file? */ + bool system; /* backup system file? */ + bool incremental; /* backup _only_ archived file? */ + bool reset; /* unset archive bit? */ + bool dry; /* don't write tar file? */ + bool regex; /* XXX: never actually using regex... */ + bool verbose; /* XXX: ignored */ + } mode; + + /* nb of bytes received */ + uint64_t total_size; + + /* path to tar archive name */ + char *tar_path; + + /* list of path to include or exclude */ + char **path_list; + int path_list_size; + + /* archive handle */ + struct archive *archive; +}; - /* compensate for blanks in chksum header */ - for (i=sizeof(hb->dbuf.chksum), jp=hb->dbuf.chksum; --i>=0;) - chk-=(0xFF & *jp++); +/** + * Global context imported in client.c when needed. + * + * Default options. + */ +struct tar tar_ctx = { + .mode.selection = TAR_INCLUDE, + .mode.blocksize = TAR_DEFAULT_BLOCK_SIZE, + .mode.hidden = true, + .mode.system = true, + .mode.incremental = false, + .mode.reset = false, + .mode.dry = false, + .mode.regex = false, + .mode.verbose = false, +}; - chk += ' ' * sizeof(hb->dbuf.chksum); +/* tar, local function */ +static int tar_create(struct tar* t); +static int tar_create_from_list(struct tar *t); +static int tar_extract(struct tar *t); +static int tar_read_inclusion_file(struct tar *t, const char* filename); +static int tar_send_file(struct tar *t, struct archive_entry *entry); +static int tar_set_blocksize(struct tar *t, int size); +static int tar_set_newer_than(struct tar *t, const char *filename); +static NTSTATUS tar_add_selection_path(struct tar *t, const char *path); +static void tar_dump(struct tar *t); +static NTSTATUS tar_extract_skip_path(struct tar *t, + struct archive_entry *entry, + bool *_skip); +static TALLOC_CTX *tar_reset_mem_context(struct tar *t); +static void tar_free_mem_context(struct tar *t); +static NTSTATUS tar_create_skip_path(struct tar *t, + const char *fullpath, + const struct file_info *finfo, + bool *_skip); + +static NTSTATUS tar_path_in_list(struct tar *t, const char *path, + bool reverse, bool *_is_in_list); + +static int tar_get_file(struct tar *t, + const char *full_dos_path, + struct file_info *finfo); + +static NTSTATUS get_file_callback(struct cli_state *cli, + struct file_info *finfo, + const char *dir); + +/* utilities */ +static char *fix_unix_path(char *path, bool removeprefix); +static NTSTATUS path_base_name(TALLOC_CTX *ctx, const char *path, char **_base); +static const char* skip_useless_char_in_path(const char *p); +static int make_remote_path(const char *full_path); +static int max_token (const char *str); +static NTSTATUS is_subpath(const char *sub, const char *full, + bool *_subpath_match); +static int set_remote_attr(const char *filename, uint16 new_attr, int mode); - fchk=unoct(hb->dbuf.chksum, sizeof(hb->dbuf.chksum)); +/** + * tar_get_ctx - retrieve global tar context handle + */ +struct tar *tar_get_ctx() +{ + return &tar_ctx; +} - DEBUG(5, ("checksum totals chk=%ld fchk=%ld chksum=%s\n", - chk, fchk, hb->dbuf.chksum)); +/** + * cmd_block - interactive command to change tar blocksize + * + * Read a size from the client command line and update the current + * blocksize. + */ +int cmd_block(void) +{ + /* XXX: from client.c */ + const extern char *cmd_ptr; + char *buf; + int err = 0; + bool ok; + TALLOC_CTX *ctx = talloc_new(NULL); + if (ctx == NULL) { + return 1; + } - if (fchk != chk) { - DEBUG(0, ("checksums don't match %ld %ld\n", fchk, chk)); - dump_data(5, (uint8 *)hb - TBLOCK, TBLOCK *3); - return -1; + ok = next_token_talloc(ctx, &cmd_ptr, &buf, NULL); + if (!ok) { + DBG(0, ("blocksize \n")); + err = 1; + goto out; } - if ((finfo->name = SMB_MALLOC(strlen(prefix) + strlen(hb -> dbuf.name) + 4)) == NULL) { - DEBUG(0, ("Out of space creating file_info2 for %s\n", hb -> dbuf.name)); - return(-1); + ok = tar_set_blocksize(&tar_ctx, atoi(buf)); + if (ok) { + DBG(0, ("invalid blocksize\n")); + err = 1; + goto out; } - strlcpy(finfo->name, prefix, strlen(prefix) + strlen(hb -> dbuf.name) + 4); + DBG(2, ("blocksize is now %d\n", tar_ctx.mode.blocksize)); + +out: + talloc_free(ctx); + return err; +} - /* use l + 1 to do the null too; do prefix - prefcnt to zap leading slash */ - unfixtarname(finfo->name + strlen(prefix), hb->dbuf.name, - strlen(hb->dbuf.name) + 1, True); +/** + * cmd_tarmode - interactive command to change tar behaviour + * + * Read one or more modes from the client command line and update the + * current tar mode. + */ +int cmd_tarmode(void) +{ + const extern char *cmd_ptr; + char *buf; + int i; + TALLOC_CTX *ctx; + + struct { + const char *cmd; + bool *p; + bool value; + } table[] = { + {"full", &tar_ctx.mode.incremental, false}, + {"inc", &tar_ctx.mode.incremental, true }, + {"reset", &tar_ctx.mode.reset, true }, + {"noreset", &tar_ctx.mode.reset, false}, + {"system", &tar_ctx.mode.system, true }, + {"nosystem", &tar_ctx.mode.system, false}, + {"hidden", &tar_ctx.mode.hidden, true }, + {"nohidden", &tar_ctx.mode.hidden, false}, + {"verbose", &tar_ctx.mode.verbose, true }, + {"noquiet", &tar_ctx.mode.verbose, true }, + {"quiet", &tar_ctx.mode.verbose, false}, + {"noverbose", &tar_ctx.mode.verbose, false}, + }; + + ctx = talloc_new(NULL); + if (ctx == NULL) { + return 1; + } - /* can't handle some links at present */ - if ((hb->dbuf.linkflag != '0') && (hb -> dbuf.linkflag != '5')) { - if (hb->dbuf.linkflag == 0) { - DEBUG(6, ("Warning: NULL link flag (gnu tar archive ?) %s\n", - finfo->name)); - } else { - if (hb -> dbuf.linkflag == 'L') { /* We have a longlink */ - /* Do nothing here at the moment. do_tarput will handle this - as long as the longlink gets back to it, as it has to advance - the buffer pointer, etc */ - } else { - DEBUG(0, ("this tar file appears to contain some kind \ -of link other than a GNUtar Longlink - ignoring\n")); - return -2; + while (next_token_talloc(ctx, &cmd_ptr, &buf, NULL)) { + for (i = 0; i < ARRAY_SIZE(table); i++) { + if (strequal(table[i].cmd, buf)) { + *table[i].p = table[i].value; + break; } } - } - if ((unoct(hb->dbuf.mode, sizeof(hb->dbuf.mode)) & S_IFDIR) || - (*(finfo->name+strlen(finfo->name)-1) == '\\')) { - finfo->mode=FILE_ATTRIBUTE_DIRECTORY; - } else { - finfo->mode=0; /* we don't care about mode at the moment, we'll - * just make it a regular file */ + if (i == ARRAY_SIZE(table)) + DBG(0, ("tarmode: unrecognised option %s\n", buf)); } - /* - * Bug fix by richard@sj.co.uk - * - * REC: restore times correctly (as does tar) - * We only get the modification time of the file; set the creation time - * from the mod. time, and the access time to current time - */ - finfo->mtime_ts = finfo->ctime_ts = - convert_time_t_to_timespec((time_t)strtol(hb->dbuf.mtime, NULL, 8)); - finfo->atime_ts = convert_time_t_to_timespec(time(NULL)); - if ((hb->dbuf.size[0] & 0xff) == 0x80) { - /* This is a non-POSIX compatible extention to extract files - greater than 8GB. */ - finfo->size = 0; - for (i = 0; i < 8; i++) { - finfo->size <<= 8; - finfo->size |= hb->dbuf.size[i+4] & 0xff; - } - } else { - finfo->size = unoct(hb->dbuf.size, sizeof(hb->dbuf.size)); - } + DBG(0, ("tarmode is now %s, %s, %s, %s, %s\n", + tar_ctx.mode.incremental ? "incremental" : "full", + tar_ctx.mode.system ? "system" : "nosystem", + tar_ctx.mode.hidden ? "hidden" : "nohidden", + tar_ctx.mode.reset ? "reset" : "noreset", + tar_ctx.mode.verbose ? "verbose" : "quiet")); - return True; + talloc_free(ctx); + return 0; } -/**************************************************************************** -Write out the tar buffer to tape or wherever -****************************************************************************/ - -static int dotarbuf(int f, char *b, int n) +/** + * cmd_tar - interactive command to start a tar backup/restoration + * + * Check presence of argument, parse them and handle the request. + */ +int cmd_tar(void) { - int fail=1, writ=n; - - if (dry_run) { - return writ; - } - /* This routine and the next one should be the only ones that do write()s */ - if (tp + n >= tbufsiz) { - int diff; - - diff=tbufsiz-tp; - memcpy(tarbuf + tp, b, diff); - fail=fail && (1+sys_write(f, tarbuf, tbufsiz)); - n-=diff; - b+=diff; - tp=0; - - while (n >= tbufsiz) { - fail=fail && (1 + sys_write(f, b, tbufsiz)); - n-=tbufsiz; - b+=tbufsiz; - } + const extern char *cmd_ptr; + const char *flag; + const char **val; + char *buf; + int maxtok = max_token(cmd_ptr); + int i = 0; + int err = 0; + bool ok; + int rc; + TALLOC_CTX *ctx = talloc_new(NULL); + if (ctx == NULL) { + return 1; } - if (n>0) { - memcpy(tarbuf+tp, b, n); - tp+=n; + ok = next_token_talloc(ctx, &cmd_ptr, &buf, NULL); + if (!ok) { + DBG(0, ("tar [IXFbganN] [options] [path list]\n")); + err = 1; + goto out; } - return(fail ? writ : 0); -} - -/**************************************************************************** -Write zeros to buffer / tape -****************************************************************************/ + flag = buf; + val = talloc_array(ctx, const char *, maxtok); + if (val == NULL) { + err = 1; + goto out; + } -static void dozerobuf(int f, int n) -{ - /* short routine just to write out n zeros to buffer - - * used to round files to nearest block - * and to do tar EOFs */ - - if (dry_run) - return; - - if (n+tp >= tbufsiz) { - memset(tarbuf+tp, 0, tbufsiz-tp); - if (sys_write(f, tarbuf, tbufsiz) != tbufsiz) { - DEBUG(0, ("dozerobuf: sys_write fail\n")); - return; - } - memset(tarbuf, 0, (tp+=n-tbufsiz)); - } else { - memset(tarbuf+tp, 0, n); - tp+=n; + while (next_token_talloc(ctx, &cmd_ptr, &buf, NULL)) { + val[i++] = buf; } -} -/**************************************************************************** -Malloc tape buffer -****************************************************************************/ + rc = tar_parse_args(&tar_ctx, flag, val, i); + if (rc != 0) { + DBG(0, ("parse_args failed\n")); + err = 1; + goto out; + } -static void initarbuf(void) -{ - /* initialize tar buffer */ - tbufsiz=blocksize*TBLOCK; - tarbuf=(char *)SMB_MALLOC(tbufsiz); /* FIXME: We might not get the buffer */ + rc = tar_process(&tar_ctx); + if (rc != 0) { + DBG(0, ("tar_process failed\n")); + err = 1; + goto out; + } - /* reset tar buffer pointer and tar file counter and total dumped */ - tp=0; ntarf=0; ttarf=0; +out: + talloc_free(ctx); + return err; } -/**************************************************************************** -Write two zero blocks at end of file -****************************************************************************/ - -static void dotareof(int f) +/** + * cmd_setmode - interactive command to set DOS attributes + * + * Read a filename and mode from the client command line and update + * the file DOS attributes. + */ +int cmd_setmode(void) { - SMB_STRUCT_STAT stbuf; - /* Two zero blocks at end of file, write out full buffer */ - - if (dry_run) - return; + const extern char *cmd_ptr; + char *buf; + char *fname = NULL; + uint16 attr[2] = {0}; + int mode = ATTR_SET; + int err = 0; + bool ok; + TALLOC_CTX *ctx = talloc_new(NULL); + if (ctx == NULL) { + return 1; + } - (void) dozerobuf(f, TBLOCK); - (void) dozerobuf(f, TBLOCK); + ok = next_token_talloc(ctx, &cmd_ptr, &buf, NULL); + if (!ok) { + DBG(0, ("setmode <[+|-]rsha>\n")); + err = 1; + goto out; + } - if (sys_fstat(f, &stbuf, false) == -1) { - DEBUG(0, ("Couldn't stat file handle\n")); - return; + fname = talloc_asprintf(ctx, + "%s%s", + client_get_cur_dir(), + buf); + if (fname == NULL) { + err = 1; + goto out; } - /* Could be a pipe, in which case S_ISREG should fail, - * and we should write out at full size */ - if (tp > 0) { - size_t towrite = S_ISREG(stbuf.st_ex_mode) ? tp : tbufsiz; - if (sys_write(f, tarbuf, towrite) != towrite) { - DEBUG(0,("dotareof: sys_write fail\n")); + while (next_token_talloc(ctx, &cmd_ptr, &buf, NULL)) { + const char *s = buf; + + while (*s) { + switch (*s++) { + case '+': + mode = ATTR_SET; + break; + case '-': + mode = ATTR_UNSET; + break; + case 'r': + attr[mode] |= FILE_ATTRIBUTE_READONLY; + break; + case 'h': + attr[mode] |= FILE_ATTRIBUTE_HIDDEN; + break; + case 's': + attr[mode] |= FILE_ATTRIBUTE_SYSTEM; + break; + case 'a': + attr[mode] |= FILE_ATTRIBUTE_ARCHIVE; + break; + default: + DBG(0, ("setmode \n")); + err = 1; + goto out; + } } } -} -/**************************************************************************** -(Un)mangle DOS pathname, make nonabsolute -****************************************************************************/ + if (attr[ATTR_SET] == 0 && attr[ATTR_UNSET] == 0) { + DBG(0, ("setmode <[+|-]rsha>\n")); + err = 1; + goto out; + } -static void fixtarname(char *tptr, const char *fp, size_t l) -{ - /* add a '.' to start of file name, convert from ugly dos \'s in path - * to lovely unix /'s :-} */ - *tptr++='.'; - l--; + DBG(2, ("perm set %d %d\n", attr[ATTR_SET], attr[ATTR_UNSET])); - StrnCpy(tptr, fp, l-1); - string_replace(tptr, '\\', '/'); + /* ignore return value: server might not store DOS attributes */ + set_remote_attr(fname, attr[ATTR_SET], ATTR_SET); + set_remote_attr(fname, attr[ATTR_UNSET], ATTR_UNSET); +out: + talloc_free(ctx); + return err; } -/**************************************************************************** -Convert from decimal to octal string -****************************************************************************/ - -static void oct_it (uint64_t value, int ndgs, char *p) +/** + * tar_parse_args - parse and set tar command line arguments + * @flag: string pointing to tar options + * @val: number of tar arguments + * @valsize: table of arguments after the flags (number of element in val) + * + * tar arguments work in a weird way. For each flag f that takes a + * value v, the user is supposed to type: + * + * on the CLI: + * -Tf1f2f3 v1 v2 v3 TARFILE PATHS... + * + * in the interactive session: + * tar f1f2f3 v1 v2 v3 TARFILE PATHS... + * + * @flag has only flags (eg. "f1f2f3") and @val has the arguments + * (values) following them (eg. ["v1", "v2", "v3", "TARFILE", "PATH1", + * "PATH2"]). + * + * There are only 2 flags that take an arg: b and N. The other flags + * just change the semantic of PATH or TARFILE. + * + * PATH can be a list of included/excluded paths, the path to a file + * containing a list of included/excluded paths to use (F flag). If no + * PATH is provided, the whole share is used (/). + */ +int tar_parse_args(struct tar* t, + const char *flag, + const char **val, + int valsize) { - /* Converts long to octal string, pads with leading zeros */ - - /* skip final null, but do final space */ - --ndgs; - p[--ndgs] = ' '; - - /* Loop does at least one digit */ - do { - p[--ndgs] = '0' + (char) (value & 7); - value >>= 3; - } while (ndgs > 0 && value != 0); - - /* Do leading zeros */ - while (ndgs > 0) - p[--ndgs] = '0'; -} - -/**************************************************************************** -Convert from octal string to long -***************************************************************************/ + TALLOC_CTX *ctx; + bool do_read_list = false; + /* index of next value to use */ + int ival = 0; + int rc; + + if (t == NULL) { + DBG(0, ("Invalid tar context\n")); + return 1; + } -static long unoct(char *p, int ndgs) -{ - long value=0; - /* Converts octal string to long, ignoring any non-digit */ + ctx = tar_reset_mem_context(t); + if (ctx == NULL) { + return 1; + } + /* + * Reset back some options - could be from interactive version + * all other modes are left as they are + */ + t->mode.operation = TAR_NO_OPERATION; + t->mode.selection = TAR_NO_SELECTION; + t->mode.dry = false; + t->to_process = false; + t->total_size = 0; + + while (flag[0] != '\0') { + switch(flag[0]) { + /* operation */ + case 'c': + if (t->mode.operation != TAR_NO_OPERATION) { + printf("Tar must be followed by only one of c or x.\n"); + return 1; + } + t->mode.operation = TAR_CREATE; + break; + case 'x': + if (t->mode.operation != TAR_NO_OPERATION) { + printf("Tar must be followed by only one of c or x.\n"); + return 1; + } + t->mode.operation = TAR_EXTRACT; + break; - while (--ndgs) { - if (isdigit((int)*p)) - value = (value << 3) | (long) (*p - '0'); + /* selection */ + case 'I': + if (t->mode.selection != TAR_NO_SELECTION) { + DBG(0,("Only one of I,X,F must be specified\n")); + return 1; + } + t->mode.selection = TAR_INCLUDE; + break; + case 'X': + if (t->mode.selection != TAR_NO_SELECTION) { + DBG(0,("Only one of I,X,F must be specified\n")); + return 1; + } + t->mode.selection = TAR_EXCLUDE; + break; + case 'F': + if (t->mode.selection != TAR_NO_SELECTION) { + DBG(0,("Only one of I,X,F must be specified\n")); + return 1; + } + t->mode.selection = TAR_INCLUDE; + do_read_list = true; + break; - p++; - } + /* blocksize */ + case 'b': + if (ival >= valsize) { + DBG(0, ("Option b must be followed by a blocksize\n")); + return 1; + } - return value; -} + if (tar_set_blocksize(t, atoi(val[ival]))) { + DBG(0, ("Option b must be followed by a valid blocksize\n")); + return 1; + } -/**************************************************************************** -Compare two strings in a slash insensitive way, allowing s1 to match s2 -if s1 is an "initial" string (up to directory marker). Thus, if s2 is -a file in any subdirectory of s1, declare a match. -***************************************************************************/ + ival++; + break; -static int strslashcmp(char *s1, char *s2) -{ - char *s1_0=s1; + /* incremental mode */ + case 'g': + t->mode.incremental = true; + break; - while(*s1 && *s2 && (*s1 == *s2 || tolower_m(*s1) == tolower_m(*s2) || - (*s1 == '\\' && *s2=='/') || (*s1 == '/' && *s2=='\\'))) { - s1++; s2++; - } + /* newer than */ + case 'N': + if (ival >= valsize) { + DBG(0, ("Option N must be followed by valid file name\n")); + return 1; + } - /* if s1 has a trailing slash, it compared equal, so s1 is an "initial" - string of s2. - */ - if (!*s1 && s1 != s1_0 && (*(s1-1) == '/' || *(s1-1) == '\\')) - return 0; + if (tar_set_newer_than(t, val[ival])) { + DBG(0,("Error setting newer-than time\n")); + return 1; + } - /* ignore trailing slash on s1 */ - if (!*s2 && (*s1 == '/' || *s1 == '\\') && !*(s1+1)) - return 0; + ival++; + break; - /* check for s1 is an "initial" string of s2 */ - if ((*s2 == '/' || *s2 == '\\') && !*s1) - return 0; + /* reset mode */ + case 'a': + t->mode.reset = true; + break; - return *s1-*s2; -} + /* verbose */ + case 'q': + t->mode.verbose = true; + break; -/**************************************************************************** -Ensure a remote path exists (make if necessary) -***************************************************************************/ + /* regex match */ + case 'r': + t->mode.regex = true; + break; -static bool ensurepath(const char *fname) -{ - /* *must* be called with buffer ready malloc'ed */ - /* ensures path exists */ - - char *partpath, *ffname; - size_t fnamelen = strlen(fname)+1; - const char *p=fname; - char *basehack; - char *saveptr; - NTSTATUS status; + /* dry run mode */ + case 'n': + if (t->mode.operation != TAR_CREATE) { + DBG(0, ("n is only meaningful when creating a tar-file\n")); + return 1; + } - DEBUG(5, ( "Ensurepath called with: %s\n", fname)); + t->mode.dry = true; + DBG(0, ("dry_run set\n")); + break; - partpath = SMB_MALLOC(fnamelen); - ffname = SMB_MALLOC(fnamelen); + default: + DBG(0,("Unknown tar option\n")); + return 1; + } - if ((partpath == NULL) || (ffname == NULL)){ - DEBUG(0, ("Out of memory in ensurepath: %s\n", fname)); - SAFE_FREE(partpath); - SAFE_FREE(ffname); - return(False); + flag++; } - *partpath = 0; + /* no selection given? default selection is include */ + if (t->mode.selection == TAR_NO_SELECTION) { + t->mode.selection = TAR_INCLUDE; + } - /* fname copied to ffname so can strtok_r */ + if (valsize - ival < 1) { + DBG(0, ("No tar file given.\n")); + return 1; + } - strlcpy(ffname, fname, fnamelen); + /* handle TARFILE */ + t->tar_path = talloc_strdup(ctx, val[ival]); + if (t->tar_path == NULL) { + return 1; + } + ival++; - /* do a `basename' on ffname, so don't try and make file name directory */ - if ((basehack=strrchr_m(ffname, '\\')) == NULL) { - SAFE_FREE(partpath); - SAFE_FREE(ffname); - return True; - } else { - *basehack='\0'; + /* + * Make sure that dbf points to stderr if we are using stdout for + * tar output + */ + if (t->mode.operation == TAR_CREATE && strequal(t->tar_path, "-")) { + setup_logging("smbclient", DEBUG_STDERR); } - p=strtok_r(ffname, "\\", &saveptr); + /* handle PATHs... */ - while (p) { - strlcat(partpath, p, fnamelen); + /* flag F -> read file list */ + if (do_read_list) { + if (valsize - ival != 1) { + DBG(0,("Option F must be followed by exactly one filename.\n")); + return 1; + } - status = cli_chkpath(cli, partpath); - if (!NT_STATUS_IS_OK(status)) { - status = cli_mkdir(cli, partpath); + rc = tar_read_inclusion_file(t, val[ival]); + if (rc != 0) { + return 1; + } + ival++; + /* otherwise store all the PATHs on the command line */ + } else { + int i; + for (i = ival; i < valsize; i++) { + NTSTATUS status; + status = tar_add_selection_path(t, val[i]); if (!NT_STATUS_IS_OK(status)) { - SAFE_FREE(partpath); - SAFE_FREE(ffname); - DEBUG(0, ("Error mkdir %s\n", nt_errstr(status))); - return False; - } else { - DEBUG(3, ("mkdirhiering %s\n", partpath)); + return 1; } } - - strlcat(partpath, "\\", fnamelen); - p = strtok_r(NULL, "/\\", &saveptr); } - SAFE_FREE(partpath); - SAFE_FREE(ffname); - return True; + t->to_process = true; + tar_dump(t); + return 0; } -static int padit(char *buf, uint64_t bufsize, uint64_t padsize) +/** + * tar_process - start processing archive + * + * The talloc context of the fields is freed at the end of the call. + */ +int tar_process(struct tar *t) { - int berr= 0; - int bytestowrite; + int rc = 0; + + if (t == NULL) { + DBG(0, ("Invalid tar context\n")); + return 1; + } - DEBUG(5, ("Padding with %0.f zeros\n", (double)padsize)); - memset(buf, 0, (size_t)bufsize); - while( !berr && padsize > 0 ) { - bytestowrite= (int)MIN(bufsize, padsize); - berr = dotarbuf(tarhandle, buf, bytestowrite) != bytestowrite; - padsize -= bytestowrite; + switch(t->mode.operation) { + case TAR_EXTRACT: + rc = tar_extract(t); + break; + case TAR_CREATE: + rc = tar_create(t); + break; + default: + DBG(0, ("Invalid tar state\n")); + rc = 1; } - return berr; + t->to_process = false; + tar_free_mem_context(t); + DBG(5, ("tar_process done, err = %d\n", rc)); + return rc; } -static void do_setrattr(char *name, uint16 attr, int set) +/** + * tar_create - create archive and fetch files + */ +static int tar_create(struct tar* t) { - uint16 oldattr; + int r; + int err = 0; NTSTATUS status; + const char *mask; + TALLOC_CTX *ctx = talloc_new(NULL); + if (ctx == NULL) { + return 1; + } + + t->archive = archive_write_new(); + + if (!t->mode.dry) { + const int bsize = t->mode.blocksize * TAR_BLOCK_UNIT; + r = archive_write_set_bytes_per_block(t->archive, bsize); + if (r != ARCHIVE_OK) { + DBG(0, ("Can't use a block size of %d bytes", bsize)); + err = 1; + goto out; + } + + /* + * Use PAX restricted format which is not the most + * conservative choice but has useful extensions and is widely + * supported + */ + r = archive_write_set_format_pax_restricted(t->archive); + if (r != ARCHIVE_OK) { + DBG(0, ("Can't use pax restricted format: %s\n", + archive_error_string(t->archive))); + err = 1; + goto out; + } + + if (strequal(t->tar_path, "-")) { + r = archive_write_open_fd(t->archive, STDOUT_FILENO); + } else { + r = archive_write_open_filename(t->archive, t->tar_path); + } - if (!NT_STATUS_IS_OK(cli_getatr(cli, name, &oldattr, NULL, NULL))) { - return; + if (r != ARCHIVE_OK) { + DBG(0, ("Can't open %s: %s\n", t->tar_path, + archive_error_string(t->archive))); + err = 1; + goto out_close; + } } - if (set == ATTRSET) { - attr |= oldattr; + /* + * In inclusion mode, iterate on the inclusion list + */ + if (t->mode.selection == TAR_INCLUDE && t->path_list_size > 0) { + if (tar_create_from_list(t)) { + err = 1; + goto out_close; + } } else { - attr = oldattr & ~attr; + mask = talloc_asprintf(ctx, "%s\\*", client_get_cur_dir()); + if (mask == NULL) { + err = 1; + goto out_close; + } + DBG(5, ("tar_process do_list with mask: %s\n", mask)); + status = do_list(mask, TAR_DO_LIST_ATTR, get_file_callback, false, true); + if (!NT_STATUS_IS_OK(status)) { + DBG(0, ("do_list fail %s\n", nt_errstr(status))); + err = 1; + goto out_close; + } } - status = cli_setatr(cli, name, attr, 0); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(1, ("setatr failed: %s\n", nt_errstr(status))); +out_close: + DBG(0, ("Total bytes received: %" PRIu64 "\n", t->total_size)); + + if (!t->mode.dry) { + r = archive_write_close(t->archive); + if (r != ARCHIVE_OK) { + DBG(0, ("Fatal: %s\n", archive_error_string(t->archive))); + err = 1; + goto out; + } } +out: + archive_write_free(t->archive); + talloc_free(ctx); + return err; } -/**************************************************************************** -append one remote file to the tar file -***************************************************************************/ - -static NTSTATUS do_atar(const char *rname_in, char *lname, - struct file_info *finfo1) +/** + * tar_create_from_list - fetch from path list in include mode + */ +static int tar_create_from_list(struct tar *t) { - uint16_t fnum = (uint16_t)-1; - uint64_t nread=0; - char ftype; - file_info2 finfo; - bool shallitime=True; - char *data = NULL; - int read_size = 65520; - size_t datalen=0; - char *rname = NULL; - TALLOC_CTX *ctx = talloc_stackframe(); - NTSTATUS status = NT_STATUS_OK; - struct timespec tp_start; - - clock_gettime_mono(&tp_start); + int err = 0; + NTSTATUS status; + char *base; + const char *path, *mask, *start_dir; + int i; + TALLOC_CTX *ctx = talloc_new(NULL); + if (ctx == NULL) { + return 1; + } - data = SMB_MALLOC_ARRAY(char, read_size); - if (!data) { - DEBUG(0,("do_atar: out of memory.\n")); - status = NT_STATUS_NO_MEMORY; - goto cleanup; + start_dir = talloc_strdup(ctx, client_get_cur_dir()); + if (start_dir == NULL) { + err = 1; + goto out; } - ftype = '0'; /* An ordinary file ... */ + for (i = 0; i < t->path_list_size; i++) { + path = t->path_list[i]; + base = NULL; + status = path_base_name(ctx, path, &base); + if (!NT_STATUS_IS_OK(status)) { + err = 1; + goto out; + } + mask = talloc_asprintf(ctx, "%s\\%s", + client_get_cur_dir(), path); + if (mask == NULL) { + err = 1; + goto out; + } + + DBG(5, ("incl. path='%s', base='%s', mask='%s'\n", + path, base ? base : "NULL", mask)); - ZERO_STRUCT(finfo); + if (base != NULL) { + base = talloc_asprintf(ctx, "%s%s\\", + client_get_cur_dir(), base); + if (base == NULL) { + err = 1; + goto out; + } + DBG(5, ("cd '%s' before do_list\n", base)); + client_set_cur_dir(base); + } + status = do_list(mask, TAR_DO_LIST_ATTR, get_file_callback, false, true); + if (base != NULL) { + client_set_cur_dir(start_dir); + } + if (!NT_STATUS_IS_OK(status)) { + DBG(0, ("do_list failed on %s (%s)\n", path, nt_errstr(status))); + err = 1; + goto out; + } + } - finfo.size = finfo1 -> size; - finfo.mode = finfo1 -> mode; - finfo.uid = finfo1 -> uid; - finfo.gid = finfo1 -> gid; - finfo.mtime_ts = finfo1 -> mtime_ts; - finfo.atime_ts = finfo1 -> atime_ts; - finfo.ctime_ts = finfo1 -> ctime_ts; +out: + talloc_free(ctx); + return err; +} - if (dry_run) { - DEBUG(3,("skipping file %s of size %12.0f bytes\n", finfo1->name, - (double)finfo.size)); - shallitime=0; - ttarf+=finfo.size + TBLOCK - (finfo.size % TBLOCK); - ntarf++; - goto cleanup; +/** + * get_file_callback - do_list callback + * + * Callback for client.c do_list(). Called for each file found on the + * share matching do_list mask. Recursively call do_list() with itself + * as callback when the current file is a directory. + */ +static NTSTATUS get_file_callback(struct cli_state *cli, + struct file_info *finfo, + const char *dir) +{ + NTSTATUS status = NT_STATUS_OK; + char *remote_name; + const char *initial_dir = client_get_cur_dir(); + bool skip = false; + int rc; + TALLOC_CTX *ctx = talloc_new(NULL); + if (ctx == NULL) { + return NT_STATUS_NO_MEMORY; } - rname = clean_name(ctx, rname_in); - if (!rname) { + remote_name = talloc_asprintf(ctx, "%s%s", initial_dir, finfo->name); + if (remote_name == NULL) { status = NT_STATUS_NO_MEMORY; - goto cleanup; + goto out; } - status = cli_open(cli, rname, O_RDONLY, DENY_NONE, &fnum); + if (strequal(finfo->name, "..") || strequal(finfo->name, ".")) { + goto out; + } + + status = tar_create_skip_path(&tar_ctx, remote_name, finfo, &skip); if (!NT_STATUS_IS_OK(status)) { - DEBUG(0,("%s opening remote file %s (%s)\n", - nt_errstr(status),rname, client_get_cur_dir())); - goto cleanup; + goto out; } - finfo.name = smb_xstrdup(rname); - if (finfo.name == NULL) { - DEBUG(0, ("Unable to allocate space for finfo.name in do_atar\n")); - status = NT_STATUS_NO_MEMORY; - goto cleanup; + if (skip) { + DBG(5, ("--- %s\n", remote_name)); + status = NT_STATUS_OK; + goto out; } - DEBUG(3,("file %s attrib 0x%X\n",finfo.name,finfo.mode)); + if (finfo->mode & FILE_ATTRIBUTE_DIRECTORY) { + char *old_dir; + char *new_dir; + char *mask; + + old_dir = talloc_strdup(ctx, initial_dir); + new_dir = talloc_asprintf(ctx, "%s%s\\", + initial_dir, finfo->name); + if ((old_dir == NULL) || (new_dir == NULL)) { + status = NT_STATUS_NO_MEMORY; + goto out; + } + mask = talloc_asprintf(ctx, "%s*", new_dir); + if (mask == NULL) { + status = NT_STATUS_NO_MEMORY; + goto out; + } + + rc = tar_get_file(&tar_ctx, remote_name, finfo); + if (rc != 0) { + status = NT_STATUS_UNSUCCESSFUL; + goto out; + } - if (tar_inc && !(finfo.mode & FILE_ATTRIBUTE_ARCHIVE)) { - DEBUG(4, ("skipping %s - archive bit not set\n", finfo.name)); - shallitime=0; - } else if (!tar_system && (finfo.mode & FILE_ATTRIBUTE_SYSTEM)) { - DEBUG(4, ("skipping %s - system bit is set\n", finfo.name)); - shallitime=0; - } else if (!tar_hidden && (finfo.mode & FILE_ATTRIBUTE_HIDDEN)) { - DEBUG(4, ("skipping %s - hidden bit is set\n", finfo.name)); - shallitime=0; + client_set_cur_dir(new_dir); + do_list(mask, TAR_DO_LIST_ATTR, get_file_callback, false, true); + client_set_cur_dir(old_dir); } else { - bool wrote_tar_header = False; - - DEBUG(3,("getting file %s of size %.0f bytes as a tar file %s", - finfo.name, (double)finfo.size, lname)); + rc = tar_get_file(&tar_ctx, remote_name, finfo); + if (rc != 0) { + status = NT_STATUS_UNSUCCESSFUL; + goto out; + } + } - do { - - DEBUG(3,("nread=%.0f\n",(double)nread)); - - status = cli_read(cli, fnum, data, nread, - read_size, &datalen); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(0,("Error reading file %s : %s\n", - rname, nt_errstr(status))); - break; - } - - nread += datalen; - - /* Only if the first read succeeds, write out the tar header. */ - if (!wrote_tar_header) { - /* write a tar header, don't bother with mode - just set to 100644 */ - writetarheader(tarhandle, rname, finfo.size, - finfo.mtime_ts.tv_sec, "100644 \0", ftype); - wrote_tar_header = True; - } - - /* if file size has increased since we made file size query, truncate - read so tar header for this file will be correct. - */ - - if (nread > finfo.size) { - datalen -= nread - finfo.size; - DEBUG(0,("File size change - truncating %s to %.0f bytes\n", - finfo.name, (double)finfo.size)); - } - - /* add received bits of file to buffer - dotarbuf will - * write out in 512 byte intervals */ - - if (dotarbuf(tarhandle,data,datalen) != datalen) { - DEBUG(0,("Error writing to tar file - %s\n", strerror(errno))); - status = map_nt_error_from_unix(errno); - break; - } - - if ( (datalen == 0) && (finfo.size != 0) ) { - status = NT_STATUS_UNSUCCESSFUL; - DEBUG(0,("Error reading file %s. Got 0 bytes\n", rname)); - break; - } - - datalen=0; - } while ( nread < finfo.size ); - - if (wrote_tar_header) { - /* pad tar file with zero's if we couldn't get entire file */ - if (nread < finfo.size) { - DEBUG(0, ("Didn't get entire file. size=%.0f, nread=%d\n", - (double)finfo.size, (int)nread)); - if (padit(data, (uint64_t)sizeof(data), finfo.size - nread)) { - status = map_nt_error_from_unix(errno); - DEBUG(0,("Error writing tar file - %s\n", strerror(errno))); - } - } - - /* round tar file to nearest block */ - if (finfo.size % TBLOCK) - dozerobuf(tarhandle, TBLOCK - (finfo.size % TBLOCK)); +out: + talloc_free(ctx); + return status; +} - ttarf+=finfo.size + TBLOCK - (finfo.size % TBLOCK); - ntarf++; - } else { - DEBUG(4, ("skipping %s - initial read failed (file was locked ?)\n", finfo.name)); - shallitime=0; - status = NT_STATUS_UNSUCCESSFUL; - } +/** + * tar_get_file - fetch a remote file to the local archive + * @full_dos_path: path to the file to fetch + * @finfo: attributes of the file to fetch + */ +static int tar_get_file(struct tar *t, + const char *full_dos_path, + struct file_info *finfo) +{ + extern struct cli_state *cli; + NTSTATUS status; + struct archive_entry *entry; + char *full_unix_path; + char buf[TAR_CLI_READ_SIZE]; + size_t len; + uint64_t off = 0; + uint16_t remote_fd = (uint16_t)-1; + int err = 0, r; + const bool isdir = finfo->mode & FILE_ATTRIBUTE_DIRECTORY; + TALLOC_CTX *ctx = talloc_new(NULL); + if (ctx == NULL) { + return 1; } - cli_close(cli, fnum); - fnum = -1; + DBG(5, ("+++ %s\n", full_dos_path)); - if (shallitime) { - struct timespec tp_end; - int this_time; + t->total_size += finfo->size; - /* if shallitime is true then we didn't skip */ - if (tar_reset && !dry_run) - (void) do_setrattr(finfo.name, FILE_ATTRIBUTE_ARCHIVE, ATTRRESET); - - clock_gettime_mono(&tp_end); - this_time = (tp_end.tv_sec - tp_start.tv_sec)*1000 + (tp_end.tv_nsec - tp_start.tv_nsec)/1000000; - get_total_time_ms += this_time; - get_total_size += finfo.size; - - if (tar_noisy) { - DEBUG(0, ("%12.0f (%7.1f kb/s) %s\n", - (double)finfo.size, finfo.size / MAX(0.001, (1.024*this_time)), - finfo.name)); - } - - /* Thanks to Carel-Jan Engel (ease@mail.wirehub.nl) for this one */ - DEBUG(3,("(%g kb/s) (average %g kb/s)\n", - finfo.size / MAX(0.001, (1.024*this_time)), - get_total_size / MAX(0.001, (1.024*get_total_time_ms)))); + if (t->mode.dry) { + goto out; } - cleanup: - - if (fnum != (uint16_t)-1) { - cli_close(cli, fnum); - fnum = -1; + if (t->mode.reset) { + /* ignore return value: server might not store DOS attributes */ + set_remote_attr(full_dos_path, FILE_ATTRIBUTE_ARCHIVE, ATTR_UNSET); } - TALLOC_FREE(ctx); - SAFE_FREE(data); - return status; -} - -/**************************************************************************** -Append single file to tar file (or not) -***************************************************************************/ - -static NTSTATUS do_tar(struct cli_state *cli_state, struct file_info *finfo, - const char *dir) -{ - TALLOC_CTX *ctx = talloc_stackframe(); - NTSTATUS status = NT_STATUS_OK; - if (strequal(finfo->name,"..") || strequal(finfo->name,".")) { - status = NT_STATUS_OK; - goto cleanup; + full_unix_path = talloc_asprintf(ctx, ".%s", full_dos_path); + if (full_unix_path == NULL) { + err = 1; + goto out; + } + string_replace(full_unix_path, '\\', '/'); + entry = archive_entry_new(); + archive_entry_copy_pathname(entry, full_unix_path); + archive_entry_set_filetype(entry, isdir ? AE_IFDIR : AE_IFREG); + archive_entry_set_atime(entry, + finfo->atime_ts.tv_sec, + finfo->atime_ts.tv_nsec); + archive_entry_set_mtime(entry, + finfo->mtime_ts.tv_sec, + finfo->mtime_ts.tv_nsec); + archive_entry_set_ctime(entry, + finfo->ctime_ts.tv_sec, + finfo->ctime_ts.tv_nsec); + archive_entry_set_perm(entry, isdir ? 0755 : 0644); + /* + * check if we can safely cast unsigned file size to libarchive + * signed size. Very unlikely problem (>9 exabyte file) + */ + if (finfo->size > INT64_MAX) { + DBG(0, ("Remote file %s too big\n", full_dos_path)); + goto out_entry; } - /* Is it on the exclude list ? */ - if (!tar_excl && clipn) { - char *exclaim; - - DEBUG(5, ("Excl: strlen(cur_dir) = %d\n", (int)strlen(client_get_cur_dir()))); - - exclaim = talloc_asprintf(ctx, - "%s\\%s", - client_get_cur_dir(), - finfo->name); - if (!exclaim) { - status = NT_STATUS_NO_MEMORY; - goto cleanup; - } + archive_entry_set_size(entry, (int64_t)finfo->size); - DEBUG(5, ("...tar_re_search: %d\n", tar_re_search)); + r = archive_write_header(t->archive, entry); + if (r != ARCHIVE_OK) { + DBG(0, ("Fatal: %s\n", archive_error_string(t->archive))); + err = 1; + goto out_entry; + } - if ((!tar_re_search && clipfind(cliplist, clipn, exclaim)) || - (tar_re_search && mask_match_list(exclaim, cliplist, clipn, True))) { - DEBUG(3,("Skipping file %s\n", exclaim)); - TALLOC_FREE(exclaim); - status = NT_STATUS_OK; - goto cleanup; - } - TALLOC_FREE(exclaim); + if (isdir) { + DBG(5, ("get_file skip dir %s\n", full_dos_path)); + goto out_entry; } - if (finfo->mode & FILE_ATTRIBUTE_DIRECTORY) { - char *saved_curdir = NULL; - char *new_cd = NULL; - char *mtar_mask = NULL; + status = cli_open(cli, full_dos_path, O_RDONLY, DENY_NONE, &remote_fd); + if (!NT_STATUS_IS_OK(status)) { + DBG(0,("%s opening remote file %s\n", + nt_errstr(status), full_dos_path)); + goto out_entry; + } - saved_curdir = talloc_strdup(ctx, client_get_cur_dir()); - if (!saved_curdir) { - status = NT_STATUS_NO_MEMORY; - goto cleanup; + do { + status = cli_read(cli, remote_fd, buf, off, sizeof(buf), &len); + if (!NT_STATUS_IS_OK(status)) { + DBG(0,("Error reading file %s : %s\n", + full_dos_path, nt_errstr(status))); + err = 1; + goto out_close; } - DEBUG(5, ("strlen(cur_dir)=%d, \ -strlen(finfo->name)=%d\nname=%s,cur_dir=%s\n", - (int)strlen(saved_curdir), - (int)strlen(finfo->name), finfo->name, saved_curdir)); + off += len; - new_cd = talloc_asprintf(ctx, - "%s%s\\", - client_get_cur_dir(), - finfo->name); - if (!new_cd) { - status = NT_STATUS_NO_MEMORY; - goto cleanup; + r = archive_write_data(t->archive, buf, len); + if (r < 0) { + DBG(0, ("Fatal: %s\n", archive_error_string(t->archive))); + err = 1; + goto out_close; } - client_set_cur_dir(new_cd); - DEBUG(5, ("Writing a dir, Name = %s\n", client_get_cur_dir())); + } while (off < finfo->size); - /* write a tar directory, don't bother with mode - just - * set it to 40755 */ - writetarheader(tarhandle, client_get_cur_dir(), 0, - finfo->mtime_ts.tv_sec, "040755 \0", '5'); - if (tar_noisy) { - DEBUG(0,(" directory %s\n", - client_get_cur_dir())); - } - ntarf++; /* Make sure we have a file on there */ - mtar_mask = talloc_asprintf(ctx, - "%s*", - client_get_cur_dir()); - if (!mtar_mask) { - status = NT_STATUS_NO_MEMORY; - goto cleanup; - } - DEBUG(5, ("Doing list with mtar_mask: %s\n", mtar_mask)); - do_list(mtar_mask, attribute, do_tar, False, True); - client_set_cur_dir(saved_curdir); - TALLOC_FREE(saved_curdir); - TALLOC_FREE(new_cd); - TALLOC_FREE(mtar_mask); - } else { - char *rname = talloc_asprintf(ctx, - "%s%s", - client_get_cur_dir(), - finfo->name); - if (!rname) { - status = NT_STATUS_NO_MEMORY; - goto cleanup; - } - status = do_atar(rname,finfo->name,finfo); - TALLOC_FREE(rname); - } +out_close: + cli_close(cli, remote_fd); - cleanup: - TALLOC_FREE(ctx); - return status; -} +out_entry: + archive_entry_free(entry); -/**************************************************************************** -Convert from UNIX to DOS file names -***************************************************************************/ +out: + talloc_free(ctx); + return err; +} -static void unfixtarname(char *tptr, char *fp, int l, bool first) +/** + * tar_extract - open archive and send files. + */ +static int tar_extract(struct tar *t) { - /* remove '.' from start of file name, convert from unix /'s to - * dos \'s in path. Kill any absolute path names. But only if first! - */ + int err = 0; + int r; + struct archive_entry *entry; + const size_t bsize = t->mode.blocksize * TAR_BLOCK_UNIT; + int rc; + + t->archive = archive_read_new(); + archive_read_support_format_all(t->archive); + archive_read_support_filter_all(t->archive); + + if (strequal(t->tar_path, "-")) { + r = archive_read_open_fd(t->archive, STDIN_FILENO, bsize); + } else { + r = archive_read_open_filename(t->archive, t->tar_path, bsize); + } - DEBUG(5, ("firstb=%lX, secondb=%lX, len=%i\n", (long)tptr, (long)fp, l)); + if (r != ARCHIVE_OK) { + DBG(0, ("Can't open %s : %s\n", t->tar_path, + archive_error_string(t->archive))); + err = 1; + goto out; + } - if (first) { - if (*fp == '.') { - fp++; - l--; + for (;;) { + NTSTATUS status; + bool skip; + r = archive_read_next_header(t->archive, &entry); + if (r == ARCHIVE_EOF) { + break; } - if (*fp == '\\' || *fp == '/') { - fp++; - l--; + if (r == ARCHIVE_WARN) { + DBG(0, ("Warning: %s\n", archive_error_string(t->archive))); } - if (l <= 0) { - return; + if (r == ARCHIVE_FATAL) { + DBG(0, ("Fatal: %s\n", archive_error_string(t->archive))); + err = 1; + goto out; } - } - strlcpy(tptr, fp, l); - string_replace(tptr, '/', '\\'); -} - -/**************************************************************************** -Move to the next block in the buffer, which may mean read in another set of -blocks. FIXME, we should allow more than one block to be skipped. -****************************************************************************/ - -static int next_block(char *ltarbuf, char **bufferp, int bufsiz) -{ - int bufread, total = 0; - - DEBUG(5, ("Advancing to next block: %0lx\n", (unsigned long)*bufferp)); - *bufferp += TBLOCK; - total = TBLOCK; - - if (*bufferp >= (ltarbuf + bufsiz)) { - - DEBUG(5, ("Reading more data into ltarbuf ...\n")); - - /* - * Bugfix from Bob Boehmer - * Fixes bug where read can return short if coming from - * a pipe. - */ - - bufread = read(tarhandle, ltarbuf, bufsiz); - total = bufread; - - while (total < bufsiz) { - if (bufread < 0) { /* An error, return false */ - return (total > 0 ? -2 : bufread); - } - if (bufread == 0) { - if (total <= 0) { - return -2; - } - break; - } - bufread = read(tarhandle, <arbuf[total], bufsiz - total); - total += bufread; + status = tar_extract_skip_path(t, entry, &skip); + if (!NT_STATUS_IS_OK(status)) { + err = 1; + goto out; + } + if (skip) { + DBG(5, ("--- %s\n", archive_entry_pathname(entry))); + continue; } - DEBUG(5, ("Total bytes read ... %i\n", total)); - - *bufferp = ltarbuf; - } - - return(total); -} - -/* Skip a file, even if it includes a long file name? */ -static int skip_file(int skipsize) -{ - int dsize = skipsize; - - DEBUG(5, ("Skiping file. Size = %i\n", skipsize)); - - /* FIXME, we should skip more than one block at a time */ + DBG(5, ("+++ %s\n", archive_entry_pathname(entry))); - while (dsize > 0) { - if (next_block(tarbuf, &buffer_p, tbufsiz) <= 0) { - DEBUG(0, ("Empty file, short tar file, or read error: %s\n", strerror(errno))); - return(False); + rc = tar_send_file(t, entry); + if (rc != 0) { + err = 1; + goto out; } - dsize -= TBLOCK; } - return(True); +out: + r = archive_read_free(t->archive); + if (r != ARCHIVE_OK) { + DBG(0, ("Can't close %s : %s\n", t->tar_path, + archive_error_string(t->archive))); + err = 1; + } + return err; } -/************************************************************* - Get a file from the tar file and store it. - When this is called, tarbuf already contains the first - file block. This is a bit broken & needs fixing. -**************************************************************/ - -static int get_file(file_info2 finfo) +/** + * tar_send_file - send @entry to the remote server + * @entry: current archive entry + * + * Handle the creation of the parent directories and transfer the + * entry to a new remote file. + */ +static int tar_send_file(struct tar *t, struct archive_entry *entry) { - uint16_t fnum = (uint16_t) -1; - int dsize = 0, bpos = 0; - uint64_t rsize = 0, pos = 0; + extern struct cli_state *cli; + char *dos_path; + char *full_path; NTSTATUS status; + uint16_t remote_fd = (uint16_t) -1; + int err = 0; + int flags = O_RDWR | O_CREAT | O_TRUNC; + mode_t mode = archive_entry_filetype(entry); + int rc; + TALLOC_CTX *ctx = talloc_new(NULL); + if (ctx == NULL) { + return 1; + } - DEBUG(5, ("get_file: file: %s, size %.0f\n", finfo.name, (double)finfo.size)); + dos_path = talloc_strdup(ctx, archive_entry_pathname(entry)); + if (dos_path == NULL) { + err = 1; + goto out; + } + fix_unix_path(dos_path, true); - if (!ensurepath(finfo.name)) { - DEBUG(0, ("abandoning restore\n")); - return False; + full_path = talloc_strdup(ctx, client_get_cur_dir()); + if (full_path == NULL) { + err = 1; + goto out; + } + full_path = talloc_strdup_append(full_path, dos_path); + if (full_path == NULL) { + err = 1; + goto out; } - status = cli_open(cli, finfo.name, O_RDWR|O_CREAT|O_TRUNC, DENY_NONE, &fnum); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(0, ("abandoning restore\n")); - return False; + if (mode != AE_IFREG && mode != AE_IFDIR) { + DBG(0, ("Skipping non-dir & non-regular file %s\n", full_path)); + goto out; } - /* read the blocks from the tar file and write to the remote file */ + rc = make_remote_path(full_path); + if (rc != 0) { + err = 1; + goto out; + } - rsize = finfo.size; /* This is how much to write */ + if (mode == AE_IFDIR) { + goto out; + } - while (rsize > 0) { + status = cli_open(cli, full_path, flags, DENY_NONE, &remote_fd); + if (!NT_STATUS_IS_OK(status)) { + DBG(0, ("Error opening remote file %s: %s\n", + full_path, nt_errstr(status))); + err = 1; + goto out; + } - /* We can only write up to the end of the buffer */ - dsize = MIN(tbufsiz - (buffer_p - tarbuf) - bpos, 65520); /* Calculate the size to write */ - dsize = MIN(dsize, rsize); /* Should be only what is left */ - DEBUG(5, ("writing %i bytes, bpos = %i ...\n", dsize, bpos)); + for (;;) { + const void *buf; + size_t len; + off_t off; + int r; - status = cli_writeall(cli, fnum, 0, - (uint8_t *)(buffer_p + bpos), pos, - dsize, NULL); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(0, ("Error writing remote file: %s\n", - nt_errstr(status))); - return 0; + r = archive_read_data_block(t->archive, &buf, &len, &off); + if (r == ARCHIVE_EOF) { + break; } - - rsize -= dsize; - pos += dsize; - - /* Now figure out how much to move in the buffer */ - - /* FIXME, we should skip more than one block at a time */ - - /* First, skip any initial part of the part written that is left over */ - /* from the end of the first TBLOCK */ - - if ((bpos) && ((bpos + dsize) >= TBLOCK)) { - dsize -= (TBLOCK - bpos); /* Get rid of the end of the first block */ - bpos = 0; - - if (next_block(tarbuf, &buffer_p, tbufsiz) <=0) { /* and skip the block */ - DEBUG(0, ("Empty file, short tar file, or read error: %s\n", strerror(errno))); - return False; - } + if (r == ARCHIVE_WARN) { + DBG(0, ("Warning: %s\n", archive_error_string(t->archive))); + } + if (r == ARCHIVE_FATAL) { + DBG(0, ("Fatal: %s\n", archive_error_string(t->archive))); + err = 1; + goto close_out; } - /* - * Bugfix from Bob Boehmer . - * If the file being extracted is an exact multiple of - * TBLOCK bytes then we don't want to extract the next - * block from the tarfile here, as it will be done in - * the caller of get_file(). - */ - - while (((rsize != 0) && (dsize >= TBLOCK)) || - ((rsize == 0) && (dsize > TBLOCK))) { - - if (next_block(tarbuf, &buffer_p, tbufsiz) <=0) { - DEBUG(0, ("Empty file, short tar file, or read error: %s\n", strerror(errno))); - return False; - } - - dsize -= TBLOCK; + status = cli_writeall(cli, remote_fd, 0, buf, off, len, NULL); + if (!NT_STATUS_IS_OK(status)) { + DBG(0, ("Error writing remote file %s: %s\n", + full_path, nt_errstr(status))); + err = 1; + goto close_out; } - bpos = dsize; } - /* Now close the file ... */ - status = cli_close(cli, fnum); +close_out: + status = cli_close(cli, remote_fd); if (!NT_STATUS_IS_OK(status)) { - DEBUG(0, ("Error %s closing remote file\n", - nt_errstr(status))); - return(False); + DBG(0, ("Error losing remote file %s: %s\n", + full_path, nt_errstr(status))); + err = 1; } - /* Now we update the creation date ... */ - DEBUG(5, ("Updating creation date on %s\n", finfo.name)); +out: + talloc_free(ctx); + return err; +} - if (!NT_STATUS_IS_OK(cli_setatr(cli, finfo.name, finfo.mode, finfo.mtime_ts.tv_sec))) { - if (tar_real_noisy) { - DEBUG(0, ("Could not set time on file: %s\n", finfo.name)); - /*return(False); */ /* Ignore, as Win95 does not allow changes */ +/** + * tar_add_selection_path - add a path to the path list + * @path: path to add + */ +static NTSTATUS tar_add_selection_path(struct tar *t, const char *path) +{ + const char **list; + TALLOC_CTX *ctx = t->talloc_ctx; + if (!t->path_list) { + t->path_list = str_list_make_empty(ctx); + if (t->path_list == NULL) { + return NT_STATUS_NO_MEMORY; } + t->path_list_size = 0; + } + + /* cast to silence gcc const-qual warning */ + list = str_list_add((void *)t->path_list, path); + if (list == NULL) { + return NT_STATUS_NO_MEMORY; } + t->path_list = discard_const_p(char *, list); + t->path_list_size++; + fix_unix_path(t->path_list[t->path_list_size - 1], true); - ntarf++; - DEBUG(0, ("restore tar file %s of size %.0f bytes\n", finfo.name, (double)finfo.size)); - return(True); + return NT_STATUS_OK; } -/* Create a directory. We just ensure that the path exists and return as there - is no file associated with a directory -*/ -static int get_dir(file_info2 finfo) +/** + * tar_set_blocksize - set block size in TAR_BLOCK_UNIT + */ +static int tar_set_blocksize(struct tar *t, int size) { - DEBUG(0, ("restore directory %s\n", finfo.name)); - - if (!ensurepath(finfo.name)) { - DEBUG(0, ("Problems creating directory\n")); - return(False); + if (size <= 0 || size > TAR_MAX_BLOCK_SIZE) { + return 1; } - ntarf++; - return(True); + + t->mode.blocksize = size; + + return 0; } -/* Get a file with a long file name ... first file has file name, next file - has the data. We only want the long file name, as the loop in do_tarput - will deal with the rest. -*/ -static char *get_longfilename(file_info2 finfo) +/** + * tar_set_newer_than - set date threshold of saved files + * @filename: local path to a file + * + * Only files newer than the modification time of @filename will be + * saved. + * + * Note: this function set the global variable newer_than from + * client.c. Thus the time is not a field of the tar structure. See + * cmd_newer() to change its value from an interactive session. + */ +static int tar_set_newer_than(struct tar *t, const char *filename) { - /* finfo.size here is the length of the filename as written by the "/./@LongLink" name - * header call. */ - int namesize = finfo.size + strlen(client_get_cur_dir()) + 2; - char *longname = (char *)SMB_MALLOC(namesize); - int offset = 0, left = finfo.size; - bool first = True; - - DEBUG(5, ("Restoring a long file name: %s\n", finfo.name)); - DEBUG(5, ("Len = %.0f\n", (double)finfo.size)); + extern time_t newer_than; + SMB_STRUCT_STAT stbuf; + int rc; - if (longname == NULL) { - DEBUG(0, ("could not allocate buffer of size %d for longname\n", namesize)); - return(NULL); + rc = sys_stat(filename, &stbuf, false); + if (rc != 0) { + DBG(0, ("Error setting newer-than time\n")); + return 1; } - /* First, add cur_dir to the long file name */ + newer_than = convert_timespec_to_time_t(stbuf.st_ex_mtime); + DBG(1, ("Getting files newer than %s\n", time_to_asc(newer_than))); + return 0; +} - if (strlen(client_get_cur_dir()) > 0) { - strncpy(longname, client_get_cur_dir(), namesize); - offset = strlen(client_get_cur_dir()); +/** + * tar_read_inclusion_file - set path list from file + * @filename: path to the list file + * + * Read and add each line of @filename to the path list. + */ +static int tar_read_inclusion_file(struct tar *t, const char* filename) +{ + char *line; + int err = 0; + int fd; + TALLOC_CTX *ctx = talloc_new(NULL); + if (ctx == NULL) { + return 1; } - /* Loop through the blocks picking up the name */ + fd = open(filename, O_RDONLY); + if (fd < 0) { + DBG(0, ("Can't open inclusion file '%s': %s\n", filename, strerror(errno))); + err = 1; + goto out; + } - while (left > 0) { - if (next_block(tarbuf, &buffer_p, tbufsiz) <= 0) { - DEBUG(0, ("Empty file, short tar file, or read error: %s\n", strerror(errno))); - SAFE_FREE(longname); - return(NULL); + for (line = afdgets(fd, ctx, 0); + line != NULL; + line = afdgets(fd, ctx, 0)) { + NTSTATUS status; + status = tar_add_selection_path(t, line); + if (!NT_STATUS_IS_OK(status)) { + err = 1; + goto out; } - - unfixtarname(longname + offset, buffer_p, - namesize - offset, first--); - DEBUG(5, ("UnfixedName: %s, buffer: %s\n", longname, buffer_p)); - - offset += TBLOCK; - left -= TBLOCK; } - return(longname); + close(fd); + +out: + talloc_free(ctx); + return err; } -static void do_tarput(void) +/** + * tar_path_in_list - check whether @path is in the path list + * @path: path to find + * @reverse: when true also try to find path list element in @path + * @_is_in_list: set if @path is in the path list + * + * Look at each path of the path list and set @_is_in_list if @path is a + * subpath of one of them. + * + * If you want /path to be in the path list (path/a/, path/b/) set + * @reverse to true to try to match the other way around. + */ +static NTSTATUS tar_path_in_list(struct tar *t, const char *path, + bool reverse, bool *_is_in_list) { - file_info2 finfo; - struct timespec tp_start; - char *longfilename = NULL, linkflag; - int skip = False; + int i; + const char *p; + const char *pattern; - ZERO_STRUCT(finfo); + if (path == NULL || path[0] == '\0') { + *_is_in_list = false; + return NT_STATUS_OK; + } - clock_gettime_mono(&tp_start); - DEBUG(5, ("RJS do_tarput called ...\n")); + p = skip_useless_char_in_path(path); - buffer_p = tarbuf + tbufsiz; /* init this to force first read */ + for (i = 0; i < t->path_list_size; i++) { + bool is_in_list; + NTSTATUS status; - /* Now read through those files ... */ - while (True) { - /* Get us to the next block, or the first block first time around */ - if (next_block(tarbuf, &buffer_p, tbufsiz) <= 0) { - DEBUG(0, ("Empty file, short tar file, or read error: %s\n", strerror(errno))); - SAFE_FREE(longfilename); - return; + pattern = skip_useless_char_in_path(t->path_list[i]); + status = is_subpath(p, pattern, &is_in_list); + if (!NT_STATUS_IS_OK(status)) { + return status; } + if (reverse && !is_in_list) { + status = is_subpath(pattern, p, &is_in_list); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + } + if (is_in_list) { + *_is_in_list = true; + return NT_STATUS_OK; + } + } - DEBUG(5, ("Reading the next header ...\n")); - - switch (readtarheader((union hblock *) buffer_p, - &finfo, client_get_cur_dir())) { - case -2: /* Hmm, not good, but not fatal */ - DEBUG(0, ("Skipping %s...\n", finfo.name)); - if ((next_block(tarbuf, &buffer_p, tbufsiz) <= 0) && !skip_file(finfo.size)) { - DEBUG(0, ("Short file, bailing out...\n")); - SAFE_FREE(longfilename); - return; - } - break; + *_is_in_list = false; + return NT_STATUS_OK; +} - case -1: - DEBUG(0, ("abandoning restore, -1 from read tar header\n")); - SAFE_FREE(longfilename); - return; +/** + * tar_extract_skip_path - check if @entry should be skipped + * @entry: current tar entry + * @_skip: set true if path should be skipped, otherwise false + * + * Skip predicate for tar extraction (archive to server) only. + */ +static NTSTATUS tar_extract_skip_path(struct tar *t, + struct archive_entry *entry, + bool *_skip) +{ + const char *fullpath = archive_entry_pathname(entry); + bool in = true; - case 0: /* chksum is zero - looks like an EOF */ - DEBUG(0, ("tar: restored %d files and directories\n", ntarf)); - SAFE_FREE(longfilename); - return; /* Hmmm, bad here ... */ + if (t->path_list_size <= 0) { + *_skip = false; + return NT_STATUS_OK; + } - default: - /* No action */ - break; + if (t->mode.regex) { + in = mask_match_list(fullpath, t->path_list, t->path_list_size, true); + } else { + NTSTATUS status = tar_path_in_list(t, fullpath, false, &in); + if (!NT_STATUS_IS_OK(status)) { + return status; } + } - /* Now, do we have a long file name? */ - if (longfilename != NULL) { - SAFE_FREE(finfo.name); /* Free the space already allocated */ - finfo.name = longfilename; - longfilename = NULL; - } + if (t->mode.selection == TAR_EXCLUDE) { + *_skip = in; + } else { + *_skip = !in; + } - /* Well, now we have a header, process the file ... */ - /* Should we skip the file? We have the long name as well here */ - skip = clipn && ((!tar_re_search && clipfind(cliplist, clipn, finfo.name) ^ tar_excl) || - (tar_re_search && mask_match_list(finfo.name, cliplist, clipn, True))); + return NT_STATUS_OK; +} - DEBUG(5, ("Skip = %i, cliplist=%s, file=%s\n", skip, (cliplist?cliplist[0]:NULL), finfo.name)); - if (skip) { - skip_file(finfo.size); - continue; +/** + * tar_create_skip_path - check if @fullpath shoud be skipped + * @fullpath: full remote path of the current file + * @finfo: remote file attributes + * @_skip: returned skip not + * + * Skip predicate for tar creation (server to archive) only. + */ +static NTSTATUS tar_create_skip_path(struct tar *t, + const char *fullpath, + const struct file_info *finfo, + bool *_skip) +{ + /* syntaxic sugar */ + const mode_t mode = finfo->mode; + const bool isdir = mode & FILE_ATTRIBUTE_DIRECTORY; + const bool exclude = t->mode.selection == TAR_EXCLUDE; + bool in = true; + + if (!isdir) { + + /* 1. if we dont want X and we have X, skip */ + if (!t->mode.system && (mode & FILE_ATTRIBUTE_SYSTEM)) { + *_skip = true; + return NT_STATUS_OK; } - /* We only get this far if we should process the file */ - linkflag = ((union hblock *)buffer_p) -> dbuf.linkflag; - switch (linkflag) { - case '0': /* Should use symbolic names--FIXME */ - /* - * Skip to the next block first, so we can get the file, FIXME, should - * be in get_file ... - * The 'finfo.size != 0' fix is from Bob Boehmer - * Fixes bug where file size in tarfile is zero. - */ - if ((finfo.size != 0) && next_block(tarbuf, &buffer_p, tbufsiz) <=0) { - DEBUG(0, ("Short file, bailing out...\n")); - return; - } - if (!get_file(finfo)) { - DEBUG(0, ("Abandoning restore\n")); - return; - } - break; - case '5': - if (!get_dir(finfo)) { - DEBUG(0, ("Abandoning restore \n")); - return; - } - break; - case 'L': - SAFE_FREE(longfilename); - longfilename = get_longfilename(finfo); - if (!longfilename) { - DEBUG(0, ("abandoning restore\n")); - return; - } - DEBUG(5, ("Long file name: %s\n", longfilename)); - break; + if (!t->mode.hidden && (mode & FILE_ATTRIBUTE_HIDDEN)) { + *_skip = true; + return NT_STATUS_OK; + } - default: - skip_file(finfo.size); /* Don't handle these yet */ - break; + /* 2. if we only want archive and it's not, skip */ + + if (t->mode.incremental && !(mode & FILE_ATTRIBUTE_ARCHIVE)) { + *_skip = true; + return NT_STATUS_OK; } } -} -/* - * samba interactive commands - */ + /* 3. is it in the selection list? */ -/**************************************************************************** -Blocksize command -***************************************************************************/ + /* + * tar_create_from_list() use the include list as a starting + * point, no need to check + */ + if (!exclude) { + *_skip = false; + return NT_STATUS_OK; + } -int cmd_block(void) -{ - TALLOC_CTX *ctx = talloc_tos(); - char *buf; - int block; + /* we are now in exclude mode */ - if (!next_token_talloc(ctx, (const char **)&cmd_ptr,&buf,NULL)) { - DEBUG(0, ("blocksize \n")); - return 1; + /* no matter the selection, no list => include everything */ + if (t->path_list_size <= 0) { + *_skip = false; + return NT_STATUS_OK; } - block=atoi(buf); - if (block < 0 || block > 65535) { - DEBUG(0, ("blocksize out of range")); - return 1; + if (t->mode.regex) { + in = mask_match_list(fullpath, t->path_list, t->path_list_size, true); + } else { + bool reverse = isdir && !exclude; + NTSTATUS status = tar_path_in_list(t, fullpath, reverse, &in); + if (!NT_STATUS_IS_OK(status)) { + return status; + } } + *_skip = in; - blocksize=block; - DEBUG(2,("blocksize is now %d\n", blocksize)); - return 0; + return NT_STATUS_OK; } -/**************************************************************************** -command to set incremental / reset mode -***************************************************************************/ - -int cmd_tarmode(void) +/** + * tar_to_process - return true if @t is ready to be processed + * + * @t is ready if it properly parsed command line arguments. + */ +bool tar_to_process(struct tar *t) { - TALLOC_CTX *ctx = talloc_tos(); - char *buf; + if (t == NULL) { + DBG(0, ("Invalid tar context\n")); + return false; + } + return t->to_process; +} - while (next_token_talloc(ctx, (const char **)&cmd_ptr,&buf,NULL)) { - if (strequal(buf, "full")) - tar_inc=False; - else if (strequal(buf, "inc")) - tar_inc=True; - else if (strequal(buf, "reset")) - tar_reset=True; - else if (strequal(buf, "noreset")) - tar_reset=False; - else if (strequal(buf, "system")) - tar_system=True; - else if (strequal(buf, "nosystem")) - tar_system=False; - else if (strequal(buf, "hidden")) - tar_hidden=True; - else if (strequal(buf, "nohidden")) - tar_hidden=False; - else if (strequal(buf, "verbose") || strequal(buf, "noquiet")) - tar_noisy=True; - else if (strequal(buf, "quiet") || strequal(buf, "noverbose")) - tar_noisy=False; +/** + * skip_useless_char_in_path - skip leading slashes/dots + * + * Skip leading slashes, backslashes and dot-slashes. + */ +static const char* skip_useless_char_in_path(const char *p) +{ + while (p) { + if (*p == '/' || *p == '\\') { + p++; + } + else if (p[0] == '.' && (p[1] == '/' || p[1] == '\\')) { + p += 2; + } else - DEBUG(0, ("tarmode: unrecognised option %s\n", buf)); - TALLOC_FREE(buf); + return p; } - - DEBUG(0, ("tarmode is now %s, %s, %s, %s, %s\n", - tar_inc ? "incremental" : "full", - tar_system ? "system" : "nosystem", - tar_hidden ? "hidden" : "nohidden", - tar_reset ? "reset" : "noreset", - tar_noisy ? "verbose" : "quiet")); - return 0; + return p; } -/**************************************************************************** -Feeble attrib command -***************************************************************************/ - -int cmd_setmode(void) +/** + * is_subpath - check if the path @sub is a subpath of @full. + * @sub: path to test + * @full: container path + * @_subpath_match: set true if @sub is a subpath of @full, otherwise false + * + * String comparaison is case-insensitive. + */ +static NTSTATUS is_subpath(const char *sub, const char *full, + bool *_subpath_match) { - TALLOC_CTX *ctx = talloc_tos(); - char *q; - char *buf; - char *fname = NULL; - uint16 attra[2]; - int direct=1; + NTSTATUS status = NT_STATUS_OK; + int len = 0; + char *f, *s; + TALLOC_CTX *tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + status = NT_STATUS_NO_MEMORY; + goto out; + } - attra[0] = attra[1] = 0; + f = strlower_talloc(tmp_ctx, full); + if (f == NULL) { + status = NT_STATUS_NO_MEMORY; + goto out_ctx_free; + } + string_replace(f, '\\', '/'); + s = strlower_talloc(tmp_ctx, sub); + if (f == NULL) { + status = NT_STATUS_NO_MEMORY; + goto out_ctx_free; + } + string_replace(s, '\\', '/'); - if (!next_token_talloc(ctx, (const char **)&cmd_ptr,&buf,NULL)) { - DEBUG(0, ("setmode <[+|-]rsha>\n")); - return 1; + /* find the point where sub and full diverge */ + while ((*f != '\0') && (*s != '\0') && (*f == *s)) { + f++; + s++; + len++; } - fname = talloc_asprintf(ctx, - "%s%s", - client_get_cur_dir(), - buf); - if (!fname) { - return 1; + if ((*f == '\0') && (*s == '\0')) { + *_subpath_match = true; /* sub and full match */ + goto out_ctx_free; } - while (next_token_talloc(ctx, (const char **)&cmd_ptr,&buf,NULL)) { - q=buf; - - while(*q) { - switch (*q++) { - case '+': - direct=1; - break; - case '-': - direct=0; - break; - case 'r': - attra[direct]|=FILE_ATTRIBUTE_READONLY; - break; - case 'h': - attra[direct]|=FILE_ATTRIBUTE_HIDDEN; - break; - case 's': - attra[direct]|=FILE_ATTRIBUTE_SYSTEM; - break; - case 'a': - attra[direct]|=FILE_ATTRIBUTE_ARCHIVE; - break; - default: - DEBUG(0, ("setmode \n")); - return 1; - } - } + if ((*f == '\0') && (len > 0) && (*(f - 1) == '/')) { + /* sub diverges from full at path separator */ + *_subpath_match = true; + goto out_ctx_free; } - if (attra[ATTRSET]==0 && attra[ATTRRESET]==0) { - DEBUG(0, ("setmode <[+|-]rsha>\n")); - return 1; + if ((*s == '\0') && (strcmp(f, "/") == 0)) { + /* full diverges from sub with trailing slash only */ + *_subpath_match = true; + goto out_ctx_free; } - DEBUG(2, ("\nperm set %d %d\n", attra[ATTRSET], attra[ATTRRESET])); - do_setrattr(fname, attra[ATTRSET], ATTRSET); - do_setrattr(fname, attra[ATTRRESET], ATTRRESET); - return 0; + if ((*s == '/') && (*f == '\0')) { + /* sub diverges from full with extra path component */ + *_subpath_match = true; + goto out_ctx_free; + } + *_subpath_match = false; + +out_ctx_free: + talloc_free(tmp_ctx); +out: + return status; } /** - Convert list of tokens to array; dependent on above routine. - Uses the global cmd_ptr from above - bit of a hack. -**/ - -static char **toktocliplist(int *ctok, const char *sep) + * set_remote_attr - set DOS attributes of a remote file + * @filename: path to the file name + * @new_attr: attribute bit mask to use + * @mode: one of ATTR_SET or ATTR_UNSET + * + * Update the file attributes with the one provided. + */ +static int set_remote_attr(const char *filename, uint16 new_attr, int mode) { - char *s=(char *)cmd_ptr; - int ictok=0; - char **ret, **iret; - - if (!sep) - sep = " \t\n\r"; + extern struct cli_state *cli; + uint16 old_attr; + NTSTATUS status; - while(*s && strchr_m(sep,*s)) - s++; + status = cli_getatr(cli, filename, &old_attr, NULL, NULL); + if (!NT_STATUS_IS_OK(status)) { + DBG(0, ("cli_getatr failed: %s\n", nt_errstr(status))); + return 1; + } - /* nothing left? */ - if (!*s) - return(NULL); + if (mode == ATTR_SET) { + new_attr |= old_attr; + } else { + new_attr = old_attr & ~new_attr; + } - do { - ictok++; - while(*s && (!strchr_m(sep,*s))) - s++; - while(*s && strchr_m(sep,*s)) - *s++=0; - } while(*s); - - *ctok=ictok; - s=(char *)cmd_ptr; - - if (!(ret=iret=SMB_MALLOC_ARRAY(char *,ictok+1))) - return NULL; - - while(ictok--) { - *iret++=s; - if (ictok > 0) { - while(*s++) - ; - while(!*s) - s++; - } + status = cli_setatr(cli, filename, new_attr, 0); + if (!NT_STATUS_IS_OK(status)) { + DBG(1, ("cli_setatr failed: %s\n", nt_errstr(status))); + return 1; } - ret[*ctok] = NULL; - return ret; + return 0; } -/**************************************************************************** -Principal command for creating / extracting -***************************************************************************/ -int cmd_tar(void) +/** + * make_remote_path - recursively make remote dirs + * @full_path: full hierarchy to create + * + * Create @full_path and each parent directories as needed. + */ +static int make_remote_path(const char *full_path) { - TALLOC_CTX *ctx = talloc_tos(); - char *buf; - char **argl = NULL; - int argcl = 0; - int ret; - - if (!next_token_talloc(ctx, (const char **)&cmd_ptr,&buf,NULL)) { - DEBUG(0,("tar [IXbgan] \n")); + extern struct cli_state *cli; + char *path; + char *subpath; + char *state; + char *last_backslash; + char *p; + int len; + NTSTATUS status; + int err = 0; + TALLOC_CTX *ctx = talloc_new(NULL); + if (ctx == NULL) { return 1; } - argl=toktocliplist(&argcl, NULL); - if (!tar_parseargs(argcl, argl, buf, 0)) { - SAFE_FREE(argl); - return 1; + subpath = talloc_strdup(ctx, full_path); + if (subpath == NULL) { + err = 1; + goto out; + } + path = talloc_strdup(ctx, full_path); + if (path == NULL) { + err = 1; + goto out; } + len = talloc_get_size(path) - 1; - ret = process_tar(); - SAFE_FREE(argl); - return ret; -} + last_backslash = strrchr_m(path, '\\'); + if (last_backslash == NULL) { + goto out; + } -/**************************************************************************** -Command line (option) version -***************************************************************************/ + *last_backslash = 0; -int process_tar(void) -{ - TALLOC_CTX *ctx = talloc_tos(); - int rc = 0; - initarbuf(); - switch(tar_type) { - case 'x': + subpath[0] = 0; + p = strtok_r(path, "\\", &state); -#if 0 - do_tarput2(); -#else - do_tarput(); -#endif - SAFE_FREE(tarbuf); - close(tarhandle); - break; - case 'r': - case 'c': - if (clipn && tar_excl) { - int i; - char *tarmac = NULL; - - for (i=0; italloc_ctx = talloc_new(NULL); + return t->talloc_ctx; +} - if (!strslashcmp(pkey, tok)) - return 1; +/** + * tar_free_mem_context - free talloc context associated with @t + */ +static void tar_free_mem_context(struct tar *t) +{ + if (t->talloc_ctx) { + talloc_free(t->talloc_ctx); + t->talloc_ctx = NULL; + t->path_list_size = 0; + t->path_list = NULL; + t->tar_path = NULL; } - return 0; } -/**************************************************************************** -Read list of files to include from the file and initialize cliplist -accordingly. -***************************************************************************/ +#define XSET(v) [v] = #v +#define XTABLE(v, t) DBG(2, ("DUMP:%-20.20s = %s\n", #v, t[v])) +#define XBOOL(v) DBG(2, ("DUMP:%-20.20s = %d\n", #v, v ? 1 : 0)) +#define XSTR(v) DBG(2, ("DUMP:%-20.20s = %s\n", #v, v ? v : "NULL")) +#define XINT(v) DBG(2, ("DUMP:%-20.20s = %d\n", #v, v)) +#define XUINT64(v) DBG(2, ("DUMP:%-20.20s = %" PRIu64 "\n", #v, v)) -static int read_inclusion_file(char *filename) +/** + * tar_dump - dump tar structure on stdout + */ +static void tar_dump(struct tar *t) { - XFILE *inclusion = NULL; - char buf[PATH_MAX + 1]; - char *inclusion_buffer = NULL; - int inclusion_buffer_size = 0; - int inclusion_buffer_sofar = 0; - char *p; - char *tmpstr; int i; - int error = 0; + const char* op[] = { + XSET(TAR_NO_OPERATION), + XSET(TAR_CREATE), + XSET(TAR_EXTRACT), + }; + + const char* sel[] = { + XSET(TAR_NO_SELECTION), + XSET(TAR_INCLUDE), + XSET(TAR_EXCLUDE), + }; + + XBOOL(t->to_process); + XTABLE(t->mode.operation, op); + XTABLE(t->mode.selection, sel); + XINT(t->mode.blocksize); + XBOOL(t->mode.hidden); + XBOOL(t->mode.system); + XBOOL(t->mode.incremental); + XBOOL(t->mode.reset); + XBOOL(t->mode.dry); + XBOOL(t->mode.verbose); + XUINT64(t->total_size); + XSTR(t->tar_path); + XINT(t->path_list_size); + + for (i = 0; t->path_list && t->path_list[i]; i++) { + DBG(2, ("DUMP: t->path_list[%2d] = %s\n", i, t->path_list[i])); + } + + DBG(2, ("DUMP:t->path_list @ %p (%d elem)\n", t->path_list, i)); +} +#undef XSET +#undef XTABLE +#undef XBOOL +#undef XSTR +#undef XINT - clipn = 0; - buf[PATH_MAX] = '\0'; /* guarantee null-termination */ - if ((inclusion = x_fopen(filename, O_RDONLY, 0)) == NULL) { - /* XXX It would be better to include a reason for failure, but without - * autoconf, it's hard to use strerror, sys_errlist, etc. - */ - DEBUG(0,("Unable to open inclusion file %s\n", filename)); +/** + * max_token - return upper limit for the number of token in @str + * + * The result is not exact, the actual number of token might be less + * than what is returned. + */ +static int max_token(const char *str) +{ + const char *s; + int nb = 0; + + if (str == NULL) { return 0; } - while ((! error) && (x_fgets(buf, sizeof(buf)-1, inclusion))) { - if (inclusion_buffer == NULL) { - inclusion_buffer_size = 1024; - if ((inclusion_buffer = (char *)SMB_MALLOC(inclusion_buffer_size)) == NULL) { - DEBUG(0,("failure allocating buffer to read inclusion file\n")); - error = 1; - break; - } + s = str; + while (s[0] != '\0') { + if (isspace((int)s[0])) { + nb++; } + s++; + } - if (buf[strlen(buf)-1] == '\n') { - buf[strlen(buf)-1] = '\0'; - } + nb++; - if ((strlen(buf) + 1 + inclusion_buffer_sofar) >= inclusion_buffer_size) { - inclusion_buffer_size *= 2; - inclusion_buffer = (char *)SMB_REALLOC(inclusion_buffer,inclusion_buffer_size); - if (!inclusion_buffer) { - DEBUG(0,("failure enlarging inclusion buffer to %d bytes\n", - inclusion_buffer_size)); - error = 1; - break; - } - } + return nb; +} + +/** + * fix_unix_path - convert @path to a DOS path + * @path: path to convert + * @removeprefix: if true, remove leading ./ or /. + */ +static char *fix_unix_path(char *path, bool do_remove_prefix) +{ + char *from = path, *to = path; - strlcpy(inclusion_buffer + inclusion_buffer_sofar, buf, inclusion_buffer_size - inclusion_buffer_sofar); - inclusion_buffer_sofar += strlen(buf) + 1; - clipn++; + if (path == NULL || path[0] == '\0') { + return path; } - x_fclose(inclusion); - if (! error) { - /* Allocate an array of clipn + 1 char*'s for cliplist */ - cliplist = SMB_MALLOC_ARRAY(char *, clipn + 1); - if (cliplist == NULL) { - DEBUG(0,("failure allocating memory for cliplist\n")); - error = 1; - } else { - cliplist[clipn] = NULL; - p = inclusion_buffer; - for (i = 0; (! error) && (i < clipn); i++) { - /* set current item to NULL so array will be null-terminated even if - * malloc fails below. */ - cliplist[i] = NULL; - if ((tmpstr = (char *)SMB_MALLOC(strlen(p)+1)) == NULL) { - DEBUG(0, ("Could not allocate space for a cliplist item, # %i\n", i)); - error = 1; - } else { - unfixtarname(tmpstr, p, strlen(p) + 1, True); - cliplist[i] = tmpstr; - if ((p = strchr_m(p, '\000')) == NULL) { - DEBUG(0,("INTERNAL ERROR: inclusion_buffer is of unexpected contents.\n")); - abort(); - } - } - ++p; - } - must_free_cliplist = True; + /* remove prefix: + * ./path => path + * /path => path + */ + if (do_remove_prefix) { + /* /path */ + if (path[0] == '/' || path[0] == '\\') { + from += 1; + } + + /* ./path */ + if (path[1] != '\0' && path[0] == '.' && (path[1] == '/' || path[1] == '\\')) { + from += 2; } } - SAFE_FREE(inclusion_buffer); - if (error) { - if (cliplist) { - char **pp; - /* We know cliplist is always null-terminated */ - for (pp = cliplist; *pp; ++pp) { - SAFE_FREE(*pp); - } - SAFE_FREE(cliplist); - cliplist = NULL; - must_free_cliplist = False; + /* replace / with \ */ + while (from[0] != '\0') { + if (from[0] == '/') { + to[0] = '\\'; + } else { + to[0] = from[0]; } - return 0; + + from++; + to++; } + to[0] = '\0'; - /* cliplist and its elements are freed at the end of process_tar. */ - return 1; + return path; } -/**************************************************************************** -Parse tar arguments. Sets tar_type, tar_excl, etc. -***************************************************************************/ - -int tar_parseargs(int argc, char *argv[], const char *Optarg, int Optind) +/** + * path_base_name - return @path basename + * + * If @path doesn't contain any directory separator return NULL. + */ +static NTSTATUS path_base_name(TALLOC_CTX *ctx, const char *path, char **_base) { - int newOptind = Optind; - char tar_clipfl='\0'; + char *base = NULL; + int last = -1; + int i; - /* Reset back to defaults - could be from interactive version - * reset mode and archive mode left as they are though - */ - tar_type='\0'; - tar_excl=True; - dry_run=False; - - while (*Optarg) { - switch(*Optarg++) { - case 'c': - tar_type='c'; - break; - case 'x': - if (tar_type=='c') { - printf("Tar must be followed by only one of c or x.\n"); - return 0; - } - tar_type='x'; - break; - case 'b': - if (Optind>=argc || !(blocksize=atoi(argv[Optind]))) { - DEBUG(0,("Option b must be followed by valid blocksize\n")); - return 0; - } else { - Optind++; - newOptind++; - } - break; - case 'g': - tar_inc=True; - break; - case 'N': - if (Optind>=argc) { - DEBUG(0,("Option N must be followed by valid file name\n")); - return 0; - } else { - SMB_STRUCT_STAT stbuf; - - if (sys_stat(argv[Optind], &stbuf, - false) == 0) { - newer_than = convert_timespec_to_time_t( - stbuf.st_ex_mtime); - DEBUG(1,("Getting files newer than %s", - time_to_asc(newer_than))); - newOptind++; - Optind++; - } else { - DEBUG(0,("Error setting newer-than time\n")); - return 0; - } - } - break; - case 'a': - tar_reset=True; - break; - case 'q': - tar_noisy=False; - break; - case 'I': - if (tar_clipfl) { - DEBUG(0,("Only one of I,X,F must be specified\n")); - return 0; - } - tar_clipfl='I'; - break; - case 'X': - if (tar_clipfl) { - DEBUG(0,("Only one of I,X,F must be specified\n")); - return 0; - } - tar_clipfl='X'; - break; - case 'F': - if (tar_clipfl) { - DEBUG(0,("Only one of I,X,F must be specified\n")); - return 0; - } - tar_clipfl='F'; - break; - case 'r': - DEBUG(0, ("tar_re_search set\n")); - tar_re_search = True; - break; - case 'n': - if (tar_type == 'c') { - DEBUG(0, ("dry_run set\n")); - dry_run = True; - } else { - DEBUG(0, ("n is only meaningful when creating a tar-file\n")); - return 0; - } - break; - default: - DEBUG(0,("Unknown tar option\n")); - return 0; + for (i = 0; path[i]; i++) { + if (path[i] == '\\' || path[i] == '/') { + last = i; } } - if (!tar_type) { - printf("Option T must be followed by one of c or x.\n"); - return 0; - } - - /* tar_excl is true if cliplist lists files to be included. - * Both 'I' and 'F' mean include. */ - tar_excl=tar_clipfl!='X'; - - if (tar_clipfl=='F') { - if (argc-Optind-1 != 1) { - DEBUG(0,("Option F must be followed by exactly one filename.\n")); - return 0; - } - newOptind++; - /* Optind points at the tar output file, Optind+1 at the inclusion file. */ - if (! read_inclusion_file(argv[Optind+1])) { - return 0; - } - } else if (Optind+1= 0) { + base = talloc_strdup(ctx, path); + if (base == NULL) { + return NT_STATUS_NO_MEMORY; } - for (clipcount = 0; clipcount < clipn; clipcount++) { + base[last] = 0; + } - DEBUG(5, ("Processing an item, %s\n", cliplist[clipcount])); + *_base = base; + return NT_STATUS_OK; +} - if ((tmpstr = (char *)SMB_MALLOC(strlen(cliplist[clipcount])+1)) == NULL) { - DEBUG(0, ("Could not allocate space for a cliplist item, # %i\n", clipcount)); - SAFE_FREE(tmplist); - return 0; - } +#else - unfixtarname(tmpstr, cliplist[clipcount], strlen(cliplist[clipcount]) + 1, True); - tmplist[clipcount] = tmpstr; - DEBUG(5, ("Processed an item, %s\n", tmpstr)); +#define NOT_IMPLEMENTED DEBUG(0, ("tar mode not compiled. build with --with-libarchive\n")) - DEBUG(5, ("Cliplist is: %s\n", cliplist[0])); - } +int cmd_block(void) +{ + NOT_IMPLEMENTED; + return 1; +} - cliplist = tmplist; - must_free_cliplist = True; +int cmd_tarmode(void) +{ + NOT_IMPLEMENTED; + return 1; +} - newOptind += clipn; - } +int cmd_setmode(void) +{ + NOT_IMPLEMENTED; + return 1; +} - if (Optind+1=argc || !strcmp(argv[Optind], "-")) { - /* Sets tar handle to either 0 or 1, as appropriate */ - tarhandle=(tar_type=='c'); - /* - * Make sure that dbf points to stderr if we are using stdout for - * tar output - */ - if (tarhandle == 1) { - setup_logging("smbclient", DEBUG_STDERR); - } - if (!argv[Optind]) { - DEBUG(0,("Must specify tar filename\n")); - return 0; - } - if (!strcmp(argv[Optind], "-")) { - newOptind++; - } +int tar_process(struct tar* tar) +{ + NOT_IMPLEMENTED; + return 1; +} - } else { - if (tar_type=='c' && dry_run) { - tarhandle=-1; - } else if ((tar_type=='x' && (tarhandle = open(argv[Optind], O_RDONLY, 0)) == -1) - || (tar_type=='c' && (tarhandle=creat(argv[Optind], 0644)) < 0)) { - DEBUG(0,("Error opening local file %s - %s\n", argv[Optind], strerror(errno))); - return(0); - } - newOptind++; - } +int tar_parse_args(struct tar *tar, const char *flag, const char **val, int valsize) +{ + NOT_IMPLEMENTED; + return 1; +} - return newOptind; +bool tar_to_process(struct tar *tar) +{ + return false; +} + +struct tar *tar_get_ctx() +{ + return NULL; } + +#endif diff --git source3/client/clitar_proto.h source3/client/clitar_proto.h new file mode 100644 index 0000000..6520476 --- /dev/null +++ source3/client/clitar_proto.h @@ -0,0 +1,34 @@ +/* + * Unix SMB/CIFS implementation. + * Tar backup command extension + * Copyright (C) Aurélien Aptel 2013 + * + * 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 3 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, see . + */ + +#ifndef _CLITAR_PROTO_H_ +#define _CLITAR_PROTO_H_ + +struct tar; + +int cmd_block(void); +int cmd_tarmode(void); +int cmd_setmode(void); +int cmd_tar(void); +int tar_process(struct tar* tar); +int tar_parse_args(struct tar *tar, const char *flag, const char **val, int valsize); +bool tar_to_process(struct tar *tar); +struct tar *tar_get_ctx(void); + +#endif /* _CLITAR_PROTO_H_ */ diff --git source3/include/clitar.h source3/include/clitar.h deleted file mode 100644 index 69267fb..0000000 --- source3/include/clitar.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Unix SMB/CIFS implementation. - * clitar file format - * Copyright (C) Andrew Tridgell 2000 - * - * 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 3 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, see . - */ - -#ifndef _CLITAR_H -#define _CLITAR_H - -#define TBLOCK 512 -#define NAMSIZ 100 -union hblock { - char dummy[TBLOCK]; - struct header { - char name[NAMSIZ]; - char mode[8]; - char uid[8]; - char gid[8]; - char size[12]; - char mtime[12]; - char chksum[8]; - char linkflag; - char linkname[NAMSIZ]; - } dbuf; -}; - -#endif /* _CLITAR_H */ diff --git source3/selftest/tests.py source3/selftest/tests.py index 85d67d6..c2bedaf 100755 --- source3/selftest/tests.py +++ source3/selftest/tests.py @@ -197,8 +197,39 @@ for env in ["s3dc"]: # encrypted plantestsuite("samba3.blackbox.smbclient_s3.crypt (%s)" % env, env, [os.path.join(samba3srcdir, "script/tests/test_smbclient_s3.sh"), '$SERVER', '$SERVER_IP', '$DOMAIN', '$USERNAME', '$PASSWORD', '$USERID', '$LOCAL_PATH', '$PREFIX', smbclient3, wbinfo, net, configuration, "-e"]) - # Test smbclient/tarmode - plantestsuite("samba3.blackbox.smbclient_tarmode (%s)" % env, env, [os.path.join(samba3srcdir, "script/tests/test_smbclient_tarmode.sh"), '$SERVER', '$SERVER_IP', '$USERNAME', '$PASSWORD', '$LOCAL_PATH', '$PREFIX', smbclient3, configuration]) + + # + # tar command tests + # + + # find config.h + try: + config_h = os.environ["CONFIG_H"] + except KeyError: + config_h = os.path.join(samba4bindir, "default/include/config.h") + + # see if libarchive is supported + f = open(config_h, 'r') + try: + have_libarchive = ("HAVE_LIBARCHIVE 1" in f.read()) + finally: + f.close() + + # tar command enabled only if built with libarchive + if have_libarchive: + # Test smbclient/tarmode + plantestsuite("samba3.blackbox.smbclient_tarmode (%s)" % env, env, + [os.path.join(samba3srcdir, "script/tests/test_smbclient_tarmode.sh"), + '$SERVER', '$SERVER_IP', '$USERNAME', '$PASSWORD', + '$LOCAL_PATH', '$PREFIX', smbclient3, configuration]) + + # Test suite for new smbclient/tar with libarchive (GSoC 13) + plantestsuite("samba3.blackbox.smbclient_tar (%s)" % env, env, + [os.path.join(samba3srcdir, "script/tests/test_smbclient_tarmode.pl"), + '-n', '$SERVER', '-i', '$SERVER_IP', '-s', 'tmp', + '-u', '$USERNAME', '-p', '$PASSWORD', '-l', '$LOCAL_PATH', + '-d', '$PREFIX', '-b', smbclient3, + '--subunit', '--', configuration]) #TODO encrypted against member, with member creds, and with DC creds plantestsuite("samba3.blackbox.net.misc", "s3dc:local", diff --git source3/wscript source3/wscript index 271314d..fba0133 100644 --- source3/wscript +++ source3/wscript @@ -43,6 +43,7 @@ def set_options(opt): opt.SAMBA3_ADD_OPTION('dmapi', default=None) # None means autodetection opt.SAMBA3_ADD_OPTION('fam', default=None) # None means autodetection opt.SAMBA3_ADD_OPTION('profiling-data', default=False) + opt.SAMBA3_ADD_OPTION('libarchive', default=None) opt.SAMBA3_ADD_OPTION('cluster-support', default=None) @@ -193,6 +194,17 @@ main() { elif check_for_fam: Logs.warn('no suitable FAM library found') + # check for libarchive (tar command in smbclient) + # None means autodetect, True/False means enable/disable + conf.env['archive_lib'] = '' + if Options.options.with_libarchive is not False: + libarchive_mandatory = Options.options.with_libarchive == True + Logs.info("Checking for libarchive existence") + if conf.CHECK_BUNDLED_SYSTEM('libarchive', minversion='3.1.2'): + conf.env['archive_lib'] = 'libarchive' + elif libarchive_mandatory: + conf.fatal('libarchive support requested, but no suitable pkgconfig found') + # check for DMAPI libs Logs.info("Checking for DMAPI library existence") conf.env['dmapi_lib'] = '' diff --git source3/wscript_build source3/wscript_build index e0432bf..9943bbe 100755 --- source3/wscript_build +++ source3/wscript_build @@ -1261,7 +1261,8 @@ bld.SAMBA3_BINARY('client/smbclient', libsmb msrpc3 RPC_NDR_SRVSVC - cli_smb_common''') + cli_smb_common + ''' + bld.env['archive_lib']) bld.SAMBA3_BINARY('net', source=NET_SRC,