Subject: bin/4231: ftp(1) doesn't create directories
To: None <gnats-bugs@gnats.netbsd.org>
From: Jaromir Dolecek <dolecek@ics.muni.cz>
List: netbsd-bugs
Date: 10/07/1997 00:53:47
>Number: 4231
>Category: bin
>Synopsis: ftp(1) doesn't create directories
>Confidential: no
>Severity: non-critical
>Priority: low
>Responsible: bin-bug-people (Utility Bug People)
>State: open
>Class: change-request
>Submitter-Id: net
>Arrival-Date: Mon Oct 6 16:05:01 1997
>Last-Modified:
>Originator: Jaromir Dolecek
>Organization:
ICS MU Brno, Czech Republic
>Release: 1.2G
>Environment:
System: NetBSD beleg.ics.muni.cz 1.2G NetBSD 1.2G
Architecture: i386
>Description:
Commands ``*put'' and ``*get'' doesn't handle case, where
target file cannot be writtent due to not-existent
directory, even throught the director(y|ies) can be created
by sequence of mkdir(2) or MKD commands.
>How-To-Repeat:
ftp> get pub/csacek/README
local: pub/csacek/README remote: pub/csacek/README
ftp: local: pub/csacek/README: No such file or directory
ftp>
>Fix:
"Un*x machines" referred to in following text stands
for a few I've tried - it's NetBSD, SunOS, Sun Solaris.
The code assumes:
root exists on every machine and needn't to be created
ftp server's response to STO? command has code
553 or 550, if the file cannot be put
due to not-existent directory (Un*x machines
use 553, NT 4.0 box 550)
even through MKD command hasn't succeeded, it's possible
it can succeed for sub-directory (is case
on special-configured NT boxes at least)
path separators on local machine are '/'
path separators used for remote files are '/' or
'\\' ('/' checked first, no translation done
in case of backslash)
Other changes introduced by a patch:
ftp.c: sendrequest(), recvrequest():
on one place the code was plain copied, just
because of passing other parameters to command(); now
the code exists just once and only the parameters
are changed
ftp.c: getreply(): new global variable getreply_silent
was added; if it's not 0, reply from ftp server
is not copied to output
The code is tested just for ``*put'' and ``*get'', but it
actually should work for anything using recvrequest() or
sendrequest().
The test was not very very hard; I've just tried a few typical
cases and all seemed to work. I've touched the code only
where it was really necessary, so I don't expect it would break
anything.
IMHO it's nice feature; the last one I need and miss in
our superior ftp client. In adittion, it's very robust;
at least its implementation is better than equivalent ncftp(1)'s
function.
The patch follows. It's done against ftp as of about a week ago.
diff -c ../ftp_old/cmds.c ./cmds.c
*** ../ftp_old/cmds.c Sun Sep 14 15:39:35 1997
--- ./cmds.c Mon Oct 6 22:41:32 1997
***************
*** 669,674 ****
--- 669,675 ----
printf("Hash mark printing: %s; Mark count: %d; Progress bar: %s.\n",
onoff(hash), mark, onoff(progress));
printf("Use of PORT cmds: %s.\n", onoff(sendport));
+ printf("Automatic directory creating: %s.\n", onoff(makedirs));
#ifndef SMALL
printf("Command line editing: %s.\n", onoff(editing));
#endif /* !SMALL */
***************
*** 1603,1608 ****
--- 1604,1617 ----
{
code = togglevar(argc, argv, &crflag, "Carriage Return stripping");
+ }
+
+ void
+ setmakedirs(argc, argv)
+ int argc;
+ char *argv[];
+ {
+ code = togglevar(argc, argv, &makedirs, "Automatic directory making");
}
void
diff -c ../ftp_old/cmdtab.c ./cmdtab.c
*** ../ftp_old/cmdtab.c Sun Sep 14 15:39:35 1997
--- ./cmdtab.c Mon Oct 6 21:20:38 1997
***************
*** 76,81 ****
--- 76,82 ----
char lpwdhelp[] = "print local working directory";
char lshelp[] = "list contents of remote directory";
char macdefhelp[] = "define a macro";
+ char makedirshelp[]= "toggle automatic creating of directories";
char mdeletehelp[] = "delete multiple files";
char mdirhelp[] = "list contents of multiple remote directories";
char mgethelp[] = "get multiple files";
***************
*** 169,174 ****
--- 170,176 ----
{ "lpwd", lpwdhelp, 0, 0, 0, CMPL0 lpwd },
{ "ls", lshelp, 1, 1, 1, CMPL(rl) ls },
{ "macdef", macdefhelp, 0, 0, 0, CMPL0 macdef },
+ { "makedirs", makedirshelp, 0, 0, 1, CMPL0 setmakedirs },
{ "mdelete", mdeletehelp, 1, 1, 1, CMPL(R) mdelete },
{ "mdir", mdirhelp, 1, 1, 1, CMPL(R) mls },
{ "mget", mgethelp, 1, 1, 1, CMPL(R) mget },
diff -c ../ftp_old/extern.h ./extern.h
*** ../ftp_old/extern.h Thu Sep 11 12:14:12 1997
--- ./extern.h Thu Sep 11 12:27:38 1997
***************
*** 135,140 ****
--- 135,141 ----
void setftmode __P((int, char **));
void setglob __P((int, char **));
void sethash __P((int, char **));
+ void setmakedirs __P((int, char **));
void setnmap __P((int, char **));
void setntrans __P((int, char **));
void setpassive __P((int, char **));
diff -c ../ftp_old/ftp.1 ./ftp.1
*** ../ftp_old/ftp.1 Thu Sep 11 12:14:12 1997
--- ./ftp.1 Mon Oct 6 22:40:32 1997
***************
*** 431,436 ****
--- 431,439 ----
on the second pass it is replaced by the second argument, and so on.
A `\e' followed by any character is replaced by that character.
Use the `\e' to prevent special treatment of the `$'.
+ .It Ic makedirs
+ Automatically create directories, if they don't exist. In interactive mode,
+ user will be prompted for confirmation first.
.It Ic mdelete Op Ar remote-files
Delete the
.Ar remote-files
diff -c ../ftp_old/ftp.c ./ftp.c
*** ../ftp_old/ftp.c Sun Sep 14 15:39:35 1997
--- ./ftp.c Tue Oct 7 00:16:56 1997
***************
*** 263,268 ****
--- 263,270 ----
}
char reply_string[BUFSIZ]; /* first line of previous reply */
+ int getreply_silent=0; /* true if getreply() shouldn't */
+ /* copy reply to output */
int
getreply(expecteof)
***************
*** 321,327 ****
if (proxflag &&
(dig == 1 || (dig == 5 && verbose == 0)))
printf("%s:", hostname);
! (void)putchar(c);
}
if (dig < 4 && isdigit(c))
code = code * 10 + (c - '0');
--- 323,331 ----
if (proxflag &&
(dig == 1 || (dig == 5 && verbose == 0)))
printf("%s:", hostname);
!
! if (!getreply_silent)
! (void)putchar(c);
}
if (dig < 4 && isdigit(c))
code = code * 10 + (c - '0');
***************
*** 348,355 ****
*cp++ = c;
}
if (verbose > 0 || (verbose > -1 && n == '5')) {
! (void)putchar(c);
! (void)fflush (stdout);
}
if (line == 0) {
size_t len = cp - current_line;
--- 352,361 ----
*cp++ = c;
}
if (verbose > 0 || (verbose > -1 && n == '5')) {
! if (!getreply_silent) {
! (void)putchar(c);
! (void)fflush (stdout);
! }
}
if (line == 0) {
size_t len = cp - current_line;
***************
*** 544,571 ****
restart_point = 0;
lmode = "r+w";
}
! if (remote) {
! if (command("%s %s", cmd, remote) != PRELIM) {
! (void)signal(SIGINT, oldintr);
! (void)signal(SIGINFO, oldinti);
! progress = oprogress;
! if (oldintp)
! (void)signal(SIGPIPE, oldintp);
! if (closefunc != NULL)
! (*closefunc)(fin);
! return;
}
! } else
! if (command("%s", cmd) != PRELIM) {
! (void)signal(SIGINT, oldintr);
! (void)signal(SIGINFO, oldinti);
! progress = oprogress;
! if (oldintp)
! (void)signal(SIGPIPE, oldintp);
! if (closefunc != NULL)
! (*closefunc)(fin);
! return;
}
dout = dataconn(lmode);
if (dout == NULL)
goto abort;
--- 550,617 ----
restart_point = 0;
lmode = "r+w";
}
! if (command(remote ? "%s %s" : "%s", cmd, remote) != PRELIM) {
! int abort=1;
!
! if (remote && makedirs && (code == 553 || code == 550)
! && strncmp(cmd, "STO", 3) == 0)
! {
! /* catch both STOR and STOU */
! /* work only when remote server returned */
! /* File Not Found or Requested action not taken */
! char *dir, *temp, pathsep='/';
! int len;
!
! /* copy directory part of cmd parameter */
! temp = strrchr(remote, '/');
! if (temp == NULL)
! temp = strchr(remote, '\\'), pathsep = '\\';
! /* some legacy machines use backslash as separator */
!
! if (temp) {
! len = temp-remote+1;
! dir = (char *) alloca(len+1);
! strncpy(dir, remote, len);
! dir[len] = 0;
! }
!
! if (temp && confirm("Create remote directory", dir) )
! {
! char *argv[2]; /* for makedir() */
!
! argv[0] = (char *) "";
! argv[1] = dir;
! temp = dir;
! while((temp = strchr(temp, pathsep))) {
! if (temp == dir)
! continue; /* root needn't to be created */
! *temp = '\0';
!
! getreply_silent=1;
! makedir(2, argv);
! getreply_silent=0;
! *temp = pathsep;
! temp++; /* shift behind the slash */
! }
! if (temp == NULL && abort) {
! if (command(remote ? "%s %s" : "%s", cmd,
! remote) == PRELIM)
! abort = 0;
! }
! }
}
!
! if (abort) {
! (void)signal(SIGINT, oldintr);
! (void)signal(SIGINFO, oldinti);
! progress = oprogress;
! if (oldintp)
! (void)signal(SIGPIPE, oldintp);
! if (closefunc != NULL)
! (*closefunc)(fin);
! return;
}
+ }
dout = dataconn(lmode);
if (dout == NULL)
goto abort;
***************
*** 754,781 ****
oldinti = signal(SIGINFO, psummary);
if (strcmp(local, "-") && *local != '|') {
if (access(local, 2) < 0) {
! char *dir = strrchr(local, '/');
! if (errno != ENOENT && errno != EACCES) {
warn("local: %s", local);
(void)signal(SIGINT, oldintr);
(void)signal(SIGINFO, oldinti);
code = -1;
return;
}
! if (dir != NULL)
! *dir = 0;
! d = access(dir == local ? "/" : dir ? local : ".", 2);
! if (dir != NULL)
! *dir = '/';
! if (d < 0) {
warn("local: %s", local);
(void)signal(SIGINT, oldintr);
(void)signal(SIGINFO, oldinti);
code = -1;
return;
! }
! if (!runique && errno == EACCES &&
chmod(local, 0600) < 0) {
warn("local: %s", local);
(void)signal(SIGINT, oldintr);
--- 800,859 ----
oldinti = signal(SIGINFO, psummary);
if (strcmp(local, "-") && *local != '|') {
if (access(local, 2) < 0) {
! char *dir, *temp;
! int len, first_errno=errno;
! if (first_errno != ENOENT && first_errno != EACCES) {
warn("local: %s", local);
(void)signal(SIGINT, oldintr);
(void)signal(SIGINFO, oldinti);
code = -1;
return;
}
!
! temp = strrchr(local, '/');
! /* have to crerate copy of ``local'', as we need */
! /* to write to it and ``local'' is const */
! /* it's only directory part of ``local'' what is */
! /* needed, so copy just this */
! len = temp ? temp-local+1 : 1;
! dir = (char *) alloca(len+1);
! strncpy(dir, temp ? local : ".", len);
! dir[len] = '\0';
!
! if (access(dir, 2) < 0) {
! if (!makedirs || !confirm("Create local dir",dir))
! {
warn("local: %s", local);
(void)signal(SIGINT, oldintr);
(void)signal(SIGINFO, oldinti);
code = -1;
return;
! }
! else {
! temp = dir;
! /* create all subdirectories too */
! while((temp = strchr(temp, '/'))) {
! if (temp == dir)
! continue;
! /* don't need to create root ;-) */
! *temp = 0;
! if (access(dir, 2) < 0
! && mkdir(dir,0755) < 0)
! {
! warn("local: %s: %s", local, dir);
! (void)signal(SIGINT, oldintr);
! (void)signal(SIGINFO, oldinti);
! code = -1;
! return;
! }
! *temp = '/';
! temp++; /* shift behind the slash */
! } /* while cycle */
! } /* else (!makedirs ...) */
! } /* if (access(dir, 2)<0) */
!
! if (!runique && first_errno == EACCES &&
chmod(local, 0600) < 0) {
warn("local: %s", local);
(void)signal(SIGINT, oldintr);
***************
*** 817,834 ****
if (is_retr && restart_point &&
command("REST %ld", (long) restart_point) != CONTINUE)
return;
! if (remote) {
! if (command("%s %s", cmd, remote) != PRELIM) {
! (void)signal(SIGINT, oldintr);
! (void)signal(SIGINFO, oldinti);
! return;
! }
! } else {
! if (command("%s", cmd) != PRELIM) {
! (void)signal(SIGINT, oldintr);
! (void)signal(SIGINFO, oldinti);
! return;
! }
}
din = dataconn("r");
if (din == NULL)
--- 895,904 ----
if (is_retr && restart_point &&
command("REST %ld", (long) restart_point) != CONTINUE)
return;
! if (command(remote ? "%s %s" : "%s", cmd, remote) != PRELIM) {
! (void)signal(SIGINT, oldintr);
! (void)signal(SIGINFO, oldinti);
! return;
}
din = dataconn("r");
if (din == NULL)
***************
*** 1247,1252 ****
--- 1317,1323 ----
int sunqe;
int runqe;
int mcse;
+ int mkdirs;
int ntflg;
char nti[17];
char nto[17];
***************
*** 1299,1304 ****
--- 1370,1377 ----
runique = op->runqe;
ip->mcse = mcase;
mcase = op->mcse;
+ ip->mkdirs = makedirs;
+ makedirs = op->mkdirs;
ip->ntflg = ntflag;
ntflag = op->ntflg;
(void)strncpy(ip->nti, ntin, sizeof(ip->nti) - 1);
diff -c ../ftp_old/ftp_var.h ./ftp_var.h
*** ../ftp_old/ftp_var.h Thu Sep 11 12:14:12 1997
--- ./ftp_var.h Tue Oct 7 00:06:01 1997
***************
*** 80,85 ****
--- 80,86 ----
int sunique; /* store files on server with unique name */
int runique; /* store local files with unique name */
int mcase; /* map upper to lower case for mget names */
+ int makedirs; /* automatically create directories */
int ntflag; /* use ntin ntout tables for name translation */
int mapflag; /* use mapin mapout templates on file names */
int preserve; /* preserve modification time on files */
>Audit-Trail:
>Unformatted: