I'm managing a large collection of network devices which, once per month, I want to download the configuration from for safekeeping and historical reasons. Up to now it always was a fight with keeping track of the names of new devices to make sure their filenames were touched in /tftpboot and set with the proper permissions. The -w option resolved this a little bit, but it still caused the configurations to be overwritten without my knowledge. So I added the -W option, which behaves the same as -w, but adds a three digit postfix to the file to be created so it will never overwrite old ones.
On Thu, 30 Nov 2006, Edwin Groothuis wrote: > So I added the -W option, which behaves the same as -w, but adds a > three digit postfix to the file to be created so it will never > overwrite old ones. could you make it a timestamp like ..? YYYYMMDD-hhmm[ss] or YYYYMMDD-hhmm[ss][-nn] or YYYYMMDD-[nn] With optional [ss] seconds and optional [nn] serial (with "optional" as in you decide which one makes most sense; I'd prefer the former). -- Bjoern A. Zeeb bzeeb at Zabbadoz dot NeT
Responsible Changed From-To: freebsd-bugs->bz If I make it the YYYYMMDD-nn one, will you commit it?
On Thu, May 24, 2007 at 01:00:22PM +0000, Bjoern A. Zeeb wrote: > On Thu, 24 May 2007, Edwin Groothuis wrote: > > >Synopsis: [patch] tftpd(8) - improve -w option to support unique filenames > > > >Responsible-Changed-From-To: freebsd-bugs->bz > >Responsible-Changed-By: edwin > >Responsible-Changed-When: Thu May 24 12:42:15 UTC 2007 > >Responsible-Changed-Why: > >If I make it the YYYYMMDD-nn one, will you commit it? > > I'll review it and if it's ok will commit it. > At least if it's, like I remember, fine otherwise;-) > > Yes, definitively would be a great feature to have, so we should work > this out. New ones attached: --- tftpd.c.orig Tue Nov 28 22:54:43 2006 +++ tftpd.c Sun May 27 10:01:38 2007 @@ -110,6 +110,7 @@ static int logging; static int ipchroot; static int create_new = 0; +static int increase_name = 0; static mode_t mask = S_IWGRP|S_IWOTH; static const char *errtomsg(int); @@ -134,7 +135,7 @@ tzset(); /* syslog in localtime */ openlog("tftpd", LOG_PID | LOG_NDELAY, LOG_FTP); - while ((ch = getopt(argc, argv, "cClns:u:U:w")) != -1) { + while ((ch = getopt(argc, argv, "cClns:u:U:wW")) != -1) { switch (ch) { case 'c': ipchroot = 1; @@ -160,6 +161,10 @@ case 'w': create_new = 1; break; + case 'W': + create_new = 1; + increase_name = 1; + break; default: syslog(LOG_WARNING, "ignoring unknown option -%c", ch); } @@ -610,9 +615,51 @@ if (mode == RRQ) fd = open(filename, O_RDONLY); else { - if (create_new) - fd = open(filename, O_WRONLY|O_TRUNC|O_CREAT, 0666); - else + if (create_new) { + if (increase_name) { + int i; + time_t tval; + struct tm lt; + char yyyymmdd[10]; + + /* Make sure the new filename is not too long */ + if (strlen(filename) > MAXPATHLEN-12) { + syslog(LOG_WARNING, + "Filename too long (%ld characters, %d maximum)", + strlen(filename), MAXPATHLEN-12); + return (EACCESS); + } + + /* If we can't find anything, fail */ + fd = -1; + + /* Append the YYYYMMDD string to the filename */ + time(&tval); + lt = *localtime(&tval); + strftime(yyyymmdd, sizeof(yyyymmdd), + "%Y%m%d", <); + + /* Find the first file which doesn't exist */ + for (i = 0; i < 100; i++) { + char newname[MAXPATHLEN]; + struct stat sb; + int ret; + + sprintf(newname, "%s.%s.%02d", + filename, yyyymmdd, i); + ret = stat(newname, &sb); + if (ret == -1 && errno == ENOENT) { + fd = open(newname, + O_WRONLY|O_TRUNC| + O_CREAT, 0666); + break; + } + } + } else { + fd = open(filename, + O_WRONLY|O_TRUNC|O_CREAT, 0666); + } + } else fd = open(filename, O_WRONLY|O_TRUNC); } if (fd < 0) --- tftpd.8.orig Tue Nov 28 22:54:51 2006 +++ tftpd.8 Sun May 27 10:04:20 2007 @@ -40,7 +40,7 @@ .Nd Internet Trivial File Transfer Protocol server .Sh SYNOPSIS .Nm /usr/libexec/tftpd -.Op Fl cClnw +.Op Fl cClnwW .Op Fl s Ar directory .Op Fl u Ar user .Op Fl U Ar umask @@ -171,10 +171,17 @@ The default is 022 .Pq Dv S_IWGRP | S_IWOTH . .It Fl w -Allow writes requests to create new files. +Allow write requests to create new files. By default .Nm -requires that the file specified in a write request exist. +requires that the file specified in a write request exist. Note that this +only works in directories writable by the user specified with +.Fl u +option. +.It Fl W +As +.Fl w +but append a YYYYMMDD.nn sequence number to the end of the filename. .El .Sh SEE ALSO .Xr tftp 1 , @@ -200,10 +207,14 @@ .Fl u option was introduced in .Fx 4.2 , -and the +the .Fl c option was introduced in -.Fx 4.3 . +.Fx 4.3 , +and the +.Fl W +option was introduced in +.Fx 6.3 . .Sh BUGS Files larger than 33488896 octets (65535 blocks) cannot be transferred without client and server supporting blocksize negotiation (RFC1783). -- Edwin Groothuis | Personal website: http://www.mavetju.org edwin@mavetju.org | Weblog: http://www.mavetju.org/weblog/
bz 2007-06-03 15:32:06 UTC FreeBSD src repository Modified files: libexec/tftpd tftpd.8 Log: Correct a typo. PR: 106049 Submitted by: edwin (as part of a larger patch) Revision Changes Path 1.21 +1 -1 src/libexec/tftpd/tftpd.8 _______________________________________________ cvs-all@freebsd.org mailing list http://lists.freebsd.org/mailman/listinfo/cvs-all To unsubscribe, send any mail to "cvs-all-unsubscribe@freebsd.org"
Hi, [resend with Cc: bug-followup] I thought a bit more about the patch while I was just about to commit it. There were three things that came to my mind: a) reduced the char yyyymmdd[10] to [9] which should be enough - am I missing something? b) make it possible to (locally) adopt the maximum for .nn . While I still didn't want ot make it -W nn, I went for an overwritable #define . But if nn could be > 99, the initial length check would be wrong so I collapsed it into the sprintf and made it an snprintf checking the result. c) if I am not mistaken, if -W is turned on, no matter what the user will do, all files from write requests will have the serial suffix. But sometimes you do not want that. So I thought I'd do it the good old way: if the file without the serial already exists, then overwrite it. If the file does not exists, then append the serial and try to find the first unused serial. This allows one to touch img.gz && chmod 666 img.gz -> put img.gz , but also to just 'put config' and get config.YYYYMMDD.nn if there is no file named 'config' before. I have put up an untested, updated patch at (also attached below): http://sources.zabbadoz.net/freebsd/patchset/EXPERIMENTAL/tftp-pr106049-p5.diff I'd only like to put this up for discussion. If you do not like it just go ahead and commit your version;) Index: tftpd.8 =================================================================== RCS file: /shared/mirror/FreeBSD/r/ncvs/src/libexec/tftpd/tftpd.8,v retrieving revision 1.21 diff -u -p -r1.21 tftpd.8 --- tftpd.8 3 Jun 2007 15:32:06 -0000 1.21 +++ tftpd.8 4 Jun 2007 21:30:26 -0000 @@ -40,7 +40,7 @@ .Nd Internet Trivial File Transfer Protocol server .Sh SYNOPSIS .Nm tftpd -.Op Fl cClnw +.Op Fl cClnwW .Op Fl s Ar directory .Op Fl u Ar user .Op Fl U Ar umask @@ -184,6 +184,19 @@ Allow write requests to create new files By default .Nm requires that the file specified in a write request exist. +Note that this only works in directories writable by the user specified +with +.Fl u +option. +.It Fl W +Like +.Fl w +if the file already exists. +If the file does not exist, additionally append a YYYYMMDD.nn serial to the +end of the filename. Increment nn until we find the first filename with a +serial that does not exist. +In case nn exceeds the compiled in maximum, which defaults to 99, deny +the write request. .El .Sh SEE ALSO .Xr tftp 1 , @@ -212,10 +225,14 @@ the .Fl u option was introduced in .Fx 4.2 , -and the +the .Fl c option was introduced in -.Fx 4.3 . +.Fx 4.3 , +and the +.Fl W +option was introduced in +.Fx 6.3 . .Sh BUGS Files larger than 33488896 octets (65535 blocks) cannot be transferred without client and server supporting blocksize negotiation (RFC1783). Index: tftpd.c =================================================================== RCS file: /shared/mirror/FreeBSD/r/ncvs/src/libexec/tftpd/tftpd.c,v retrieving revision 1.37 diff -u -p -r1.37 tftpd.c --- tftpd.c 31 May 2005 17:22:53 -0000 1.37 +++ tftpd.c 4 Jun 2007 21:30:26 -0000 @@ -91,6 +91,10 @@ char buf[PKTSIZE]; char ackbuf[PKTSIZE]; struct sockaddr_storage from; +#ifndef INCREASE_NAME_MAX_SEQ +#define INCREASE_NAME_MAX_SEQ 100 +#endif + void tftp(struct tftphdr *, int); static void unmappedaddr(struct sockaddr_in6 *); @@ -110,6 +114,7 @@ static int suppress_naks; static int logging; static int ipchroot; static int create_new = 0; +static int increase_name = 0; static mode_t mask = S_IWGRP|S_IWOTH; static const char *errtomsg(int); @@ -134,7 +139,7 @@ main(int argc, char *argv[]) tzset(); /* syslog in localtime */ openlog("tftpd", LOG_PID | LOG_NDELAY, LOG_FTP); - while ((ch = getopt(argc, argv, "cClns:u:U:w")) != -1) { + while ((ch = getopt(argc, argv, "cClns:u:U:wW")) != -1) { switch (ch) { case 'c': ipchroot = 1; @@ -160,6 +165,10 @@ main(int argc, char *argv[]) case 'w': create_new = 1; break; + case 'W': + create_new = 1; + increase_name = 1; + break; default: syslog(LOG_WARNING, "ignoring unknown option -%c", ch); } @@ -610,9 +619,66 @@ validate_access(char **filep, int mode) if (mode == RRQ) fd = open(filename, O_RDONLY); else { - if (create_new) - fd = open(filename, O_WRONLY|O_TRUNC|O_CREAT, 0666); - else + if (create_new) { + int fexists = 0; + struct stat sb; + + /* + * Only create a file with a serial suffix if the file + * without one does not yet exist. + */ + fexists = stat(filename, &sb); + if (fexists == -1 && errno == ENOENT && increase_name) { + int i; + time_t tval; + struct tm *lt; + char yyyymmdd[9]; + + /* Fail if we cannot find anything */ + fd = -1; + + /* Append the YYYYMMDD string to the filename */ + time(&tval); + lt = localtime(&tval); + if (strftime(yyyymmdd, sizeof(yyyymmdd), + "%Y%m%d", lt) != 8) { + syslog(LOG_WARNING, "Cannot format " + "date for filename."); + return (EACCESS); + } + + /* Find the first file which doesn't exist */ + for (i = 0; i < INCREASE_NAME_MAX_SEQ; i++) { + char newname[MAXPATHLEN]; + int len; + + /* + * Make sure the new filename is + * not too long. + */ + if ((len = snprintf(newname, MAXPATHLEN, + "%s.%s.%02d", filename, yyyymmdd, + i)) >= MAXPATHLEN) { + syslog(LOG_WARNING, + "Filename with serial too " + "long (%d characters, " + "%d maximum)", + len, MAXPATHLEN-1); + return (ENAMETOOLONG); + } + fexists = stat(newname, &sb); + if (fexists == -1 && errno == ENOENT) { + fd = open(newname, + O_WRONLY|O_TRUNC|O_CREAT, + 0666); + break; + } + } + } else { + fd = open(filename, + O_WRONLY|O_TRUNC|O_CREAT, 0666); + } + } else fd = open(filename, O_WRONLY|O_TRUNC); } if (fd < 0) -- Bjoern A. Zeeb bzeeb at Zabbadoz dot NeT Software is harder than hardware so better get it right the first time.
Responsible Changed From-To: bz->edwin You are source committer yourself these days, so feel free to handle your PR yourself.
update for mentor -- Edwin Groothuis | Personal website: http://www.mavetju.org edwin@mavetju.org | Weblog: http://www.mavetju.org/weblog/
State Changed From-To: open->patched Commited to HEAD
edwin 2007-11-23 00:05:29 UTC FreeBSD src repository Modified files: libexec/tftpd Makefile tftpd.8 tftpd.c Log: Add the -W options, which acts the same as -w but will generate unique names based on the submitted filename, a strftime(3) format string and a two digit sequence number. By default the strftime(3) format string is %Y%m%d (YYYYMMDD), but this can be changed by the -F option. PR: bin/106049 (based on patch in that PR) Approved by: grog@ (mentor) Revision Changes Path 1.10 +2 -1 src/libexec/tftpd/Makefile 1.22 +29 -3 src/libexec/tftpd/tftpd.8 1.38 +74 -5 src/libexec/tftpd/tftpd.c _______________________________________________ cvs-all@freebsd.org mailing list http://lists.freebsd.org/mailman/listinfo/cvs-all To unsubscribe, send any mail to "cvs-all-unsubscribe@freebsd.org"
Responsible Changed From-To: edwin->freebsd-bgus Give back into the pool until later.
Responsible Changed From-To: freebsd-bgus->freebsd-bugs Fix name
Author: marius Date: Wed Sep 22 20:27:59 2010 New Revision: 213027 URL: http://svn.freebsd.org/changeset/base/213027 Log: MFC: r173852 Add the -W options, which acts the same as -w but will generate unique names based on the submitted filename, a strftime(3) format string and a two digit sequence number. By default the strftime(3) format string is %Y%m%d (YYYYMMDD), but this can be changed by the -F option. PR: bin/106049 (based on patch in that PR) Approved by: grog@ (mentor) Modified: stable/7/libexec/tftpd/Makefile stable/7/libexec/tftpd/tftpd.8 stable/7/libexec/tftpd/tftpd.c Directory Properties: stable/7/libexec/tftpd/ (props changed) Modified: stable/7/libexec/tftpd/Makefile ============================================================================== --- stable/7/libexec/tftpd/Makefile Wed Sep 22 20:17:34 2010 (r213026) +++ stable/7/libexec/tftpd/Makefile Wed Sep 22 20:27:59 2010 (r213027) @@ -5,6 +5,7 @@ PROG= tftpd SRCS= tftpd.c tftpsubs.c DPADD= ${LIBUTIL} LDADD= -lutil +WFORMAT=0 MAN= tftpd.8 CFLAGS+=-I${.CURDIR}/../../usr.bin/tftp .PATH: ${.CURDIR}/../../usr.bin/tftp Modified: stable/7/libexec/tftpd/tftpd.8 ============================================================================== --- stable/7/libexec/tftpd/tftpd.8 Wed Sep 22 20:17:34 2010 (r213026) +++ stable/7/libexec/tftpd/tftpd.8 Wed Sep 22 20:27:59 2010 (r213027) @@ -40,7 +40,8 @@ .Nd Internet Trivial File Transfer Protocol server .Sh SYNOPSIS .Nm tftpd -.Op Fl cClnw +.Op Fl cClnwW +.Op Fl F Ar strftime-format .Op Fl s Ar directory .Op Fl u Ar user .Op Fl U Ar umask @@ -142,6 +143,13 @@ except it falls back to specified via .Fl s if a directory does not exist for the client's IP. +.It Fl F +Use this +.Xr strftime 3 +compatible format string for the creation of the suffix if +.Fl W +is specified. +By default the string "%Y%m%d" is used. .It Fl l Log all requests using .Xr syslog 3 @@ -184,6 +192,17 @@ Allow write requests to create new files By default .Nm requires that the file specified in a write request exist. +Note that this only works in directories writable by the user +specified with +.Fl u +option +.It Fl W +As +.Fl w +but append a YYYYMMDD.nn sequence number to the end of the filename. +Note that the string YYYYMMDD can be changed the +.Fl F +option. .El .Sh SEE ALSO .Xr tftp 1 , @@ -212,10 +231,17 @@ the .Fl u option was introduced in .Fx 4.2 , -and the +the .Fl c option was introduced in -.Fx 4.3 . +.Fx 4.3 , +and the +.Fl F +and +.Fl W +options were introduced in +.Fx 7 . +.Pp .Sh BUGS Files larger than 33488896 octets (65535 blocks) cannot be transferred without client and server supporting blocksize negotiation (RFC1783). Modified: stable/7/libexec/tftpd/tftpd.c ============================================================================== --- stable/7/libexec/tftpd/tftpd.c Wed Sep 22 20:17:34 2010 (r213026) +++ stable/7/libexec/tftpd/tftpd.c Wed Sep 22 20:27:59 2010 (r213027) @@ -110,6 +110,8 @@ static int suppress_naks; static int logging; static int ipchroot; static int create_new = 0; +static char *newfile_format = "%Y%m%d"; +static int increase_name = 0; static mode_t mask = S_IWGRP|S_IWOTH; static const char *errtomsg(int); @@ -134,7 +136,7 @@ main(int argc, char *argv[]) tzset(); /* syslog in localtime */ openlog("tftpd", LOG_PID | LOG_NDELAY, LOG_FTP); - while ((ch = getopt(argc, argv, "cClns:u:U:w")) != -1) { + while ((ch = getopt(argc, argv, "cCF:lns:u:U:wW")) != -1) { switch (ch) { case 'c': ipchroot = 1; @@ -142,6 +144,9 @@ main(int argc, char *argv[]) case 'C': ipchroot = 2; break; + case 'F': + newfile_format = optarg; + break; case 'l': logging = 1; break; @@ -160,6 +165,10 @@ main(int argc, char *argv[]) case 'w': create_new = 1; break; + case 'W': + create_new = 1; + increase_name = 1; + break; default: syslog(LOG_WARNING, "ignoring unknown option -%c", ch); } @@ -513,6 +522,57 @@ option_fail: FILE *file; /* + * Find the next value for YYYYMMDD.nn when the file to be written should + * be unique. Due to the limitations of nn, we will fail if nn reaches 100. + * Besides, that is four updates per hour on a file, which is kind of + * execessive anyway. + */ +static int +find_next_name(char *filename, int *fd) +{ + int i; + time_t tval; + size_t len; + struct tm lt; + char yyyymmdd[MAXPATHLEN]; + char newname[MAXPATHLEN]; + struct stat sb; + int ret; + + /* Create the YYYYMMDD part of the filename */ + time(&tval); + lt = *localtime(&tval); + len = strftime(yyyymmdd, sizeof(yyyymmdd), newfile_format, <); + if (len == 0) { + syslog(LOG_WARNING, + "Filename suffix too long (%d characters maximum)", + MAXPATHLEN); + return (EACCESS); + } + + /* Make sure the new filename is not too long */ + if (strlen(filename) > MAXPATHLEN - len - 5) { + syslog(LOG_WARNING, + "Filename too long (%d characters, %d maximum)", + strlen(filename), MAXPATHLEN - len - 5); + return (EACCESS); + } + + /* Find the first file which doesn't exist */ + for (i = 0; i < 100; i++) { + sprintf(newname, "%s.%s.%02d", filename, yyyymmdd, i); + *fd = open(newname, + O_WRONLY | O_CREAT | O_EXCL, + S_IRUSR | S_IWUSR | S_IRGRP | + S_IWGRP | S_IROTH | S_IWOTH); + if (*fd > 0) + return 0; + } + + return (EEXIST); +} + +/* * Validate file access. Since we * have no uid or gid, for now require * file to exist and be publicly @@ -528,6 +588,7 @@ validate_access(char **filep, int mode) { struct stat stbuf; int fd; + int error; struct dirlist *dirp; static char pathname[MAXPATHLEN]; char *filename = *filep; @@ -610,10 +671,18 @@ validate_access(char **filep, int mode) if (mode == RRQ) fd = open(filename, O_RDONLY); else { - if (create_new) - fd = open(filename, O_WRONLY|O_TRUNC|O_CREAT, 0666); - else - fd = open(filename, O_WRONLY|O_TRUNC); + if (create_new) { + if (increase_name) { + error = find_next_name(filename, &fd); + if (error > 0) + return (error + 100); + } else + fd = open(filename, + O_WRONLY | O_TRUNC | O_CREAT, + S_IRUSR | S_IWUSR | S_IRGRP | + S_IWGRP | S_IROTH | S_IWOTH ); + } else + fd = open(filename, O_WRONLY | O_TRUNC); } if (fd < 0) return (errno + 100); _______________________________________________ svn-src-all@freebsd.org mailing list http://lists.freebsd.org/mailman/listinfo/svn-src-all To unsubscribe, send any mail to "svn-src-all-unsubscribe@freebsd.org"
State Changed From-To: patched->closed Looks like this PR can be closed, since all changes have been MFC'ed to stable/7.