Skip to content

Commit

Permalink
Set mtime on downloaded files to match the source file.
Browse files Browse the repository at this point in the history
  • Loading branch information
Colin Phipps committed Jan 22, 2009
1 parent 7e818ff commit 01de713
Show file tree
Hide file tree
Showing 6 changed files with 106 additions and 11 deletions.
1 change: 1 addition & 0 deletions c/NEWS
Expand Up @@ -4,6 +4,7 @@ Changes in 0.6
same.
- fix "try a smaller blocksize" failures when zsyncmakeing for huge compressed
files on 32bit systems
- preserve mtime on downloaded files
- fix potential crash when re/deallocating checksum hash in librcksum (patch
from Timothy Lee)
- explain status code errors better
Expand Down
39 changes: 37 additions & 2 deletions c/client.c
Expand Up @@ -26,6 +26,9 @@
#include <fcntl.h>
#include <errno.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <utime.h>

#ifdef WITH_DMALLOC
# include <dmalloc.h>
Expand Down Expand Up @@ -420,6 +423,26 @@ int fetch_remaining_blocks(struct zsync_state *zs) {
return 0;
}

static int set_mtime(char* filename, time_t mtime) {
struct stat s;
struct utimbuf u;

/* Get the access time, which I don't want to modify. */
if (stat(filename, &s) != 0) {
perror("stat");
return -1;
}

/* Set the modification time. */
u.actime = s.st_atime;
u.modtime = mtime;
if (utime(filename, &u) != 0) {
perror("utime");
return -1;
}
return 0;
}

/****************************************************************************
*
* Main program */
Expand All @@ -431,6 +454,7 @@ int main(int argc, char **argv) {
char *filename = NULL;
long long local_used;
char *zfname = NULL;
time_t mtime;

srand(getpid());
{ /* Option parsing */
Expand Down Expand Up @@ -592,6 +616,11 @@ int main(int argc, char **argv) {
}

free(temp_file);

/* Get any mtime that we is suggested to set for the file, and then shut
* down the zsync_state as we are done on the file transfer. Getting the
* current name of the file at the same time. */
mtime = zsync_mtime(zs);
temp_file = zsync_end(zs);

/* STEP 5: Move completed .part file into place as the final target */
Expand All @@ -613,13 +642,19 @@ int main(int argc, char **argv) {
ok = 0; /* Prevent overwrite of old file below */
}
}
if (ok)
if (rename(temp_file, filename) != 0) {
if (ok) {
/* Rename the file to the desired name */
if (rename(temp_file, filename) == 0) {
/* final, final thing - set the mtime on the file if we have one */
if (mtime != -1) set_mtime(filename, mtime);
}
else {
perror("rename");
fprintf(stderr,
"Unable to back up old file %s - completed download left in %s\n",
filename, temp_file);
}
}
free(oldfile_backup);
free(filename);
}
Expand Down
2 changes: 1 addition & 1 deletion c/doc/zsync.1
Expand Up @@ -15,7 +15,7 @@ Either a filename or a URL can be given on the command line \- this is the path
.LP
zsync downloads to your current directory. It looks for any file in the directory of the same name as the file to download. If it finds one, it assumes that this is an earlier or incomplete version of the new file to download, and scans this file for any blocks that it can use to build the target file. (It also looks for a file of the same name with .part appended, so it will automatically find previously interrupted zsync downloads and reuse the data already downloaded. If you know that the local file to use as input has a different name, you must use \fB\-i\fR)
.LP
zsync retrieves the rest of the target file over HTTP. Once the download is finished, the old version (if the new file wants the same name) is moved aside (a .zs\-old extension is appended).
zsync retrieves the rest of the target file over HTTP. Once the download is finished, the old version (if the new file wants the same name) is moved aside (a .zs\-old extension is appended). The modification time of the file is set to be the same as the remote source file (if specified in the .zsync).
.SH "OPTIONS"
.LP
.TP
Expand Down
42 changes: 38 additions & 4 deletions c/libzsync/zsync.c
Expand Up @@ -40,6 +40,7 @@
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <time.h>

#include <arpa/inet.h>

Expand Down Expand Up @@ -88,18 +89,23 @@ struct zsync_state {
int nzurl;

char *cur_filename; /* If we have taken the filename from rcksum, it is here */
char *filename; /* This is just the Filename: header from the .zsync */

/* Hints for the output file, from the .zsync */
char *filename; /* The Filename: header */
char *zfilename; /* ditto Z-Filename: */

char *gzopts; /* If we're recompressing the download afterwards, these are the options to gzip(1) */
char *gzhead; /* And this is the header of the gzip file (for the mtime) */

time_t mtime; /* MTime: from the .zsync, or -1 */
};

static int zsync_read_blocksums(struct zsync_state *zs, FILE * f,
int rsum_bytes, int checksum_bytes,
int seq_matches);
static int zsync_sha1(struct zsync_state *zs, int fh);
static int zsync_recompress(struct zsync_state *zs);
static time_t parse_822(const char* ts);

/* char*[] = append_ptrlist(&num, &char[], "to add")
* Crude data structure to store an ordered list of strings. This appends one
Expand Down Expand Up @@ -136,6 +142,9 @@ struct zsync_state *zsync_begin(FILE * f) {
if (!zs)
return NULL;

/* Any non-zero defaults here. */
zs->mtime = -1;

for (;;) {
char buf[1024];
char *p = NULL;
Expand Down Expand Up @@ -258,6 +267,9 @@ struct zsync_state *zsync_begin(FILE * f) {
}
}
}
else if (!strcmp(buf, "MTime")) {
zs->mtime = parse_822(p);
}
else if (!safelines || !strstr(safelines, buf)) {
fprintf(stderr,
"unrecognised tag %s - you need a newer version of zsync.\n",
Expand Down Expand Up @@ -328,6 +340,20 @@ static int zsync_read_blocksums(struct zsync_state *zs, FILE * f,
return 0;
}

/* parse_822(buf[])
* Parse an RFC822 date string. Returns a time_t, or -1 on failure.
* E.g. Tue, 25 Jul 2006 20:02:17 +0000
*/
static time_t parse_822(const char* ts) {
struct tm t;

if (strptime(ts, "%a, %d %b %Y %H:%M:%S %z", &t) == NULL
&& strptime(ts, "%d %b %Y %H:%M:%S %z", &t) == NULL) {
return -1;
}
return mktime(&t);
}

/* zsync_hint_decompress(self)
* Returns true if we think we'll be able to download compressed data to get
* the needed data to complete the target file */
Expand All @@ -341,13 +367,21 @@ int zsync_blocksize(const struct zsync_state *zs) {
return zs->blocksize;
}

/* zsync_filename(self)
* Returns a malloced string containing the filename where the final result of
* the download is/will be. Up to the caller to free the string. */
/* char* = zsync_filename(self)
* Returns the suggested filename to be used for the final result of this
* zsync. Malloced string to be freed by the caller. */
char *zsync_filename(const struct zsync_state *zs) {
return strdup(zs->gzhead && zs->zfilename ? zs->zfilename : zs->filename);
}

/* time_t = zsync_mtime(self)
* Returns the mtime on the original copy of the target; for the client program
* to set the mtime of the local file to match, if it so chooses.
* Or -1 if no mtime specified in the .zsync */
time_t zsync_mtime(const struct zsync_state *zs) {
return zs->mtime;
}

/* zsync_status(self)
* Returns 0 if we have no data in the target file yet.
* 1 if we have some but not all
Expand Down
2 changes: 2 additions & 0 deletions c/libzsync/zsync.h
Expand Up @@ -25,6 +25,8 @@ int zsync_hint_decompress(const struct zsync_state*);

/* zsync_filename - return the suggested filename from the .zsync file */
char* zsync_filename(const struct zsync_state*);
/* zsync_mtime - return the suggested mtime from the .zsync file */
time_t zsync_mtime(const struct zsync_state*);

/* zsync_rename_file - renames the temporary file used by zsync to the given name.
* You don't "own" the filename until you zsync_end, but you can use this to give zsync a more
Expand Down
31 changes: 27 additions & 4 deletions c/make.c
Expand Up @@ -26,7 +26,7 @@
#include <errno.h>
#include <libgen.h>
#include <math.h>

#include <time.h>

#include <arpa/inet.h>
#ifdef HAVE_INTTYPES_H
Expand Down Expand Up @@ -531,6 +531,7 @@ int main(int argc, char **argv) {
int do_recompress = -1; // -1 means we decide for ourselves
int do_exact = 0;
const char *gzopts = NULL;
time_t mtime = -1;

/* Open temporary file */
FILE *tf = tmpfile();
Expand Down Expand Up @@ -600,6 +601,15 @@ int main(int argc, char **argv) {
perror("open");
exit(2);
}

{ /* Get mtime if available */
struct stat st;
if (fstat(fileno(instream), &st) == 0) {
mtime = st.st_mtime;
}
}

/* Use supplied filename as the target filename */
if (!fname)
fname = basename(argv[optind]);
}
Expand Down Expand Up @@ -766,14 +776,27 @@ int main(int argc, char **argv) {
/* Lines we might include but which older clients can ignore */
if (do_recompress) {
if (zfname)
fprintf(fout, "Safe: Z-Filename Recompress\nZ-Filename: %s\n",
fprintf(fout, "Safe: Z-Filename Recompress MTime\nZ-Filename: %s\n",
zfname);
else
fprintf(fout, "Safe: Recompress\n");
fprintf(fout, "Safe: Recompress MTime:\n");
}

if (fname)
if (fname) {
fprintf(fout, "Filename: %s\n", fname);
if (mtime != -1) {
char buf[32];
struct tm mtime_tm;

if (gmtime_r(&mtime, &mtime_tm) != NULL) {
if (strftime(buf, sizeof buf, "%a, %d %b %Y %H:%M:%S %z", &mtime_tm) > 0)
fprintf(fout, "MTime: %s\n", buf);
}
else {
fprintf(stderr, "error converting %d to struct tm\n", mtime);
}
}
}
fprintf(fout, "Blocksize: " SIZE_T_PF "\n", blocksize);
fprintf(fout, "Length: " OFF_T_PF "\n", len);
fprintf(fout, "Hash-Lengths: %d,%d,%d\n", seq_matches, rsum_len,
Expand Down

0 comments on commit 01de713

Please sign in to comment.