| #include "git-compat-util.h" |
| #include "copy.h" |
| #include "path.h" |
| #include "gettext.h" |
| #include "strbuf.h" |
| #include "abspath.h" |
| |
| int copy_fd(int ifd, int ofd) |
| { |
| while (1) { |
| char buffer[8192]; |
| ssize_t len = xread(ifd, buffer, sizeof(buffer)); |
| if (!len) |
| break; |
| if (len < 0) |
| return COPY_READ_ERROR; |
| if (write_in_full(ofd, buffer, len) < 0) |
| return COPY_WRITE_ERROR; |
| } |
| return 0; |
| } |
| |
| static int copy_times(const char *dst, const char *src) |
| { |
| struct stat st; |
| struct utimbuf times; |
| if (stat(src, &st) < 0) |
| return -1; |
| times.actime = st.st_atime; |
| times.modtime = st.st_mtime; |
| if (utime(dst, ×) < 0) |
| return -1; |
| return 0; |
| } |
| |
| int copy_file(const char *dst, const char *src, int mode) |
| { |
| int fdi, fdo, status; |
| |
| mode = (mode & 0111) ? 0777 : 0666; |
| if ((fdi = open(src, O_RDONLY)) < 0) |
| return fdi; |
| if ((fdo = open(dst, O_WRONLY | O_CREAT | O_EXCL, mode)) < 0) { |
| close(fdi); |
| return fdo; |
| } |
| status = copy_fd(fdi, fdo); |
| switch (status) { |
| case COPY_READ_ERROR: |
| error_errno("copy-fd: read returned"); |
| break; |
| case COPY_WRITE_ERROR: |
| error_errno("copy-fd: write returned"); |
| break; |
| } |
| close(fdi); |
| if (close(fdo) != 0) |
| return error_errno("%s: close error", dst); |
| |
| if (!status && adjust_shared_perm(dst)) |
| return -1; |
| |
| return status; |
| } |
| |
| int copy_file_with_time(const char *dst, const char *src, int mode) |
| { |
| int status = copy_file(dst, src, mode); |
| if (!status) |
| return copy_times(dst, src); |
| return status; |
| } |
| |
| static int do_symlinks_match(const char *path1, const char *path2) |
| { |
| struct strbuf buf1 = STRBUF_INIT, buf2 = STRBUF_INIT; |
| int ret = 0; |
| |
| if (!strbuf_readlink(&buf1, path1, 0) && |
| !strbuf_readlink(&buf2, path2, 0)) |
| ret = !strcmp(buf1.buf, buf2.buf); |
| |
| strbuf_release(&buf1); |
| strbuf_release(&buf2); |
| return ret; |
| } |
| |
| int do_files_match(const char *path1, const char *path2) |
| { |
| struct stat st1, st2; |
| int fd1 = -1, fd2 = -1, ret = 1; |
| char buf1[8192], buf2[8192]; |
| |
| if ((fd1 = open_nofollow(path1, O_RDONLY)) < 0 || |
| fstat(fd1, &st1) || !S_ISREG(st1.st_mode)) { |
| if (fd1 < 0 && errno == ELOOP) |
| /* maybe this is a symbolic link? */ |
| return do_symlinks_match(path1, path2); |
| ret = 0; |
| } else if ((fd2 = open_nofollow(path2, O_RDONLY)) < 0 || |
| fstat(fd2, &st2) || !S_ISREG(st2.st_mode)) { |
| ret = 0; |
| } |
| |
| if (ret) |
| /* to match, neither must be executable, or both */ |
| ret = !(st1.st_mode & 0111) == !(st2.st_mode & 0111); |
| |
| if (ret) |
| ret = st1.st_size == st2.st_size; |
| |
| while (ret) { |
| ssize_t len1 = read_in_full(fd1, buf1, sizeof(buf1)); |
| ssize_t len2 = read_in_full(fd2, buf2, sizeof(buf2)); |
| |
| if (len1 < 0 || len2 < 0 || len1 != len2) |
| ret = 0; /* read error or different file size */ |
| else if (!len1) /* len2 is also 0; hit EOF on both */ |
| break; /* ret is still true */ |
| else |
| ret = !memcmp(buf1, buf2, len1); |
| } |
| |
| if (fd1 >= 0) |
| close(fd1); |
| if (fd2 >= 0) |
| close(fd2); |
| |
| return ret; |
| } |