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