[14149] | 1 | /* Copyright 1999 by the Massachusetts Institute of Technology. |
---|
| 2 | * |
---|
| 3 | * Permission to use, copy, modify, and distribute this |
---|
| 4 | * software and its documentation for any purpose and without |
---|
| 5 | * fee is hereby granted, provided that the above copyright |
---|
| 6 | * notice appear in all copies and that both that copyright |
---|
| 7 | * notice and this permission notice appear in supporting |
---|
| 8 | * documentation, and that the name of M.I.T. not be used in |
---|
| 9 | * advertising or publicity pertaining to distribution of the |
---|
| 10 | * software without specific, written prior permission. |
---|
| 11 | * M.I.T. makes no representations about the suitability of |
---|
| 12 | * this software for any purpose. It is provided "as is" |
---|
| 13 | * without express or implied warranty. |
---|
| 14 | */ |
---|
| 15 | |
---|
[16698] | 16 | static const char rcsid[] = "$Id: syncupdate.c,v 1.4 2001-10-23 21:59:02 amb Exp $"; |
---|
[14149] | 17 | |
---|
| 18 | #include <sys/types.h> |
---|
| 19 | #include <sys/stat.h> |
---|
| 20 | #include <stdio.h> |
---|
| 21 | #include <string.h> |
---|
| 22 | #include <stdlib.h> |
---|
| 23 | #include <unistd.h> |
---|
| 24 | #include <fcntl.h> |
---|
| 25 | #include <errno.h> |
---|
| 26 | |
---|
[16698] | 27 | #ifdef linux |
---|
| 28 | #include <libgen.h> |
---|
| 29 | #endif |
---|
| 30 | |
---|
[14307] | 31 | #ifndef S_IAMB |
---|
| 32 | #define S_IAMB 0x1FF /* Access bits part of struct stat st_mode field */ |
---|
| 33 | #endif |
---|
| 34 | |
---|
[14149] | 35 | static void copy(const char *source, const char *tempfile); |
---|
| 36 | static void open_and_sync(const char *filename); |
---|
| 37 | static void usage(void); |
---|
[16698] | 38 | #ifdef linux |
---|
| 39 | static void open_dir_and_sync(const char *filename); |
---|
| 40 | #endif |
---|
[14149] | 41 | |
---|
| 42 | int main(int argc, char **argv) |
---|
| 43 | { |
---|
| 44 | int c; |
---|
[16698] | 45 | char *tempfile = NULL, *source, *dest; |
---|
[14149] | 46 | |
---|
| 47 | while ((c = getopt(argc, argv, "c:")) != EOF) |
---|
| 48 | { |
---|
| 49 | switch (c) |
---|
| 50 | { |
---|
| 51 | case 'c': |
---|
| 52 | tempfile = optarg; |
---|
| 53 | break; |
---|
| 54 | case '?': |
---|
| 55 | usage(); |
---|
| 56 | } |
---|
| 57 | } |
---|
| 58 | argc -= optind; |
---|
| 59 | argv += optind; |
---|
| 60 | if (argc != 2) |
---|
| 61 | usage(); |
---|
| 62 | source = argv[0]; |
---|
| 63 | dest = argv[1]; |
---|
| 64 | |
---|
| 65 | /* Set up for the move. */ |
---|
| 66 | if (tempfile) |
---|
| 67 | { |
---|
| 68 | copy(source, tempfile); |
---|
| 69 | source = tempfile; |
---|
| 70 | } |
---|
| 71 | else |
---|
| 72 | open_and_sync(source); |
---|
| 73 | |
---|
| 74 | if (rename(source, dest) == -1) |
---|
| 75 | { |
---|
| 76 | fprintf(stderr, "syncupdate: can't rename %s to %s: %s\n", |
---|
| 77 | source, dest, strerror(errno)); |
---|
| 78 | exit(1); |
---|
| 79 | } |
---|
| 80 | |
---|
[16698] | 81 | #ifdef linux |
---|
| 82 | open_dir_and_sync(dirname(dest)); |
---|
| 83 | #endif |
---|
| 84 | |
---|
[14149] | 85 | exit(0); |
---|
| 86 | } |
---|
| 87 | |
---|
| 88 | static void copy(const char *source, const char *tempfile) |
---|
| 89 | { |
---|
| 90 | int sfd, tfd, count, pos, n; |
---|
| 91 | struct stat statbuf; |
---|
| 92 | unsigned char buf[8192]; |
---|
| 93 | |
---|
| 94 | /* Open the source file and get its status information. */ |
---|
| 95 | sfd = open(source, O_RDONLY, 0); |
---|
| 96 | if (sfd == -1) |
---|
| 97 | { |
---|
| 98 | fprintf(stderr, "syncupdate: can't read %s: %s\n", source, |
---|
| 99 | strerror(errno)); |
---|
| 100 | exit(1); |
---|
| 101 | } |
---|
| 102 | if (fstat(sfd, &statbuf) == -1) |
---|
| 103 | { |
---|
| 104 | fprintf(stderr, "syncupdate: can't stat %s: %s\n", source, |
---|
| 105 | strerror(errno)); |
---|
| 106 | exit(1); |
---|
| 107 | } |
---|
| 108 | |
---|
| 109 | /* Remove, open, and set mode on the temp file. */ |
---|
| 110 | if (unlink(tempfile) == -1 && errno != ENOENT) |
---|
| 111 | { |
---|
| 112 | fprintf(stderr, "syncupdate: can't remove %s: %s\n", tempfile, |
---|
| 113 | strerror(errno)); |
---|
| 114 | exit(1); |
---|
| 115 | } |
---|
| 116 | tfd = open(tempfile, O_RDWR | O_CREAT | O_EXCL, 0); |
---|
| 117 | if (tfd == -1) |
---|
| 118 | { |
---|
| 119 | fprintf(stderr, "syncupdate: can't create %s: %s\n", tempfile, |
---|
| 120 | strerror(errno)); |
---|
| 121 | exit(1); |
---|
| 122 | } |
---|
| 123 | if (fchmod(tfd, statbuf.st_mode & S_IAMB) == -1) |
---|
| 124 | { |
---|
| 125 | fprintf(stderr, "syncupdate: can't fchmod %s: %s\n", tempfile, |
---|
| 126 | strerror(errno)); |
---|
| 127 | unlink(tempfile); |
---|
| 128 | exit(1); |
---|
| 129 | } |
---|
| 130 | |
---|
| 131 | /* Now copy and fsync. */ |
---|
| 132 | while ((count = read(sfd, buf, sizeof(buf))) > 0) |
---|
| 133 | { |
---|
| 134 | for (pos = 0; pos < count; pos += n) |
---|
| 135 | { |
---|
| 136 | n = write(tfd, buf + pos, count - pos); |
---|
| 137 | if (n == -1) |
---|
| 138 | { |
---|
| 139 | fprintf(stderr, "syncupdate: can't write to %s: %s\n", tempfile, |
---|
| 140 | strerror(errno)); |
---|
| 141 | unlink(tempfile); |
---|
| 142 | exit(1); |
---|
| 143 | } |
---|
| 144 | } |
---|
| 145 | } |
---|
| 146 | if (count == -1) |
---|
| 147 | { |
---|
| 148 | fprintf(stderr, "syncupdate: can't read from %s: %s\n", source, |
---|
| 149 | strerror(errno)); |
---|
| 150 | } |
---|
| 151 | |
---|
| 152 | if (fsync(tfd) == -1) |
---|
| 153 | { |
---|
| 154 | fprintf(stderr, "syncupdate: can't fsync %s: %s\n", tempfile, |
---|
| 155 | strerror(errno)); |
---|
| 156 | unlink(tempfile); |
---|
| 157 | exit(1); |
---|
| 158 | } |
---|
| 159 | |
---|
| 160 | close(sfd); |
---|
| 161 | close(tfd); |
---|
| 162 | } |
---|
| 163 | |
---|
| 164 | static void open_and_sync(const char *filename) |
---|
| 165 | { |
---|
| 166 | int fd; |
---|
| 167 | |
---|
| 168 | fd = open(filename, O_RDWR, 0); |
---|
| 169 | if (fd == -1) |
---|
| 170 | { |
---|
| 171 | fprintf(stderr, "syncupdate: can't open %s: %s\n", filename, |
---|
| 172 | strerror(errno)); |
---|
| 173 | exit(1); |
---|
| 174 | } |
---|
| 175 | |
---|
| 176 | if (fsync(fd) == -1) |
---|
| 177 | { |
---|
| 178 | fprintf(stderr, "syncupdate: can't fsync %s: %s\n", filename, |
---|
| 179 | strerror(errno)); |
---|
| 180 | exit(1); |
---|
| 181 | } |
---|
| 182 | |
---|
| 183 | close(fd); |
---|
| 184 | } |
---|
| 185 | |
---|
[16698] | 186 | #ifdef linux |
---|
| 187 | /* Despite the implication of the man page that fsync() will only work on |
---|
| 188 | RDWR file descriptors, it also works on directories open RDONLY. |
---|
| 189 | Since linux fsync() doesn't flush directory entries, force the issue. */ |
---|
| 190 | static void open_dir_and_sync(const char *filename) |
---|
| 191 | { |
---|
| 192 | int fd; |
---|
| 193 | |
---|
| 194 | fd = open(filename, O_RDONLY, 0); |
---|
| 195 | if (fd == -1) |
---|
| 196 | { |
---|
| 197 | fprintf(stderr, "syncupdate: can't open %s: %s\n", filename, |
---|
| 198 | strerror(errno)); |
---|
| 199 | exit(1); |
---|
| 200 | } |
---|
| 201 | |
---|
| 202 | fsync(fd); |
---|
| 203 | close(fd); |
---|
| 204 | } |
---|
| 205 | #endif |
---|
| 206 | |
---|
[14149] | 207 | static void usage(void) |
---|
| 208 | { |
---|
| 209 | fprintf(stderr, "Usage: syncupdate [-c tempfile] source dest\n"); |
---|
[16689] | 210 | exit(1); |
---|
[14149] | 211 | } |
---|