line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
/* |
2
|
|
|
|
|
|
|
* Copyright (C) the libgit2 contributors. All rights reserved. |
3
|
|
|
|
|
|
|
* |
4
|
|
|
|
|
|
|
* This file is part of libgit2, distributed under the GNU GPL v2 with |
5
|
|
|
|
|
|
|
* a Linking Exception. For full terms see the included COPYING file. |
6
|
|
|
|
|
|
|
*/ |
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
#include "futils.h" |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
#include "global.h" |
11
|
|
|
|
|
|
|
#include "strmap.h" |
12
|
|
|
|
|
|
|
#include |
13
|
|
|
|
|
|
|
#if GIT_WIN32 |
14
|
|
|
|
|
|
|
#include "win32/findfile.h" |
15
|
|
|
|
|
|
|
#endif |
16
|
|
|
|
|
|
|
|
17
|
262
|
|
|
|
|
|
int git_futils_mkpath2file(const char *file_path, const mode_t mode) |
18
|
|
|
|
|
|
|
{ |
19
|
262
|
|
|
|
|
|
return git_futils_mkdir( |
20
|
|
|
|
|
|
|
file_path, mode, |
21
|
|
|
|
|
|
|
GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST | GIT_MKDIR_VERIFY_DIR); |
22
|
|
|
|
|
|
|
} |
23
|
|
|
|
|
|
|
|
24
|
187
|
|
|
|
|
|
int git_futils_mktmp(git_buf *path_out, const char *filename, mode_t mode) |
25
|
|
|
|
|
|
|
{ |
26
|
|
|
|
|
|
|
int fd; |
27
|
|
|
|
|
|
|
mode_t mask; |
28
|
|
|
|
|
|
|
|
29
|
187
|
|
|
|
|
|
p_umask(mask = p_umask(0)); |
30
|
|
|
|
|
|
|
|
31
|
187
|
|
|
|
|
|
git_buf_sets(path_out, filename); |
32
|
187
|
|
|
|
|
|
git_buf_puts(path_out, "_git2_XXXXXX"); |
33
|
|
|
|
|
|
|
|
34
|
187
|
50
|
|
|
|
|
if (git_buf_oom(path_out)) |
35
|
0
|
|
|
|
|
|
return -1; |
36
|
|
|
|
|
|
|
|
37
|
187
|
50
|
|
|
|
|
if ((fd = p_mkstemp(path_out->ptr)) < 0) { |
38
|
0
|
|
|
|
|
|
git_error_set(GIT_ERROR_OS, |
39
|
|
|
|
|
|
|
"failed to create temporary file '%s'", path_out->ptr); |
40
|
0
|
|
|
|
|
|
return -1; |
41
|
|
|
|
|
|
|
} |
42
|
|
|
|
|
|
|
|
43
|
187
|
50
|
|
|
|
|
if (p_chmod(path_out->ptr, (mode & ~mask))) { |
44
|
0
|
|
|
|
|
|
git_error_set(GIT_ERROR_OS, |
45
|
|
|
|
|
|
|
"failed to set permissions on file '%s'", path_out->ptr); |
46
|
0
|
|
|
|
|
|
return -1; |
47
|
|
|
|
|
|
|
} |
48
|
|
|
|
|
|
|
|
49
|
187
|
|
|
|
|
|
return fd; |
50
|
|
|
|
|
|
|
} |
51
|
|
|
|
|
|
|
|
52
|
0
|
|
|
|
|
|
int git_futils_creat_withpath(const char *path, const mode_t dirmode, const mode_t mode) |
53
|
|
|
|
|
|
|
{ |
54
|
|
|
|
|
|
|
int fd; |
55
|
|
|
|
|
|
|
|
56
|
0
|
0
|
|
|
|
|
if (git_futils_mkpath2file(path, dirmode) < 0) |
57
|
0
|
|
|
|
|
|
return -1; |
58
|
|
|
|
|
|
|
|
59
|
0
|
|
|
|
|
|
fd = p_creat(path, mode); |
60
|
0
|
0
|
|
|
|
|
if (fd < 0) { |
61
|
0
|
|
|
|
|
|
git_error_set(GIT_ERROR_OS, "failed to create file '%s'", path); |
62
|
0
|
|
|
|
|
|
return -1; |
63
|
|
|
|
|
|
|
} |
64
|
|
|
|
|
|
|
|
65
|
0
|
|
|
|
|
|
return fd; |
66
|
|
|
|
|
|
|
} |
67
|
|
|
|
|
|
|
|
68
|
316
|
|
|
|
|
|
int git_futils_creat_locked(const char *path, const mode_t mode) |
69
|
|
|
|
|
|
|
{ |
70
|
316
|
|
|
|
|
|
int fd = p_open(path, O_WRONLY | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC, |
71
|
|
|
|
|
|
|
mode); |
72
|
|
|
|
|
|
|
|
73
|
316
|
50
|
|
|
|
|
if (fd < 0) { |
74
|
0
|
|
|
|
|
|
int error = errno; |
75
|
0
|
|
|
|
|
|
git_error_set(GIT_ERROR_OS, "failed to create locked file '%s'", path); |
76
|
0
|
|
|
|
|
|
switch (error) { |
77
|
|
|
|
|
|
|
case EEXIST: |
78
|
0
|
|
|
|
|
|
return GIT_ELOCKED; |
79
|
|
|
|
|
|
|
case ENOENT: |
80
|
0
|
|
|
|
|
|
return GIT_ENOTFOUND; |
81
|
|
|
|
|
|
|
default: |
82
|
0
|
|
|
|
|
|
return -1; |
83
|
|
|
|
|
|
|
} |
84
|
|
|
|
|
|
|
} |
85
|
|
|
|
|
|
|
|
86
|
316
|
|
|
|
|
|
return fd; |
87
|
|
|
|
|
|
|
} |
88
|
|
|
|
|
|
|
|
89
|
144
|
|
|
|
|
|
int git_futils_creat_locked_withpath(const char *path, const mode_t dirmode, const mode_t mode) |
90
|
|
|
|
|
|
|
{ |
91
|
144
|
50
|
|
|
|
|
if (git_futils_mkpath2file(path, dirmode) < 0) |
92
|
0
|
|
|
|
|
|
return -1; |
93
|
|
|
|
|
|
|
|
94
|
144
|
|
|
|
|
|
return git_futils_creat_locked(path, mode); |
95
|
|
|
|
|
|
|
} |
96
|
|
|
|
|
|
|
|
97
|
2873
|
|
|
|
|
|
int git_futils_open_ro(const char *path) |
98
|
|
|
|
|
|
|
{ |
99
|
2873
|
|
|
|
|
|
int fd = p_open(path, O_RDONLY); |
100
|
2873
|
50
|
|
|
|
|
if (fd < 0) |
101
|
0
|
|
|
|
|
|
return git_path_set_error(errno, path, "open"); |
102
|
2873
|
|
|
|
|
|
return fd; |
103
|
|
|
|
|
|
|
} |
104
|
|
|
|
|
|
|
|
105
|
0
|
|
|
|
|
|
int git_futils_truncate(const char *path, int mode) |
106
|
|
|
|
|
|
|
{ |
107
|
0
|
|
|
|
|
|
int fd = p_open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, mode); |
108
|
0
|
0
|
|
|
|
|
if (fd < 0) |
109
|
0
|
|
|
|
|
|
return git_path_set_error(errno, path, "open"); |
110
|
|
|
|
|
|
|
|
111
|
0
|
|
|
|
|
|
close(fd); |
112
|
0
|
|
|
|
|
|
return 0; |
113
|
|
|
|
|
|
|
} |
114
|
|
|
|
|
|
|
|
115
|
0
|
|
|
|
|
|
int git_futils_filesize(uint64_t *out, git_file fd) |
116
|
|
|
|
|
|
|
{ |
117
|
|
|
|
|
|
|
struct stat sb; |
118
|
|
|
|
|
|
|
|
119
|
0
|
0
|
|
|
|
|
if (p_fstat(fd, &sb)) { |
120
|
0
|
|
|
|
|
|
git_error_set(GIT_ERROR_OS, "failed to stat file descriptor"); |
121
|
0
|
|
|
|
|
|
return -1; |
122
|
|
|
|
|
|
|
} |
123
|
|
|
|
|
|
|
|
124
|
0
|
0
|
|
|
|
|
if (sb.st_size < 0) { |
125
|
0
|
|
|
|
|
|
git_error_set(GIT_ERROR_INVALID, "invalid file size"); |
126
|
0
|
|
|
|
|
|
return -1; |
127
|
|
|
|
|
|
|
} |
128
|
|
|
|
|
|
|
|
129
|
0
|
|
|
|
|
|
*out = sb.st_size; |
130
|
0
|
|
|
|
|
|
return 0; |
131
|
|
|
|
|
|
|
} |
132
|
|
|
|
|
|
|
|
133
|
895
|
|
|
|
|
|
mode_t git_futils_canonical_mode(mode_t raw_mode) |
134
|
|
|
|
|
|
|
{ |
135
|
895
|
100
|
|
|
|
|
if (S_ISREG(raw_mode)) |
136
|
689
|
50
|
|
|
|
|
return S_IFREG | GIT_PERMS_CANONICAL(raw_mode); |
137
|
206
|
50
|
|
|
|
|
else if (S_ISLNK(raw_mode)) |
138
|
0
|
|
|
|
|
|
return S_IFLNK; |
139
|
206
|
50
|
|
|
|
|
else if (S_ISGITLINK(raw_mode)) |
140
|
0
|
|
|
|
|
|
return S_IFGITLINK; |
141
|
206
|
50
|
|
|
|
|
else if (S_ISDIR(raw_mode)) |
142
|
206
|
|
|
|
|
|
return S_IFDIR; |
143
|
|
|
|
|
|
|
else |
144
|
0
|
|
|
|
|
|
return 0; |
145
|
|
|
|
|
|
|
} |
146
|
|
|
|
|
|
|
|
147
|
2312
|
|
|
|
|
|
int git_futils_readbuffer_fd(git_buf *buf, git_file fd, size_t len) |
148
|
|
|
|
|
|
|
{ |
149
|
2312
|
|
|
|
|
|
ssize_t read_size = 0; |
150
|
|
|
|
|
|
|
size_t alloc_len; |
151
|
|
|
|
|
|
|
|
152
|
2312
|
|
|
|
|
|
git_buf_clear(buf); |
153
|
|
|
|
|
|
|
|
154
|
2312
|
50
|
|
|
|
|
if (!git__is_ssizet(len)) { |
155
|
0
|
|
|
|
|
|
git_error_set(GIT_ERROR_INVALID, "read too large"); |
156
|
0
|
|
|
|
|
|
return -1; |
157
|
|
|
|
|
|
|
} |
158
|
|
|
|
|
|
|
|
159
|
2312
|
50
|
|
|
|
|
GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, len, 1); |
|
|
50
|
|
|
|
|
|
160
|
2312
|
50
|
|
|
|
|
if (git_buf_grow(buf, alloc_len) < 0) |
161
|
0
|
|
|
|
|
|
return -1; |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
/* p_read loops internally to read len bytes */ |
164
|
2312
|
|
|
|
|
|
read_size = p_read(fd, buf->ptr, len); |
165
|
|
|
|
|
|
|
|
166
|
2312
|
50
|
|
|
|
|
if (read_size != (ssize_t)len) { |
167
|
0
|
|
|
|
|
|
git_error_set(GIT_ERROR_OS, "failed to read descriptor"); |
168
|
0
|
|
|
|
|
|
git_buf_dispose(buf); |
169
|
0
|
|
|
|
|
|
return -1; |
170
|
|
|
|
|
|
|
} |
171
|
|
|
|
|
|
|
|
172
|
2312
|
|
|
|
|
|
buf->ptr[read_size] = '\0'; |
173
|
2312
|
|
|
|
|
|
buf->size = read_size; |
174
|
|
|
|
|
|
|
|
175
|
2312
|
|
|
|
|
|
return 0; |
176
|
|
|
|
|
|
|
} |
177
|
|
|
|
|
|
|
|
178
|
3345
|
|
|
|
|
|
int git_futils_readbuffer_updated( |
179
|
|
|
|
|
|
|
git_buf *out, const char *path, git_oid *checksum, int *updated) |
180
|
|
|
|
|
|
|
{ |
181
|
|
|
|
|
|
|
int error; |
182
|
|
|
|
|
|
|
git_file fd; |
183
|
|
|
|
|
|
|
struct stat st; |
184
|
3345
|
|
|
|
|
|
git_buf buf = GIT_BUF_INIT; |
185
|
|
|
|
|
|
|
git_oid checksum_new; |
186
|
|
|
|
|
|
|
|
187
|
3345
|
50
|
|
|
|
|
assert(out && path && *path); |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
188
|
|
|
|
|
|
|
|
189
|
3345
|
50
|
|
|
|
|
if (updated != NULL) |
190
|
0
|
|
|
|
|
|
*updated = 0; |
191
|
|
|
|
|
|
|
|
192
|
3345
|
100
|
|
|
|
|
if (p_stat(path, &st) < 0) |
193
|
1075
|
|
|
|
|
|
return git_path_set_error(errno, path, "stat"); |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
|
196
|
2270
|
50
|
|
|
|
|
if (S_ISDIR(st.st_mode)) { |
197
|
0
|
|
|
|
|
|
git_error_set(GIT_ERROR_INVALID, "requested file is a directory"); |
198
|
0
|
|
|
|
|
|
return GIT_ENOTFOUND; |
199
|
|
|
|
|
|
|
} |
200
|
|
|
|
|
|
|
|
201
|
2270
|
50
|
|
|
|
|
if (!git__is_sizet(st.st_size+1)) { |
202
|
0
|
|
|
|
|
|
git_error_set(GIT_ERROR_OS, "invalid regular file stat for '%s'", path); |
203
|
0
|
|
|
|
|
|
return -1; |
204
|
|
|
|
|
|
|
} |
205
|
|
|
|
|
|
|
|
206
|
2270
|
50
|
|
|
|
|
if ((fd = git_futils_open_ro(path)) < 0) |
207
|
0
|
|
|
|
|
|
return fd; |
208
|
|
|
|
|
|
|
|
209
|
2270
|
50
|
|
|
|
|
if (git_futils_readbuffer_fd(&buf, fd, (size_t)st.st_size) < 0) { |
210
|
0
|
|
|
|
|
|
p_close(fd); |
211
|
0
|
|
|
|
|
|
return -1; |
212
|
|
|
|
|
|
|
} |
213
|
|
|
|
|
|
|
|
214
|
2270
|
|
|
|
|
|
p_close(fd); |
215
|
|
|
|
|
|
|
|
216
|
2270
|
50
|
|
|
|
|
if (checksum) { |
217
|
0
|
0
|
|
|
|
|
if ((error = git_hash_buf(&checksum_new, buf.ptr, buf.size)) < 0) { |
218
|
0
|
|
|
|
|
|
git_buf_dispose(&buf); |
219
|
0
|
|
|
|
|
|
return error; |
220
|
|
|
|
|
|
|
} |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
/* |
223
|
|
|
|
|
|
|
* If we were given a checksum, we only want to use it if it's different |
224
|
|
|
|
|
|
|
*/ |
225
|
0
|
0
|
|
|
|
|
if (!git_oid__cmp(checksum, &checksum_new)) { |
226
|
0
|
|
|
|
|
|
git_buf_dispose(&buf); |
227
|
0
|
0
|
|
|
|
|
if (updated) |
228
|
0
|
|
|
|
|
|
*updated = 0; |
229
|
|
|
|
|
|
|
|
230
|
0
|
|
|
|
|
|
return 0; |
231
|
|
|
|
|
|
|
} |
232
|
|
|
|
|
|
|
|
233
|
0
|
|
|
|
|
|
git_oid_cpy(checksum, &checksum_new); |
234
|
|
|
|
|
|
|
} |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
/* |
237
|
|
|
|
|
|
|
* If we're here, the file did change, or the user didn't have an old version |
238
|
|
|
|
|
|
|
*/ |
239
|
2270
|
50
|
|
|
|
|
if (updated != NULL) |
240
|
0
|
|
|
|
|
|
*updated = 1; |
241
|
|
|
|
|
|
|
|
242
|
2270
|
|
|
|
|
|
git_buf_swap(out, &buf); |
243
|
2270
|
|
|
|
|
|
git_buf_dispose(&buf); |
244
|
|
|
|
|
|
|
|
245
|
3345
|
|
|
|
|
|
return 0; |
246
|
|
|
|
|
|
|
} |
247
|
|
|
|
|
|
|
|
248
|
3345
|
|
|
|
|
|
int git_futils_readbuffer(git_buf *buf, const char *path) |
249
|
|
|
|
|
|
|
{ |
250
|
3345
|
|
|
|
|
|
return git_futils_readbuffer_updated(buf, path, NULL, NULL); |
251
|
|
|
|
|
|
|
} |
252
|
|
|
|
|
|
|
|
253
|
143
|
|
|
|
|
|
int git_futils_writebuffer( |
254
|
|
|
|
|
|
|
const git_buf *buf, const char *path, int flags, mode_t mode) |
255
|
|
|
|
|
|
|
{ |
256
|
143
|
|
|
|
|
|
int fd, do_fsync = 0, error = 0; |
257
|
|
|
|
|
|
|
|
258
|
143
|
100
|
|
|
|
|
if (!flags) |
259
|
21
|
|
|
|
|
|
flags = O_CREAT | O_TRUNC | O_WRONLY; |
260
|
|
|
|
|
|
|
|
261
|
143
|
50
|
|
|
|
|
if ((flags & O_FSYNC) != 0) |
262
|
0
|
|
|
|
|
|
do_fsync = 1; |
263
|
|
|
|
|
|
|
|
264
|
143
|
|
|
|
|
|
flags &= ~O_FSYNC; |
265
|
|
|
|
|
|
|
|
266
|
143
|
50
|
|
|
|
|
if (!mode) |
267
|
0
|
|
|
|
|
|
mode = GIT_FILEMODE_BLOB; |
268
|
|
|
|
|
|
|
|
269
|
143
|
50
|
|
|
|
|
if ((fd = p_open(path, flags, mode)) < 0) { |
270
|
0
|
|
|
|
|
|
git_error_set(GIT_ERROR_OS, "could not open '%s' for writing", path); |
271
|
0
|
|
|
|
|
|
return fd; |
272
|
|
|
|
|
|
|
} |
273
|
|
|
|
|
|
|
|
274
|
143
|
50
|
|
|
|
|
if ((error = p_write(fd, git_buf_cstr(buf), git_buf_len(buf))) < 0) { |
275
|
0
|
|
|
|
|
|
git_error_set(GIT_ERROR_OS, "could not write to '%s'", path); |
276
|
0
|
|
|
|
|
|
(void)p_close(fd); |
277
|
0
|
|
|
|
|
|
return error; |
278
|
|
|
|
|
|
|
} |
279
|
|
|
|
|
|
|
|
280
|
143
|
50
|
|
|
|
|
if (do_fsync && (error = p_fsync(fd)) < 0) { |
|
|
0
|
|
|
|
|
|
281
|
0
|
|
|
|
|
|
git_error_set(GIT_ERROR_OS, "could not fsync '%s'", path); |
282
|
0
|
|
|
|
|
|
p_close(fd); |
283
|
0
|
|
|
|
|
|
return error; |
284
|
|
|
|
|
|
|
} |
285
|
|
|
|
|
|
|
|
286
|
143
|
50
|
|
|
|
|
if ((error = p_close(fd)) < 0) { |
287
|
0
|
|
|
|
|
|
git_error_set(GIT_ERROR_OS, "error while closing '%s'", path); |
288
|
0
|
|
|
|
|
|
return error; |
289
|
|
|
|
|
|
|
} |
290
|
|
|
|
|
|
|
|
291
|
143
|
50
|
|
|
|
|
if (do_fsync && (flags & O_CREAT)) |
|
|
0
|
|
|
|
|
|
292
|
0
|
|
|
|
|
|
error = git_futils_fsync_parent(path); |
293
|
|
|
|
|
|
|
|
294
|
143
|
|
|
|
|
|
return error; |
295
|
|
|
|
|
|
|
} |
296
|
|
|
|
|
|
|
|
297
|
0
|
|
|
|
|
|
int git_futils_mv_withpath(const char *from, const char *to, const mode_t dirmode) |
298
|
|
|
|
|
|
|
{ |
299
|
0
|
0
|
|
|
|
|
if (git_futils_mkpath2file(to, dirmode) < 0) |
300
|
0
|
|
|
|
|
|
return -1; |
301
|
|
|
|
|
|
|
|
302
|
0
|
0
|
|
|
|
|
if (p_rename(from, to) < 0) { |
303
|
0
|
|
|
|
|
|
git_error_set(GIT_ERROR_OS, "failed to rename '%s' to '%s'", from, to); |
304
|
0
|
|
|
|
|
|
return -1; |
305
|
|
|
|
|
|
|
} |
306
|
|
|
|
|
|
|
|
307
|
0
|
|
|
|
|
|
return 0; |
308
|
|
|
|
|
|
|
} |
309
|
|
|
|
|
|
|
|
310
|
55
|
|
|
|
|
|
int git_futils_mmap_ro(git_map *out, git_file fd, off64_t begin, size_t len) |
311
|
|
|
|
|
|
|
{ |
312
|
55
|
|
|
|
|
|
return p_mmap(out, len, GIT_PROT_READ, GIT_MAP_SHARED, fd, begin); |
313
|
|
|
|
|
|
|
} |
314
|
|
|
|
|
|
|
|
315
|
0
|
|
|
|
|
|
int git_futils_mmap_ro_file(git_map *out, const char *path) |
316
|
|
|
|
|
|
|
{ |
317
|
0
|
|
|
|
|
|
git_file fd = git_futils_open_ro(path); |
318
|
|
|
|
|
|
|
uint64_t len; |
319
|
|
|
|
|
|
|
int result; |
320
|
|
|
|
|
|
|
|
321
|
0
|
0
|
|
|
|
|
if (fd < 0) |
322
|
0
|
|
|
|
|
|
return fd; |
323
|
|
|
|
|
|
|
|
324
|
0
|
0
|
|
|
|
|
if ((result = git_futils_filesize(&len, fd)) < 0) |
325
|
0
|
|
|
|
|
|
goto out; |
326
|
|
|
|
|
|
|
|
327
|
0
|
0
|
|
|
|
|
if (!git__is_sizet(len)) { |
328
|
0
|
|
|
|
|
|
git_error_set(GIT_ERROR_OS, "file `%s` too large to mmap", path); |
329
|
0
|
|
|
|
|
|
result = -1; |
330
|
0
|
|
|
|
|
|
goto out; |
331
|
|
|
|
|
|
|
} |
332
|
|
|
|
|
|
|
|
333
|
0
|
|
|
|
|
|
result = git_futils_mmap_ro(out, fd, 0, (size_t)len); |
334
|
|
|
|
|
|
|
out: |
335
|
0
|
|
|
|
|
|
p_close(fd); |
336
|
0
|
|
|
|
|
|
return result; |
337
|
|
|
|
|
|
|
} |
338
|
|
|
|
|
|
|
|
339
|
55
|
|
|
|
|
|
void git_futils_mmap_free(git_map *out) |
340
|
|
|
|
|
|
|
{ |
341
|
55
|
|
|
|
|
|
p_munmap(out); |
342
|
55
|
|
|
|
|
|
} |
343
|
|
|
|
|
|
|
|
344
|
301
|
|
|
|
|
|
GIT_INLINE(int) mkdir_validate_dir( |
345
|
|
|
|
|
|
|
const char *path, |
346
|
|
|
|
|
|
|
struct stat *st, |
347
|
|
|
|
|
|
|
mode_t mode, |
348
|
|
|
|
|
|
|
uint32_t flags, |
349
|
|
|
|
|
|
|
struct git_futils_mkdir_options *opts) |
350
|
|
|
|
|
|
|
{ |
351
|
|
|
|
|
|
|
/* with exclusive create, existing dir is an error */ |
352
|
301
|
50
|
|
|
|
|
if ((flags & GIT_MKDIR_EXCL) != 0) { |
353
|
0
|
|
|
|
|
|
git_error_set(GIT_ERROR_FILESYSTEM, |
354
|
|
|
|
|
|
|
"failed to make directory '%s': directory exists", path); |
355
|
0
|
|
|
|
|
|
return GIT_EEXISTS; |
356
|
|
|
|
|
|
|
} |
357
|
|
|
|
|
|
|
|
358
|
301
|
50
|
|
|
|
|
if ((S_ISREG(st->st_mode) && (flags & GIT_MKDIR_REMOVE_FILES)) || |
|
|
0
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
359
|
0
|
0
|
|
|
|
|
(S_ISLNK(st->st_mode) && (flags & GIT_MKDIR_REMOVE_SYMLINKS))) { |
360
|
0
|
0
|
|
|
|
|
if (p_unlink(path) < 0) { |
361
|
0
|
0
|
|
|
|
|
git_error_set(GIT_ERROR_OS, "failed to remove %s '%s'", |
362
|
0
|
|
|
|
|
|
S_ISLNK(st->st_mode) ? "symlink" : "file", path); |
363
|
0
|
|
|
|
|
|
return GIT_EEXISTS; |
364
|
|
|
|
|
|
|
} |
365
|
|
|
|
|
|
|
|
366
|
0
|
|
|
|
|
|
opts->perfdata.mkdir_calls++; |
367
|
|
|
|
|
|
|
|
368
|
0
|
0
|
|
|
|
|
if (p_mkdir(path, mode) < 0) { |
369
|
0
|
|
|
|
|
|
git_error_set(GIT_ERROR_OS, "failed to make directory '%s'", path); |
370
|
0
|
|
|
|
|
|
return GIT_EEXISTS; |
371
|
|
|
|
|
|
|
} |
372
|
|
|
|
|
|
|
} |
373
|
|
|
|
|
|
|
|
374
|
301
|
50
|
|
|
|
|
else if (S_ISLNK(st->st_mode)) { |
375
|
|
|
|
|
|
|
/* Re-stat the target, make sure it's a directory */ |
376
|
0
|
|
|
|
|
|
opts->perfdata.stat_calls++; |
377
|
|
|
|
|
|
|
|
378
|
0
|
0
|
|
|
|
|
if (p_stat(path, st) < 0) { |
379
|
0
|
|
|
|
|
|
git_error_set(GIT_ERROR_OS, "failed to make directory '%s'", path); |
380
|
0
|
|
|
|
|
|
return GIT_EEXISTS; |
381
|
|
|
|
|
|
|
} |
382
|
|
|
|
|
|
|
} |
383
|
|
|
|
|
|
|
|
384
|
301
|
50
|
|
|
|
|
else if (!S_ISDIR(st->st_mode)) { |
385
|
0
|
|
|
|
|
|
git_error_set(GIT_ERROR_FILESYSTEM, |
386
|
|
|
|
|
|
|
"failed to make directory '%s': directory exists", path); |
387
|
0
|
|
|
|
|
|
return GIT_EEXISTS; |
388
|
|
|
|
|
|
|
} |
389
|
|
|
|
|
|
|
|
390
|
301
|
|
|
|
|
|
return 0; |
391
|
|
|
|
|
|
|
} |
392
|
|
|
|
|
|
|
|
393
|
495
|
|
|
|
|
|
GIT_INLINE(int) mkdir_validate_mode( |
394
|
|
|
|
|
|
|
const char *path, |
395
|
|
|
|
|
|
|
struct stat *st, |
396
|
|
|
|
|
|
|
bool terminal_path, |
397
|
|
|
|
|
|
|
mode_t mode, |
398
|
|
|
|
|
|
|
uint32_t flags, |
399
|
|
|
|
|
|
|
struct git_futils_mkdir_options *opts) |
400
|
|
|
|
|
|
|
{ |
401
|
495
|
100
|
|
|
|
|
if (((terminal_path && (flags & GIT_MKDIR_CHMOD) != 0) || |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
402
|
0
|
0
|
|
|
|
|
(flags & GIT_MKDIR_CHMOD_PATH) != 0) && st->st_mode != mode) { |
403
|
|
|
|
|
|
|
|
404
|
0
|
|
|
|
|
|
opts->perfdata.chmod_calls++; |
405
|
|
|
|
|
|
|
|
406
|
0
|
0
|
|
|
|
|
if (p_chmod(path, mode) < 0) { |
407
|
0
|
|
|
|
|
|
git_error_set(GIT_ERROR_OS, "failed to set permissions on '%s'", path); |
408
|
0
|
|
|
|
|
|
return -1; |
409
|
|
|
|
|
|
|
} |
410
|
|
|
|
|
|
|
} |
411
|
|
|
|
|
|
|
|
412
|
495
|
|
|
|
|
|
return 0; |
413
|
|
|
|
|
|
|
} |
414
|
|
|
|
|
|
|
|
415
|
517
|
|
|
|
|
|
GIT_INLINE(int) mkdir_canonicalize( |
416
|
|
|
|
|
|
|
git_buf *path, |
417
|
|
|
|
|
|
|
uint32_t flags) |
418
|
|
|
|
|
|
|
{ |
419
|
|
|
|
|
|
|
ssize_t root_len; |
420
|
|
|
|
|
|
|
|
421
|
517
|
50
|
|
|
|
|
if (path->size == 0) { |
422
|
0
|
|
|
|
|
|
git_error_set(GIT_ERROR_OS, "attempt to create empty path"); |
423
|
0
|
|
|
|
|
|
return -1; |
424
|
|
|
|
|
|
|
} |
425
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
/* Trim trailing slashes (except the root) */ |
427
|
517
|
50
|
|
|
|
|
if ((root_len = git_path_root(path->ptr)) < 0) |
428
|
0
|
|
|
|
|
|
root_len = 0; |
429
|
|
|
|
|
|
|
else |
430
|
517
|
|
|
|
|
|
root_len++; |
431
|
|
|
|
|
|
|
|
432
|
562
|
50
|
|
|
|
|
while (path->size > (size_t)root_len && path->ptr[path->size - 1] == '/') |
|
|
100
|
|
|
|
|
|
433
|
45
|
|
|
|
|
|
path->ptr[--path->size] = '\0'; |
434
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
/* if we are not supposed to made the last element, truncate it */ |
436
|
517
|
50
|
|
|
|
|
if ((flags & GIT_MKDIR_SKIP_LAST2) != 0) { |
437
|
0
|
|
|
|
|
|
git_path_dirname_r(path, path->ptr); |
438
|
0
|
|
|
|
|
|
flags |= GIT_MKDIR_SKIP_LAST; |
439
|
|
|
|
|
|
|
} |
440
|
517
|
100
|
|
|
|
|
if ((flags & GIT_MKDIR_SKIP_LAST) != 0) { |
441
|
460
|
|
|
|
|
|
git_path_dirname_r(path, path->ptr); |
442
|
|
|
|
|
|
|
} |
443
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
/* We were either given the root path (or trimmed it to |
445
|
|
|
|
|
|
|
* the root), we don't have anything to do. |
446
|
|
|
|
|
|
|
*/ |
447
|
517
|
50
|
|
|
|
|
if (path->size <= (size_t)root_len) |
448
|
0
|
|
|
|
|
|
git_buf_clear(path); |
449
|
|
|
|
|
|
|
|
450
|
517
|
|
|
|
|
|
return 0; |
451
|
|
|
|
|
|
|
} |
452
|
|
|
|
|
|
|
|
453
|
282
|
|
|
|
|
|
int git_futils_mkdir( |
454
|
|
|
|
|
|
|
const char *path, |
455
|
|
|
|
|
|
|
mode_t mode, |
456
|
|
|
|
|
|
|
uint32_t flags) |
457
|
|
|
|
|
|
|
{ |
458
|
282
|
|
|
|
|
|
git_buf make_path = GIT_BUF_INIT, parent_path = GIT_BUF_INIT; |
459
|
|
|
|
|
|
|
const char *relative; |
460
|
282
|
|
|
|
|
|
struct git_futils_mkdir_options opts = { 0 }; |
461
|
|
|
|
|
|
|
struct stat st; |
462
|
282
|
|
|
|
|
|
size_t depth = 0; |
463
|
282
|
|
|
|
|
|
int len = 0, root_len, error; |
464
|
|
|
|
|
|
|
|
465
|
282
|
50
|
|
|
|
|
if ((error = git_buf_puts(&make_path, path)) < 0 || |
|
|
50
|
|
|
|
|
|
466
|
282
|
50
|
|
|
|
|
(error = mkdir_canonicalize(&make_path, flags)) < 0 || |
467
|
282
|
50
|
|
|
|
|
(error = git_buf_puts(&parent_path, make_path.ptr)) < 0 || |
468
|
282
|
|
|
|
|
|
make_path.size == 0) |
469
|
|
|
|
|
|
|
goto done; |
470
|
|
|
|
|
|
|
|
471
|
282
|
|
|
|
|
|
root_len = git_path_root(make_path.ptr); |
472
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
/* find the first parent directory that exists. this will be used |
474
|
|
|
|
|
|
|
* as the base to dirname_relative. |
475
|
|
|
|
|
|
|
*/ |
476
|
293
|
50
|
|
|
|
|
for (relative = make_path.ptr; parent_path.size; ) { |
477
|
293
|
|
|
|
|
|
error = p_lstat(parent_path.ptr, &st); |
478
|
|
|
|
|
|
|
|
479
|
293
|
100
|
|
|
|
|
if (error == 0) { |
480
|
273
|
|
|
|
|
|
break; |
481
|
20
|
50
|
|
|
|
|
} else if (errno != ENOENT) { |
482
|
0
|
|
|
|
|
|
git_error_set(GIT_ERROR_OS, "failed to stat '%s'", parent_path.ptr); |
483
|
0
|
|
|
|
|
|
error = -1; |
484
|
0
|
|
|
|
|
|
goto done; |
485
|
|
|
|
|
|
|
} |
486
|
|
|
|
|
|
|
|
487
|
20
|
|
|
|
|
|
depth++; |
488
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
/* examine the parent of the current path */ |
490
|
20
|
50
|
|
|
|
|
if ((len = git_path_dirname_r(&parent_path, parent_path.ptr)) < 0) { |
491
|
0
|
|
|
|
|
|
error = len; |
492
|
0
|
|
|
|
|
|
goto done; |
493
|
|
|
|
|
|
|
} |
494
|
|
|
|
|
|
|
|
495
|
20
|
50
|
|
|
|
|
assert(len); |
496
|
|
|
|
|
|
|
|
497
|
|
|
|
|
|
|
/* |
498
|
|
|
|
|
|
|
* We've walked all the given path's parents and it's either relative |
499
|
|
|
|
|
|
|
* (the parent is simply '.') or rooted (the length is less than or |
500
|
|
|
|
|
|
|
* equal to length of the root path). The path may be less than the |
501
|
|
|
|
|
|
|
* root path length on Windows, where `C:` == `C:/`. |
502
|
|
|
|
|
|
|
*/ |
503
|
20
|
50
|
|
|
|
|
if ((len == 1 && parent_path.ptr[0] == '.') || |
|
|
0
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
504
|
20
|
0
|
|
|
|
|
(len == 1 && parent_path.ptr[0] == '/') || |
|
|
50
|
|
|
|
|
|
505
|
|
|
|
|
|
|
len <= root_len) { |
506
|
0
|
|
|
|
|
|
relative = make_path.ptr; |
507
|
0
|
|
|
|
|
|
break; |
508
|
|
|
|
|
|
|
} |
509
|
|
|
|
|
|
|
|
510
|
20
|
|
|
|
|
|
relative = make_path.ptr + len + 1; |
511
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
/* not recursive? just make this directory relative to its parent. */ |
513
|
20
|
100
|
|
|
|
|
if ((flags & GIT_MKDIR_PATH) == 0) |
514
|
9
|
|
|
|
|
|
break; |
515
|
|
|
|
|
|
|
} |
516
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
/* we found an item at the location we're trying to create, |
518
|
|
|
|
|
|
|
* validate it. |
519
|
|
|
|
|
|
|
*/ |
520
|
282
|
100
|
|
|
|
|
if (depth == 0) { |
521
|
268
|
|
|
|
|
|
error = mkdir_validate_dir(make_path.ptr, &st, mode, flags, &opts); |
522
|
|
|
|
|
|
|
|
523
|
268
|
50
|
|
|
|
|
if (!error) |
524
|
268
|
|
|
|
|
|
error = mkdir_validate_mode( |
525
|
268
|
|
|
|
|
|
make_path.ptr, &st, true, mode, flags, &opts); |
526
|
|
|
|
|
|
|
|
527
|
268
|
|
|
|
|
|
goto done; |
528
|
|
|
|
|
|
|
} |
529
|
|
|
|
|
|
|
|
530
|
|
|
|
|
|
|
/* we already took `SKIP_LAST` and `SKIP_LAST2` into account when |
531
|
|
|
|
|
|
|
* canonicalizing `make_path`. |
532
|
|
|
|
|
|
|
*/ |
533
|
14
|
|
|
|
|
|
flags &= ~(GIT_MKDIR_SKIP_LAST2 | GIT_MKDIR_SKIP_LAST); |
534
|
|
|
|
|
|
|
|
535
|
14
|
50
|
|
|
|
|
error = git_futils_mkdir_relative(relative, |
536
|
14
|
|
|
|
|
|
parent_path.size ? parent_path.ptr : NULL, mode, flags, &opts); |
537
|
|
|
|
|
|
|
|
538
|
|
|
|
|
|
|
done: |
539
|
282
|
|
|
|
|
|
git_buf_dispose(&make_path); |
540
|
282
|
|
|
|
|
|
git_buf_dispose(&parent_path); |
541
|
282
|
|
|
|
|
|
return error; |
542
|
|
|
|
|
|
|
} |
543
|
|
|
|
|
|
|
|
544
|
0
|
|
|
|
|
|
int git_futils_mkdir_r(const char *path, const mode_t mode) |
545
|
|
|
|
|
|
|
{ |
546
|
0
|
|
|
|
|
|
return git_futils_mkdir(path, mode, GIT_MKDIR_PATH); |
547
|
|
|
|
|
|
|
} |
548
|
|
|
|
|
|
|
|
549
|
235
|
|
|
|
|
|
int git_futils_mkdir_relative( |
550
|
|
|
|
|
|
|
const char *relative_path, |
551
|
|
|
|
|
|
|
const char *base, |
552
|
|
|
|
|
|
|
mode_t mode, |
553
|
|
|
|
|
|
|
uint32_t flags, |
554
|
|
|
|
|
|
|
struct git_futils_mkdir_options *opts) |
555
|
|
|
|
|
|
|
{ |
556
|
235
|
|
|
|
|
|
git_buf make_path = GIT_BUF_INIT; |
557
|
235
|
|
|
|
|
|
ssize_t root = 0, min_root_len; |
558
|
235
|
|
|
|
|
|
char lastch = '/', *tail; |
559
|
|
|
|
|
|
|
struct stat st; |
560
|
235
|
|
|
|
|
|
struct git_futils_mkdir_options empty_opts = {0}; |
561
|
|
|
|
|
|
|
int error; |
562
|
|
|
|
|
|
|
|
563
|
235
|
100
|
|
|
|
|
if (!opts) |
564
|
187
|
|
|
|
|
|
opts = &empty_opts; |
565
|
|
|
|
|
|
|
|
566
|
|
|
|
|
|
|
/* build path and find "root" where we should start calling mkdir */ |
567
|
235
|
50
|
|
|
|
|
if (git_path_join_unrooted(&make_path, relative_path, base, &root) < 0) |
568
|
0
|
|
|
|
|
|
return -1; |
569
|
|
|
|
|
|
|
|
570
|
235
|
50
|
|
|
|
|
if ((error = mkdir_canonicalize(&make_path, flags)) < 0 || |
|
|
50
|
|
|
|
|
|
571
|
235
|
|
|
|
|
|
make_path.size == 0) |
572
|
|
|
|
|
|
|
goto done; |
573
|
|
|
|
|
|
|
|
574
|
|
|
|
|
|
|
/* if we are not supposed to make the whole path, reset root */ |
575
|
235
|
100
|
|
|
|
|
if ((flags & GIT_MKDIR_PATH) == 0) |
576
|
9
|
|
|
|
|
|
root = git_buf_rfind(&make_path, '/'); |
577
|
|
|
|
|
|
|
|
578
|
|
|
|
|
|
|
/* advance root past drive name or network mount prefix */ |
579
|
235
|
|
|
|
|
|
min_root_len = git_path_root(make_path.ptr); |
580
|
235
|
50
|
|
|
|
|
if (root < min_root_len) |
581
|
0
|
|
|
|
|
|
root = min_root_len; |
582
|
249
|
50
|
|
|
|
|
while (root >= 0 && make_path.ptr[root] == '/') |
|
|
100
|
|
|
|
|
|
583
|
14
|
|
|
|
|
|
++root; |
584
|
|
|
|
|
|
|
|
585
|
|
|
|
|
|
|
/* clip root to make_path length */ |
586
|
235
|
50
|
|
|
|
|
if (root > (ssize_t)make_path.size) |
587
|
0
|
|
|
|
|
|
root = (ssize_t)make_path.size; /* i.e. NUL byte of string */ |
588
|
235
|
50
|
|
|
|
|
if (root < 0) |
589
|
0
|
|
|
|
|
|
root = 0; |
590
|
|
|
|
|
|
|
|
591
|
|
|
|
|
|
|
/* walk down tail of path making each directory */ |
592
|
462
|
100
|
|
|
|
|
for (tail = &make_path.ptr[root]; *tail; *tail = lastch) { |
593
|
227
|
|
|
|
|
|
bool mkdir_attempted = false; |
594
|
|
|
|
|
|
|
|
595
|
|
|
|
|
|
|
/* advance tail to include next path component */ |
596
|
253
|
100
|
|
|
|
|
while (*tail == '/') |
597
|
26
|
|
|
|
|
|
tail++; |
598
|
930
|
100
|
|
|
|
|
while (*tail && *tail != '/') |
|
|
100
|
|
|
|
|
|
599
|
703
|
|
|
|
|
|
tail++; |
600
|
|
|
|
|
|
|
|
601
|
|
|
|
|
|
|
/* truncate path at next component */ |
602
|
227
|
|
|
|
|
|
lastch = *tail; |
603
|
227
|
|
|
|
|
|
*tail = '\0'; |
604
|
227
|
|
|
|
|
|
st.st_mode = 0; |
605
|
|
|
|
|
|
|
|
606
|
227
|
50
|
|
|
|
|
if (opts->dir_map && git_strmap_exists(opts->dir_map, make_path.ptr)) |
|
|
0
|
|
|
|
|
|
607
|
0
|
|
|
|
|
|
continue; |
608
|
|
|
|
|
|
|
|
609
|
|
|
|
|
|
|
/* See what's going on with this path component */ |
610
|
227
|
|
|
|
|
|
opts->perfdata.stat_calls++; |
611
|
|
|
|
|
|
|
|
612
|
|
|
|
|
|
|
retry_lstat: |
613
|
227
|
100
|
|
|
|
|
if (p_lstat(make_path.ptr, &st) < 0) { |
614
|
194
|
50
|
|
|
|
|
if (mkdir_attempted || errno != ENOENT) { |
|
|
50
|
|
|
|
|
|
615
|
0
|
|
|
|
|
|
git_error_set(GIT_ERROR_OS, "cannot access component in path '%s'", make_path.ptr); |
616
|
0
|
|
|
|
|
|
error = -1; |
617
|
0
|
|
|
|
|
|
goto done; |
618
|
|
|
|
|
|
|
} |
619
|
|
|
|
|
|
|
|
620
|
194
|
|
|
|
|
|
git_error_clear(); |
621
|
194
|
|
|
|
|
|
opts->perfdata.mkdir_calls++; |
622
|
194
|
|
|
|
|
|
mkdir_attempted = true; |
623
|
194
|
50
|
|
|
|
|
if (p_mkdir(make_path.ptr, mode) < 0) { |
624
|
0
|
0
|
|
|
|
|
if (errno == EEXIST) |
625
|
0
|
|
|
|
|
|
goto retry_lstat; |
626
|
0
|
|
|
|
|
|
git_error_set(GIT_ERROR_OS, "failed to make directory '%s'", make_path.ptr); |
627
|
0
|
|
|
|
|
|
error = -1; |
628
|
0
|
|
|
|
|
|
goto done; |
629
|
|
|
|
|
|
|
} |
630
|
|
|
|
|
|
|
} else { |
631
|
33
|
50
|
|
|
|
|
if ((error = mkdir_validate_dir( |
632
|
33
|
|
|
|
|
|
make_path.ptr, &st, mode, flags, opts)) < 0) |
633
|
0
|
|
|
|
|
|
goto done; |
634
|
|
|
|
|
|
|
} |
635
|
|
|
|
|
|
|
|
636
|
|
|
|
|
|
|
/* chmod if requested and necessary */ |
637
|
227
|
50
|
|
|
|
|
if ((error = mkdir_validate_mode( |
638
|
227
|
|
|
|
|
|
make_path.ptr, &st, (lastch == '\0'), mode, flags, opts)) < 0) |
639
|
0
|
|
|
|
|
|
goto done; |
640
|
|
|
|
|
|
|
|
641
|
227
|
50
|
|
|
|
|
if (opts->dir_map && opts->pool) { |
|
|
0
|
|
|
|
|
|
642
|
|
|
|
|
|
|
char *cache_path; |
643
|
|
|
|
|
|
|
size_t alloc_size; |
644
|
|
|
|
|
|
|
|
645
|
0
|
0
|
|
|
|
|
GIT_ERROR_CHECK_ALLOC_ADD(&alloc_size, make_path.size, 1); |
|
|
0
|
|
|
|
|
|
646
|
0
|
|
|
|
|
|
cache_path = git_pool_malloc(opts->pool, alloc_size); |
647
|
0
|
0
|
|
|
|
|
GIT_ERROR_CHECK_ALLOC(cache_path); |
648
|
|
|
|
|
|
|
|
649
|
0
|
|
|
|
|
|
memcpy(cache_path, make_path.ptr, make_path.size + 1); |
650
|
|
|
|
|
|
|
|
651
|
0
|
0
|
|
|
|
|
if ((error = git_strmap_set(opts->dir_map, cache_path, cache_path)) < 0) |
652
|
0
|
|
|
|
|
|
goto done; |
653
|
|
|
|
|
|
|
} |
654
|
|
|
|
|
|
|
} |
655
|
|
|
|
|
|
|
|
656
|
235
|
|
|
|
|
|
error = 0; |
657
|
|
|
|
|
|
|
|
658
|
|
|
|
|
|
|
/* check that full path really is a directory if requested & needed */ |
659
|
235
|
100
|
|
|
|
|
if ((flags & GIT_MKDIR_VERIFY_DIR) != 0 && |
|
|
100
|
|
|
|
|
|
660
|
|
|
|
|
|
|
lastch != '\0') { |
661
|
34
|
|
|
|
|
|
opts->perfdata.stat_calls++; |
662
|
|
|
|
|
|
|
|
663
|
34
|
50
|
|
|
|
|
if (p_stat(make_path.ptr, &st) < 0 || !S_ISDIR(st.st_mode)) { |
|
|
50
|
|
|
|
|
|
664
|
0
|
|
|
|
|
|
git_error_set(GIT_ERROR_OS, "path is not a directory '%s'", |
665
|
|
|
|
|
|
|
make_path.ptr); |
666
|
0
|
|
|
|
|
|
error = GIT_ENOTFOUND; |
667
|
|
|
|
|
|
|
} |
668
|
|
|
|
|
|
|
} |
669
|
|
|
|
|
|
|
|
670
|
|
|
|
|
|
|
done: |
671
|
235
|
|
|
|
|
|
git_buf_dispose(&make_path); |
672
|
235
|
|
|
|
|
|
return error; |
673
|
|
|
|
|
|
|
} |
674
|
|
|
|
|
|
|
|
675
|
|
|
|
|
|
|
typedef struct { |
676
|
|
|
|
|
|
|
const char *base; |
677
|
|
|
|
|
|
|
size_t baselen; |
678
|
|
|
|
|
|
|
uint32_t flags; |
679
|
|
|
|
|
|
|
int depth; |
680
|
|
|
|
|
|
|
} futils__rmdir_data; |
681
|
|
|
|
|
|
|
|
682
|
|
|
|
|
|
|
#define FUTILS_MAX_DEPTH 100 |
683
|
|
|
|
|
|
|
|
684
|
0
|
|
|
|
|
|
static int futils__error_cannot_rmdir(const char *path, const char *filemsg) |
685
|
|
|
|
|
|
|
{ |
686
|
0
|
0
|
|
|
|
|
if (filemsg) |
687
|
0
|
|
|
|
|
|
git_error_set(GIT_ERROR_OS, "could not remove directory '%s': %s", |
688
|
|
|
|
|
|
|
path, filemsg); |
689
|
|
|
|
|
|
|
else |
690
|
0
|
|
|
|
|
|
git_error_set(GIT_ERROR_OS, "could not remove directory '%s'", path); |
691
|
|
|
|
|
|
|
|
692
|
0
|
|
|
|
|
|
return -1; |
693
|
|
|
|
|
|
|
} |
694
|
|
|
|
|
|
|
|
695
|
0
|
|
|
|
|
|
static int futils__rm_first_parent(git_buf *path, const char *ceiling) |
696
|
|
|
|
|
|
|
{ |
697
|
0
|
|
|
|
|
|
int error = GIT_ENOTFOUND; |
698
|
|
|
|
|
|
|
struct stat st; |
699
|
|
|
|
|
|
|
|
700
|
0
|
0
|
|
|
|
|
while (error == GIT_ENOTFOUND) { |
701
|
0
|
|
|
|
|
|
git_buf_rtruncate_at_char(path, '/'); |
702
|
|
|
|
|
|
|
|
703
|
0
|
0
|
|
|
|
|
if (!path->size || git__prefixcmp(path->ptr, ceiling) != 0) |
|
|
0
|
|
|
|
|
|
704
|
0
|
|
|
|
|
|
error = 0; |
705
|
0
|
0
|
|
|
|
|
else if (p_lstat_posixly(path->ptr, &st) == 0) { |
706
|
0
|
0
|
|
|
|
|
if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) |
|
|
0
|
|
|
|
|
|
707
|
0
|
|
|
|
|
|
error = p_unlink(path->ptr); |
708
|
0
|
0
|
|
|
|
|
else if (!S_ISDIR(st.st_mode)) |
709
|
0
|
|
|
|
|
|
error = -1; /* fail to remove non-regular file */ |
710
|
0
|
0
|
|
|
|
|
} else if (errno != ENOTDIR) |
711
|
0
|
|
|
|
|
|
error = -1; |
712
|
|
|
|
|
|
|
} |
713
|
|
|
|
|
|
|
|
714
|
0
|
0
|
|
|
|
|
if (error) |
715
|
0
|
|
|
|
|
|
futils__error_cannot_rmdir(path->ptr, "cannot remove parent"); |
716
|
|
|
|
|
|
|
|
717
|
0
|
|
|
|
|
|
return error; |
718
|
|
|
|
|
|
|
} |
719
|
|
|
|
|
|
|
|
720
|
182
|
|
|
|
|
|
static int futils__rmdir_recurs_foreach(void *opaque, git_buf *path) |
721
|
|
|
|
|
|
|
{ |
722
|
182
|
|
|
|
|
|
int error = 0; |
723
|
182
|
|
|
|
|
|
futils__rmdir_data *data = opaque; |
724
|
|
|
|
|
|
|
struct stat st; |
725
|
|
|
|
|
|
|
|
726
|
182
|
50
|
|
|
|
|
if (data->depth > FUTILS_MAX_DEPTH) |
727
|
0
|
|
|
|
|
|
error = futils__error_cannot_rmdir( |
728
|
0
|
|
|
|
|
|
path->ptr, "directory nesting too deep"); |
729
|
|
|
|
|
|
|
|
730
|
182
|
100
|
|
|
|
|
else if ((error = p_lstat_posixly(path->ptr, &st)) < 0) { |
731
|
39
|
50
|
|
|
|
|
if (errno == ENOENT) |
732
|
39
|
|
|
|
|
|
error = 0; |
733
|
0
|
0
|
|
|
|
|
else if (errno == ENOTDIR) { |
734
|
|
|
|
|
|
|
/* asked to remove a/b/c/d/e and a/b is a normal file */ |
735
|
0
|
0
|
|
|
|
|
if ((data->flags & GIT_RMDIR_REMOVE_BLOCKERS) != 0) |
736
|
0
|
|
|
|
|
|
error = futils__rm_first_parent(path, data->base); |
737
|
|
|
|
|
|
|
else |
738
|
0
|
|
|
|
|
|
futils__error_cannot_rmdir( |
739
|
0
|
|
|
|
|
|
path->ptr, "parent is not directory"); |
740
|
|
|
|
|
|
|
} |
741
|
|
|
|
|
|
|
else |
742
|
39
|
|
|
|
|
|
error = git_path_set_error(errno, path->ptr, "rmdir"); |
743
|
|
|
|
|
|
|
} |
744
|
|
|
|
|
|
|
|
745
|
143
|
100
|
|
|
|
|
else if (S_ISDIR(st.st_mode)) { |
746
|
12
|
|
|
|
|
|
data->depth++; |
747
|
|
|
|
|
|
|
|
748
|
12
|
|
|
|
|
|
error = git_path_direach(path, 0, futils__rmdir_recurs_foreach, data); |
749
|
|
|
|
|
|
|
|
750
|
12
|
|
|
|
|
|
data->depth--; |
751
|
|
|
|
|
|
|
|
752
|
12
|
50
|
|
|
|
|
if (error < 0) |
753
|
0
|
|
|
|
|
|
return error; |
754
|
|
|
|
|
|
|
|
755
|
12
|
50
|
|
|
|
|
if (data->depth == 0 && (data->flags & GIT_RMDIR_SKIP_ROOT) != 0) |
|
|
50
|
|
|
|
|
|
756
|
0
|
|
|
|
|
|
return error; |
757
|
|
|
|
|
|
|
|
758
|
12
|
50
|
|
|
|
|
if ((error = p_rmdir(path->ptr)) < 0) { |
759
|
0
|
|
|
|
|
|
if ((data->flags & GIT_RMDIR_SKIP_NONEMPTY) != 0 && |
760
|
0
|
0
|
|
|
|
|
(errno == ENOTEMPTY || errno == EEXIST || errno == EBUSY)) |
|
|
0
|
|
|
|
|
|
761
|
0
|
|
|
|
|
|
error = 0; |
762
|
|
|
|
|
|
|
else |
763
|
12
|
|
|
|
|
|
error = git_path_set_error(errno, path->ptr, "rmdir"); |
764
|
|
|
|
|
|
|
} |
765
|
|
|
|
|
|
|
} |
766
|
|
|
|
|
|
|
|
767
|
131
|
100
|
|
|
|
|
else if ((data->flags & GIT_RMDIR_REMOVE_FILES) != 0) { |
768
|
55
|
50
|
|
|
|
|
if (p_unlink(path->ptr) < 0) |
769
|
55
|
|
|
|
|
|
error = git_path_set_error(errno, path->ptr, "remove"); |
770
|
|
|
|
|
|
|
} |
771
|
|
|
|
|
|
|
|
772
|
76
|
50
|
|
|
|
|
else if ((data->flags & GIT_RMDIR_SKIP_NONEMPTY) == 0) |
773
|
0
|
|
|
|
|
|
error = futils__error_cannot_rmdir(path->ptr, "still present"); |
774
|
|
|
|
|
|
|
|
775
|
182
|
|
|
|
|
|
return error; |
776
|
|
|
|
|
|
|
} |
777
|
|
|
|
|
|
|
|
778
|
56
|
|
|
|
|
|
static int futils__rmdir_empty_parent(void *opaque, const char *path) |
779
|
|
|
|
|
|
|
{ |
780
|
56
|
|
|
|
|
|
futils__rmdir_data *data = opaque; |
781
|
56
|
|
|
|
|
|
int error = 0; |
782
|
|
|
|
|
|
|
|
783
|
56
|
100
|
|
|
|
|
if (strlen(path) <= data->baselen) |
784
|
26
|
|
|
|
|
|
error = GIT_ITEROVER; |
785
|
|
|
|
|
|
|
|
786
|
30
|
100
|
|
|
|
|
else if (p_rmdir(path) < 0) { |
787
|
26
|
|
|
|
|
|
int en = errno; |
788
|
|
|
|
|
|
|
|
789
|
26
|
50
|
|
|
|
|
if (en == ENOENT || en == ENOTDIR) { |
|
|
0
|
|
|
|
|
|
790
|
|
|
|
|
|
|
/* do nothing */ |
791
|
0
|
0
|
|
|
|
|
} else if ((data->flags & GIT_RMDIR_SKIP_NONEMPTY) == 0 && |
|
|
0
|
|
|
|
|
|
792
|
|
|
|
|
|
|
en == EBUSY) { |
793
|
0
|
|
|
|
|
|
error = git_path_set_error(errno, path, "rmdir"); |
794
|
0
|
0
|
|
|
|
|
} else if (en == ENOTEMPTY || en == EEXIST || en == EBUSY) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
795
|
0
|
|
|
|
|
|
error = GIT_ITEROVER; |
796
|
|
|
|
|
|
|
} else { |
797
|
0
|
|
|
|
|
|
error = git_path_set_error(errno, path, "rmdir"); |
798
|
|
|
|
|
|
|
} |
799
|
|
|
|
|
|
|
} |
800
|
|
|
|
|
|
|
|
801
|
56
|
|
|
|
|
|
return error; |
802
|
|
|
|
|
|
|
} |
803
|
|
|
|
|
|
|
|
804
|
145
|
|
|
|
|
|
int git_futils_rmdir_r( |
805
|
|
|
|
|
|
|
const char *path, const char *base, uint32_t flags) |
806
|
|
|
|
|
|
|
{ |
807
|
|
|
|
|
|
|
int error; |
808
|
145
|
|
|
|
|
|
git_buf fullpath = GIT_BUF_INIT; |
809
|
|
|
|
|
|
|
futils__rmdir_data data; |
810
|
|
|
|
|
|
|
|
811
|
|
|
|
|
|
|
/* build path and find "root" where we should start calling mkdir */ |
812
|
145
|
50
|
|
|
|
|
if (git_path_join_unrooted(&fullpath, path, base, NULL) < 0) |
813
|
0
|
|
|
|
|
|
return -1; |
814
|
|
|
|
|
|
|
|
815
|
145
|
|
|
|
|
|
memset(&data, 0, sizeof(data)); |
816
|
145
|
100
|
|
|
|
|
data.base = base ? base : ""; |
817
|
145
|
100
|
|
|
|
|
data.baselen = base ? strlen(base) : 0; |
818
|
145
|
|
|
|
|
|
data.flags = flags; |
819
|
|
|
|
|
|
|
|
820
|
145
|
|
|
|
|
|
error = futils__rmdir_recurs_foreach(&data, &fullpath); |
821
|
|
|
|
|
|
|
|
822
|
|
|
|
|
|
|
/* remove now-empty parents if requested */ |
823
|
145
|
50
|
|
|
|
|
if (!error && (flags & GIT_RMDIR_EMPTY_PARENTS) != 0) |
|
|
100
|
|
|
|
|
|
824
|
26
|
|
|
|
|
|
error = git_path_walk_up( |
825
|
|
|
|
|
|
|
&fullpath, base, futils__rmdir_empty_parent, &data); |
826
|
|
|
|
|
|
|
|
827
|
145
|
100
|
|
|
|
|
if (error == GIT_ITEROVER) { |
828
|
26
|
|
|
|
|
|
git_error_clear(); |
829
|
26
|
|
|
|
|
|
error = 0; |
830
|
|
|
|
|
|
|
} |
831
|
|
|
|
|
|
|
|
832
|
145
|
|
|
|
|
|
git_buf_dispose(&fullpath); |
833
|
|
|
|
|
|
|
|
834
|
145
|
|
|
|
|
|
return error; |
835
|
|
|
|
|
|
|
} |
836
|
|
|
|
|
|
|
|
837
|
0
|
|
|
|
|
|
int git_futils_fake_symlink(const char *old, const char *new) |
838
|
|
|
|
|
|
|
{ |
839
|
0
|
|
|
|
|
|
int retcode = GIT_ERROR; |
840
|
0
|
|
|
|
|
|
int fd = git_futils_creat_withpath(new, 0755, 0644); |
841
|
0
|
0
|
|
|
|
|
if (fd >= 0) { |
842
|
0
|
|
|
|
|
|
retcode = p_write(fd, old, strlen(old)); |
843
|
0
|
|
|
|
|
|
p_close(fd); |
844
|
|
|
|
|
|
|
} |
845
|
0
|
|
|
|
|
|
return retcode; |
846
|
|
|
|
|
|
|
} |
847
|
|
|
|
|
|
|
|
848
|
0
|
|
|
|
|
|
static int cp_by_fd(int ifd, int ofd, bool close_fd_when_done) |
849
|
|
|
|
|
|
|
{ |
850
|
0
|
|
|
|
|
|
int error = 0; |
851
|
|
|
|
|
|
|
char buffer[FILEIO_BUFSIZE]; |
852
|
0
|
|
|
|
|
|
ssize_t len = 0; |
853
|
|
|
|
|
|
|
|
854
|
0
|
0
|
|
|
|
|
while (!error && (len = p_read(ifd, buffer, sizeof(buffer))) > 0) |
|
|
0
|
|
|
|
|
|
855
|
|
|
|
|
|
|
/* p_write() does not have the same semantics as write(). It loops |
856
|
|
|
|
|
|
|
* internally and will return 0 when it has completed writing. |
857
|
|
|
|
|
|
|
*/ |
858
|
0
|
|
|
|
|
|
error = p_write(ofd, buffer, len); |
859
|
|
|
|
|
|
|
|
860
|
0
|
0
|
|
|
|
|
if (len < 0) { |
861
|
0
|
|
|
|
|
|
git_error_set(GIT_ERROR_OS, "read error while copying file"); |
862
|
0
|
|
|
|
|
|
error = (int)len; |
863
|
|
|
|
|
|
|
} |
864
|
|
|
|
|
|
|
|
865
|
0
|
0
|
|
|
|
|
if (error < 0) |
866
|
0
|
|
|
|
|
|
git_error_set(GIT_ERROR_OS, "write error while copying file"); |
867
|
|
|
|
|
|
|
|
868
|
0
|
0
|
|
|
|
|
if (close_fd_when_done) { |
869
|
0
|
|
|
|
|
|
p_close(ifd); |
870
|
0
|
|
|
|
|
|
p_close(ofd); |
871
|
|
|
|
|
|
|
} |
872
|
|
|
|
|
|
|
|
873
|
0
|
|
|
|
|
|
return error; |
874
|
|
|
|
|
|
|
} |
875
|
|
|
|
|
|
|
|
876
|
0
|
|
|
|
|
|
int git_futils_cp(const char *from, const char *to, mode_t filemode) |
877
|
|
|
|
|
|
|
{ |
878
|
|
|
|
|
|
|
int ifd, ofd; |
879
|
|
|
|
|
|
|
|
880
|
0
|
0
|
|
|
|
|
if ((ifd = git_futils_open_ro(from)) < 0) |
881
|
0
|
|
|
|
|
|
return ifd; |
882
|
|
|
|
|
|
|
|
883
|
0
|
0
|
|
|
|
|
if ((ofd = p_open(to, O_WRONLY | O_CREAT | O_EXCL, filemode)) < 0) { |
884
|
0
|
|
|
|
|
|
p_close(ifd); |
885
|
0
|
|
|
|
|
|
return git_path_set_error(errno, to, "open for writing"); |
886
|
|
|
|
|
|
|
} |
887
|
|
|
|
|
|
|
|
888
|
0
|
|
|
|
|
|
return cp_by_fd(ifd, ofd, true); |
889
|
|
|
|
|
|
|
} |
890
|
|
|
|
|
|
|
|
891
|
286
|
|
|
|
|
|
int git_futils_touch(const char *path, time_t *when) |
892
|
|
|
|
|
|
|
{ |
893
|
|
|
|
|
|
|
struct p_timeval times[2]; |
894
|
|
|
|
|
|
|
int ret; |
895
|
|
|
|
|
|
|
|
896
|
286
|
50
|
|
|
|
|
times[0].tv_sec = times[1].tv_sec = when ? *when : time(NULL); |
897
|
286
|
|
|
|
|
|
times[0].tv_usec = times[1].tv_usec = 0; |
898
|
|
|
|
|
|
|
|
899
|
286
|
|
|
|
|
|
ret = p_utimes(path, times); |
900
|
|
|
|
|
|
|
|
901
|
286
|
100
|
|
|
|
|
return (ret < 0) ? git_path_set_error(errno, path, "touch") : 0; |
902
|
|
|
|
|
|
|
} |
903
|
|
|
|
|
|
|
|
904
|
0
|
|
|
|
|
|
static int cp_link(const char *from, const char *to, size_t link_size) |
905
|
|
|
|
|
|
|
{ |
906
|
0
|
|
|
|
|
|
int error = 0; |
907
|
|
|
|
|
|
|
ssize_t read_len; |
908
|
|
|
|
|
|
|
char *link_data; |
909
|
|
|
|
|
|
|
size_t alloc_size; |
910
|
|
|
|
|
|
|
|
911
|
0
|
0
|
|
|
|
|
GIT_ERROR_CHECK_ALLOC_ADD(&alloc_size, link_size, 1); |
|
|
0
|
|
|
|
|
|
912
|
0
|
|
|
|
|
|
link_data = git__malloc(alloc_size); |
913
|
0
|
0
|
|
|
|
|
GIT_ERROR_CHECK_ALLOC(link_data); |
914
|
|
|
|
|
|
|
|
915
|
0
|
|
|
|
|
|
read_len = p_readlink(from, link_data, link_size); |
916
|
0
|
0
|
|
|
|
|
if (read_len != (ssize_t)link_size) { |
917
|
0
|
|
|
|
|
|
git_error_set(GIT_ERROR_OS, "failed to read symlink data for '%s'", from); |
918
|
0
|
|
|
|
|
|
error = -1; |
919
|
|
|
|
|
|
|
} |
920
|
|
|
|
|
|
|
else { |
921
|
0
|
|
|
|
|
|
link_data[read_len] = '\0'; |
922
|
|
|
|
|
|
|
|
923
|
0
|
0
|
|
|
|
|
if (p_symlink(link_data, to) < 0) { |
924
|
0
|
|
|
|
|
|
git_error_set(GIT_ERROR_OS, "could not symlink '%s' as '%s'", |
925
|
|
|
|
|
|
|
link_data, to); |
926
|
0
|
|
|
|
|
|
error = -1; |
927
|
|
|
|
|
|
|
} |
928
|
|
|
|
|
|
|
} |
929
|
|
|
|
|
|
|
|
930
|
0
|
|
|
|
|
|
git__free(link_data); |
931
|
0
|
|
|
|
|
|
return error; |
932
|
|
|
|
|
|
|
} |
933
|
|
|
|
|
|
|
|
934
|
|
|
|
|
|
|
typedef struct { |
935
|
|
|
|
|
|
|
const char *to_root; |
936
|
|
|
|
|
|
|
git_buf to; |
937
|
|
|
|
|
|
|
ssize_t from_prefix; |
938
|
|
|
|
|
|
|
uint32_t flags; |
939
|
|
|
|
|
|
|
uint32_t mkdir_flags; |
940
|
|
|
|
|
|
|
mode_t dirmode; |
941
|
|
|
|
|
|
|
} cp_r_info; |
942
|
|
|
|
|
|
|
|
943
|
|
|
|
|
|
|
#define GIT_CPDIR__MKDIR_DONE_FOR_TO_ROOT (1u << 10) |
944
|
|
|
|
|
|
|
|
945
|
0
|
|
|
|
|
|
static int _cp_r_mkdir(cp_r_info *info, git_buf *from) |
946
|
|
|
|
|
|
|
{ |
947
|
0
|
|
|
|
|
|
int error = 0; |
948
|
|
|
|
|
|
|
|
949
|
|
|
|
|
|
|
/* create root directory the first time we need to create a directory */ |
950
|
0
|
0
|
|
|
|
|
if ((info->flags & GIT_CPDIR__MKDIR_DONE_FOR_TO_ROOT) == 0) { |
951
|
0
|
0
|
|
|
|
|
error = git_futils_mkdir( |
952
|
|
|
|
|
|
|
info->to_root, info->dirmode, |
953
|
0
|
|
|
|
|
|
(info->flags & GIT_CPDIR_CHMOD_DIRS) ? GIT_MKDIR_CHMOD : 0); |
954
|
|
|
|
|
|
|
|
955
|
0
|
|
|
|
|
|
info->flags |= GIT_CPDIR__MKDIR_DONE_FOR_TO_ROOT; |
956
|
|
|
|
|
|
|
} |
957
|
|
|
|
|
|
|
|
958
|
|
|
|
|
|
|
/* create directory with root as base to prevent excess chmods */ |
959
|
0
|
0
|
|
|
|
|
if (!error) |
960
|
0
|
|
|
|
|
|
error = git_futils_mkdir_relative( |
961
|
0
|
|
|
|
|
|
from->ptr + info->from_prefix, info->to_root, |
962
|
|
|
|
|
|
|
info->dirmode, info->mkdir_flags, NULL); |
963
|
|
|
|
|
|
|
|
964
|
0
|
|
|
|
|
|
return error; |
965
|
|
|
|
|
|
|
} |
966
|
|
|
|
|
|
|
|
967
|
0
|
|
|
|
|
|
static int _cp_r_callback(void *ref, git_buf *from) |
968
|
|
|
|
|
|
|
{ |
969
|
0
|
|
|
|
|
|
int error = 0; |
970
|
0
|
|
|
|
|
|
cp_r_info *info = ref; |
971
|
|
|
|
|
|
|
struct stat from_st, to_st; |
972
|
0
|
|
|
|
|
|
bool exists = false; |
973
|
|
|
|
|
|
|
|
974
|
0
|
|
|
|
|
|
if ((info->flags & GIT_CPDIR_COPY_DOTFILES) == 0 && |
975
|
0
|
|
|
|
|
|
from->ptr[git_path_basename_offset(from)] == '.') |
976
|
0
|
|
|
|
|
|
return 0; |
977
|
|
|
|
|
|
|
|
978
|
0
|
0
|
|
|
|
|
if ((error = git_buf_joinpath( |
979
|
0
|
|
|
|
|
|
&info->to, info->to_root, from->ptr + info->from_prefix)) < 0) |
980
|
0
|
|
|
|
|
|
return error; |
981
|
|
|
|
|
|
|
|
982
|
0
|
0
|
|
|
|
|
if (!(error = git_path_lstat(info->to.ptr, &to_st))) |
983
|
0
|
|
|
|
|
|
exists = true; |
984
|
0
|
0
|
|
|
|
|
else if (error != GIT_ENOTFOUND) |
985
|
0
|
|
|
|
|
|
return error; |
986
|
|
|
|
|
|
|
else { |
987
|
0
|
|
|
|
|
|
git_error_clear(); |
988
|
0
|
|
|
|
|
|
error = 0; |
989
|
|
|
|
|
|
|
} |
990
|
|
|
|
|
|
|
|
991
|
0
|
0
|
|
|
|
|
if ((error = git_path_lstat(from->ptr, &from_st)) < 0) |
992
|
0
|
|
|
|
|
|
return error; |
993
|
|
|
|
|
|
|
|
994
|
0
|
0
|
|
|
|
|
if (S_ISDIR(from_st.st_mode)) { |
995
|
0
|
|
|
|
|
|
mode_t oldmode = info->dirmode; |
996
|
|
|
|
|
|
|
|
997
|
|
|
|
|
|
|
/* if we are not chmod'ing, then overwrite dirmode */ |
998
|
0
|
0
|
|
|
|
|
if ((info->flags & GIT_CPDIR_CHMOD_DIRS) == 0) |
999
|
0
|
|
|
|
|
|
info->dirmode = from_st.st_mode; |
1000
|
|
|
|
|
|
|
|
1001
|
|
|
|
|
|
|
/* make directory now if CREATE_EMPTY_DIRS is requested and needed */ |
1002
|
0
|
0
|
|
|
|
|
if (!exists && (info->flags & GIT_CPDIR_CREATE_EMPTY_DIRS) != 0) |
|
|
0
|
|
|
|
|
|
1003
|
0
|
|
|
|
|
|
error = _cp_r_mkdir(info, from); |
1004
|
|
|
|
|
|
|
|
1005
|
|
|
|
|
|
|
/* recurse onto target directory */ |
1006
|
0
|
0
|
|
|
|
|
if (!error && (!exists || S_ISDIR(to_st.st_mode))) |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
1007
|
0
|
|
|
|
|
|
error = git_path_direach(from, 0, _cp_r_callback, info); |
1008
|
|
|
|
|
|
|
|
1009
|
0
|
0
|
|
|
|
|
if (oldmode != 0) |
1010
|
0
|
|
|
|
|
|
info->dirmode = oldmode; |
1011
|
|
|
|
|
|
|
|
1012
|
0
|
|
|
|
|
|
return error; |
1013
|
|
|
|
|
|
|
} |
1014
|
|
|
|
|
|
|
|
1015
|
0
|
0
|
|
|
|
|
if (exists) { |
1016
|
0
|
0
|
|
|
|
|
if ((info->flags & GIT_CPDIR_OVERWRITE) == 0) |
1017
|
0
|
|
|
|
|
|
return 0; |
1018
|
|
|
|
|
|
|
|
1019
|
0
|
0
|
|
|
|
|
if (p_unlink(info->to.ptr) < 0) { |
1020
|
0
|
|
|
|
|
|
git_error_set(GIT_ERROR_OS, "cannot overwrite existing file '%s'", |
1021
|
|
|
|
|
|
|
info->to.ptr); |
1022
|
0
|
|
|
|
|
|
return GIT_EEXISTS; |
1023
|
|
|
|
|
|
|
} |
1024
|
|
|
|
|
|
|
} |
1025
|
|
|
|
|
|
|
|
1026
|
|
|
|
|
|
|
/* Done if this isn't a regular file or a symlink */ |
1027
|
0
|
0
|
|
|
|
|
if (!S_ISREG(from_st.st_mode) && |
|
|
0
|
|
|
|
|
|
1028
|
0
|
0
|
|
|
|
|
(!S_ISLNK(from_st.st_mode) || |
1029
|
0
|
|
|
|
|
|
(info->flags & GIT_CPDIR_COPY_SYMLINKS) == 0)) |
1030
|
0
|
|
|
|
|
|
return 0; |
1031
|
|
|
|
|
|
|
|
1032
|
|
|
|
|
|
|
/* Make container directory on demand if needed */ |
1033
|
0
|
0
|
|
|
|
|
if ((info->flags & GIT_CPDIR_CREATE_EMPTY_DIRS) == 0 && |
|
|
0
|
|
|
|
|
|
1034
|
|
|
|
|
|
|
(error = _cp_r_mkdir(info, from)) < 0) |
1035
|
0
|
|
|
|
|
|
return error; |
1036
|
|
|
|
|
|
|
|
1037
|
|
|
|
|
|
|
/* make symlink or regular file */ |
1038
|
0
|
0
|
|
|
|
|
if (info->flags & GIT_CPDIR_LINK_FILES) { |
1039
|
0
|
0
|
|
|
|
|
if ((error = p_link(from->ptr, info->to.ptr)) < 0) |
1040
|
0
|
|
|
|
|
|
git_error_set(GIT_ERROR_OS, "failed to link '%s'", from->ptr); |
1041
|
0
|
0
|
|
|
|
|
} else if (S_ISLNK(from_st.st_mode)) { |
1042
|
0
|
|
|
|
|
|
error = cp_link(from->ptr, info->to.ptr, (size_t)from_st.st_size); |
1043
|
|
|
|
|
|
|
} else { |
1044
|
0
|
|
|
|
|
|
mode_t usemode = from_st.st_mode; |
1045
|
|
|
|
|
|
|
|
1046
|
0
|
0
|
|
|
|
|
if ((info->flags & GIT_CPDIR_SIMPLE_TO_MODE) != 0) |
1047
|
0
|
0
|
|
|
|
|
usemode = GIT_PERMS_FOR_WRITE(usemode); |
1048
|
|
|
|
|
|
|
|
1049
|
0
|
|
|
|
|
|
error = git_futils_cp(from->ptr, info->to.ptr, usemode); |
1050
|
|
|
|
|
|
|
} |
1051
|
|
|
|
|
|
|
|
1052
|
0
|
|
|
|
|
|
return error; |
1053
|
|
|
|
|
|
|
} |
1054
|
|
|
|
|
|
|
|
1055
|
0
|
|
|
|
|
|
int git_futils_cp_r( |
1056
|
|
|
|
|
|
|
const char *from, |
1057
|
|
|
|
|
|
|
const char *to, |
1058
|
|
|
|
|
|
|
uint32_t flags, |
1059
|
|
|
|
|
|
|
mode_t dirmode) |
1060
|
|
|
|
|
|
|
{ |
1061
|
|
|
|
|
|
|
int error; |
1062
|
0
|
|
|
|
|
|
git_buf path = GIT_BUF_INIT; |
1063
|
|
|
|
|
|
|
cp_r_info info; |
1064
|
|
|
|
|
|
|
|
1065
|
0
|
0
|
|
|
|
|
if (git_buf_joinpath(&path, from, "") < 0) /* ensure trailing slash */ |
1066
|
0
|
|
|
|
|
|
return -1; |
1067
|
|
|
|
|
|
|
|
1068
|
0
|
|
|
|
|
|
memset(&info, 0, sizeof(info)); |
1069
|
0
|
|
|
|
|
|
info.to_root = to; |
1070
|
0
|
|
|
|
|
|
info.flags = flags; |
1071
|
0
|
|
|
|
|
|
info.dirmode = dirmode; |
1072
|
0
|
|
|
|
|
|
info.from_prefix = path.size; |
1073
|
0
|
|
|
|
|
|
git_buf_init(&info.to, 0); |
1074
|
|
|
|
|
|
|
|
1075
|
|
|
|
|
|
|
/* precalculate mkdir flags */ |
1076
|
0
|
0
|
|
|
|
|
if ((flags & GIT_CPDIR_CREATE_EMPTY_DIRS) == 0) { |
1077
|
|
|
|
|
|
|
/* if not creating empty dirs, then use mkdir to create the path on |
1078
|
|
|
|
|
|
|
* demand right before files are copied. |
1079
|
|
|
|
|
|
|
*/ |
1080
|
0
|
|
|
|
|
|
info.mkdir_flags = GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST; |
1081
|
0
|
0
|
|
|
|
|
if ((flags & GIT_CPDIR_CHMOD_DIRS) != 0) |
1082
|
0
|
|
|
|
|
|
info.mkdir_flags |= GIT_MKDIR_CHMOD_PATH; |
1083
|
|
|
|
|
|
|
} else { |
1084
|
|
|
|
|
|
|
/* otherwise, we will do simple mkdir as directories are encountered */ |
1085
|
0
|
0
|
|
|
|
|
info.mkdir_flags = |
1086
|
0
|
|
|
|
|
|
((flags & GIT_CPDIR_CHMOD_DIRS) != 0) ? GIT_MKDIR_CHMOD : 0; |
1087
|
|
|
|
|
|
|
} |
1088
|
|
|
|
|
|
|
|
1089
|
0
|
|
|
|
|
|
error = _cp_r_callback(&info, &path); |
1090
|
|
|
|
|
|
|
|
1091
|
0
|
|
|
|
|
|
git_buf_dispose(&path); |
1092
|
0
|
|
|
|
|
|
git_buf_dispose(&info.to); |
1093
|
|
|
|
|
|
|
|
1094
|
0
|
|
|
|
|
|
return error; |
1095
|
|
|
|
|
|
|
} |
1096
|
|
|
|
|
|
|
|
1097
|
2540
|
|
|
|
|
|
int git_futils_filestamp_check( |
1098
|
|
|
|
|
|
|
git_futils_filestamp *stamp, const char *path) |
1099
|
|
|
|
|
|
|
{ |
1100
|
|
|
|
|
|
|
struct stat st; |
1101
|
|
|
|
|
|
|
|
1102
|
|
|
|
|
|
|
/* if the stamp is NULL, then always reload */ |
1103
|
2540
|
50
|
|
|
|
|
if (stamp == NULL) |
1104
|
0
|
|
|
|
|
|
return 1; |
1105
|
|
|
|
|
|
|
|
1106
|
2540
|
100
|
|
|
|
|
if (p_stat(path, &st) < 0) |
1107
|
1201
|
|
|
|
|
|
return GIT_ENOTFOUND; |
1108
|
|
|
|
|
|
|
|
1109
|
1339
|
100
|
|
|
|
|
if (stamp->mtime.tv_sec == st.st_mtime && |
|
|
100
|
|
|
|
|
|
1110
|
|
|
|
|
|
|
#if defined(GIT_USE_NSEC) |
1111
|
|
|
|
|
|
|
stamp->mtime.tv_nsec == st.st_mtime_nsec && |
1112
|
|
|
|
|
|
|
#endif |
1113
|
1254
|
100
|
|
|
|
|
stamp->size == (uint64_t)st.st_size && |
1114
|
1254
|
|
|
|
|
|
stamp->ino == (unsigned int)st.st_ino) |
1115
|
1213
|
|
|
|
|
|
return 0; |
1116
|
|
|
|
|
|
|
|
1117
|
126
|
|
|
|
|
|
stamp->mtime.tv_sec = st.st_mtime; |
1118
|
|
|
|
|
|
|
#if defined(GIT_USE_NSEC) |
1119
|
|
|
|
|
|
|
stamp->mtime.tv_nsec = st.st_mtime_nsec; |
1120
|
|
|
|
|
|
|
#endif |
1121
|
126
|
|
|
|
|
|
stamp->size = (uint64_t)st.st_size; |
1122
|
126
|
|
|
|
|
|
stamp->ino = (unsigned int)st.st_ino; |
1123
|
|
|
|
|
|
|
|
1124
|
2540
|
|
|
|
|
|
return 1; |
1125
|
|
|
|
|
|
|
} |
1126
|
|
|
|
|
|
|
|
1127
|
82
|
|
|
|
|
|
void git_futils_filestamp_set( |
1128
|
|
|
|
|
|
|
git_futils_filestamp *target, const git_futils_filestamp *source) |
1129
|
|
|
|
|
|
|
{ |
1130
|
82
|
50
|
|
|
|
|
assert(target); |
1131
|
|
|
|
|
|
|
|
1132
|
82
|
100
|
|
|
|
|
if (source) |
1133
|
11
|
|
|
|
|
|
memcpy(target, source, sizeof(*target)); |
1134
|
|
|
|
|
|
|
else |
1135
|
71
|
|
|
|
|
|
memset(target, 0, sizeof(*target)); |
1136
|
82
|
|
|
|
|
|
} |
1137
|
|
|
|
|
|
|
|
1138
|
|
|
|
|
|
|
|
1139
|
83
|
|
|
|
|
|
void git_futils_filestamp_set_from_stat( |
1140
|
|
|
|
|
|
|
git_futils_filestamp *stamp, struct stat *st) |
1141
|
|
|
|
|
|
|
{ |
1142
|
83
|
50
|
|
|
|
|
if (st) { |
1143
|
83
|
|
|
|
|
|
stamp->mtime.tv_sec = st->st_mtime; |
1144
|
|
|
|
|
|
|
#if defined(GIT_USE_NSEC) |
1145
|
|
|
|
|
|
|
stamp->mtime.tv_nsec = st->st_mtime_nsec; |
1146
|
|
|
|
|
|
|
#else |
1147
|
83
|
|
|
|
|
|
stamp->mtime.tv_nsec = 0; |
1148
|
|
|
|
|
|
|
#endif |
1149
|
83
|
|
|
|
|
|
stamp->size = (uint64_t)st->st_size; |
1150
|
83
|
|
|
|
|
|
stamp->ino = (unsigned int)st->st_ino; |
1151
|
|
|
|
|
|
|
} else { |
1152
|
0
|
|
|
|
|
|
memset(stamp, 0, sizeof(*stamp)); |
1153
|
|
|
|
|
|
|
} |
1154
|
83
|
|
|
|
|
|
} |
1155
|
|
|
|
|
|
|
|
1156
|
0
|
|
|
|
|
|
int git_futils_fsync_dir(const char *path) |
1157
|
|
|
|
|
|
|
{ |
1158
|
|
|
|
|
|
|
#ifdef GIT_WIN32 |
1159
|
|
|
|
|
|
|
GIT_UNUSED(path); |
1160
|
|
|
|
|
|
|
return 0; |
1161
|
|
|
|
|
|
|
#else |
1162
|
0
|
|
|
|
|
|
int fd, error = -1; |
1163
|
|
|
|
|
|
|
|
1164
|
0
|
0
|
|
|
|
|
if ((fd = p_open(path, O_RDONLY)) < 0) { |
1165
|
0
|
|
|
|
|
|
git_error_set(GIT_ERROR_OS, "failed to open directory '%s' for fsync", path); |
1166
|
0
|
|
|
|
|
|
return -1; |
1167
|
|
|
|
|
|
|
} |
1168
|
|
|
|
|
|
|
|
1169
|
0
|
0
|
|
|
|
|
if ((error = p_fsync(fd)) < 0) |
1170
|
0
|
|
|
|
|
|
git_error_set(GIT_ERROR_OS, "failed to fsync directory '%s'", path); |
1171
|
|
|
|
|
|
|
|
1172
|
0
|
|
|
|
|
|
p_close(fd); |
1173
|
0
|
|
|
|
|
|
return error; |
1174
|
|
|
|
|
|
|
#endif |
1175
|
|
|
|
|
|
|
} |
1176
|
|
|
|
|
|
|
|
1177
|
0
|
|
|
|
|
|
int git_futils_fsync_parent(const char *path) |
1178
|
|
|
|
|
|
|
{ |
1179
|
|
|
|
|
|
|
char *parent; |
1180
|
|
|
|
|
|
|
int error; |
1181
|
|
|
|
|
|
|
|
1182
|
0
|
0
|
|
|
|
|
if ((parent = git_path_dirname(path)) == NULL) |
1183
|
0
|
|
|
|
|
|
return -1; |
1184
|
|
|
|
|
|
|
|
1185
|
0
|
|
|
|
|
|
error = git_futils_fsync_dir(parent); |
1186
|
0
|
|
|
|
|
|
git__free(parent); |
1187
|
0
|
|
|
|
|
|
return error; |
1188
|
|
|
|
|
|
|
} |