39{
40 if (!rename(_oldpath, _newpath))
41 return 0;
42
43 if (errno != EXDEV)
44 return -1;
45
46
47
48 int oldfd = -1, newfd = -1;
49 struct stat oldstat, newstat;
50
51 if (lstat(_oldpath, &oldstat) || lstat(_newpath, &newstat))
52 return -1;
53#if 0
54 if (S_ISLNK(oldstat.st_mode)) {
55 char oldpath[1024 + 1];
56 int n = readlink(_oldpath, oldpath, sizeof(oldpath) - 1);
57 if (n > 0) {
58
59
60 oldpath[n] = '\0';
61 if (symlink(oldpath, _newpath))
62 return -1;
63 unlink(_oldpath);
64 }
65 return -1;
66 }
67#endif
68 if (!S_ISREG(oldstat.st_mode)) {
69 errno = EINVAL;
70 return -1;
71 }
72
73 do {
74 oldfd = open(_oldpath, O_RDONLY);
75 newfd = open(_newpath, O_CREAT | O_WRONLY, oldstat.st_mode);
76 if (oldfd == -1 || newfd == -1)
77 break;
78#if __linux__
79 off_t offset = 0;
80 ssize_t n = sendfile(newfd, oldfd, &offset, oldstat.st_size);
81 if (n == -1)
82 break;
83#else
84 char* buf = (char*)malloc(newstat.st_blksize);
85 if (!buf) {
86 errno = ENOMEM;
87 break;
88 }
89 ssize_t n = 0;
90 while ((n != -1) && (n = read(oldfd, buf, newstat.st_blksize)) > 0)
91 n = write(newfd, buf, n);
92 free(buf);
93 if (n == -1)
94 break;
95#endif
96 close(oldfd);
97 close(newfd);
98
99 struct utimbuf ub;
100 ub.actime = oldstat.st_atime;
101 ub.modtime = oldstat.st_mtime;
102 utime(_newpath, &ub);
103 unlink(_oldpath);
104 return 0;
105 } while (true);
106
107 int save = errno;
108 if (oldfd != -1)
109 close(oldfd);
110 if (newfd != -1) {
111 close(newfd);
112 unlink(_newpath);
113 }
114
115 errno = save;
116 return -1;
117}