]>
Commit | Line | Data |
---|---|---|
bb742ede VM |
1 | /* |
2 | * Copyright (C) 2009-2011 the libgit2 contributors | |
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 | */ | |
5ee2fe77 | 7 | #include "common.h" |
ec250c6e | 8 | #include "fileops.h" |
2e29957a | 9 | #include <ctype.h> |
ec250c6e | 10 | |
ce8cd006 | 11 | int git_futils_mkpath2file(const char *file_path, const mode_t mode) |
55ffebe3 | 12 | { |
97769280 RB |
13 | int error; |
14 | git_buf target_folder = GIT_BUF_INIT; | |
55ffebe3 | 15 | |
97769280 RB |
16 | error = git_path_dirname_r(&target_folder, file_path); |
17 | if (error < GIT_SUCCESS) { | |
18 | git_buf_free(&target_folder); | |
77c3999c | 19 | return git__throw(GIT_EINVALIDPATH, "Failed to recursively build `%s` tree structure. Unable to parse parent folder name", file_path); |
97769280 RB |
20 | } else { |
21 | /* reset error */ | |
22 | error = GIT_SUCCESS; | |
23 | } | |
55ffebe3 VM |
24 | |
25 | /* Does the containing folder exist? */ | |
1744fafe | 26 | if (git_path_isdir(target_folder.ptr) != GIT_SUCCESS) |
55ffebe3 | 27 | /* Let's create the tree structure */ |
97769280 | 28 | error = git_futils_mkdir_r(target_folder.ptr, NULL, mode); |
55ffebe3 | 29 | |
97769280 RB |
30 | git_buf_free(&target_folder); |
31 | return error; | |
55ffebe3 VM |
32 | } |
33 | ||
97769280 | 34 | int git_futils_mktmp(git_buf *path_out, const char *filename) |
72a3fe42 VM |
35 | { |
36 | int fd; | |
37 | ||
97769280 RB |
38 | git_buf_sets(path_out, filename); |
39 | git_buf_puts(path_out, "_git2_XXXXXX"); | |
40 | ||
41 | if (git_buf_oom(path_out)) | |
42 | return git__rethrow(git_buf_lasterror(path_out), | |
43 | "Failed to create temporary file for %s", filename); | |
72a3fe42 | 44 | |
97769280 RB |
45 | if ((fd = p_mkstemp(path_out->ptr)) < 0) |
46 | return git__throw(GIT_EOSERR, "Failed to create temporary file %s", path_out->ptr); | |
72a3fe42 | 47 | |
f978b748 | 48 | return fd; |
72a3fe42 VM |
49 | } |
50 | ||
ce8cd006 | 51 | int git_futils_creat_withpath(const char *path, const mode_t dirmode, const mode_t mode) |
7dd8a9f7 | 52 | { |
ce8cd006 | 53 | if (git_futils_mkpath2file(path, dirmode) < GIT_SUCCESS) |
cc2ac058 | 54 | return git__throw(GIT_EOSERR, "Failed to create file %s", path); |
55ffebe3 | 55 | |
f79026b4 | 56 | return p_creat(path, mode); |
55ffebe3 VM |
57 | } |
58 | ||
33127043 | 59 | int git_futils_creat_locked(const char *path, const mode_t mode) |
1549cba9 RG |
60 | { |
61 | int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_EXCL, mode); | |
62 | return fd >= 0 ? fd : git__throw(GIT_EOSERR, "Failed to create locked file. Could not open %s", path); | |
63 | } | |
64 | ||
ce8cd006 | 65 | int git_futils_creat_locked_withpath(const char *path, const mode_t dirmode, const mode_t mode) |
1549cba9 | 66 | { |
ce8cd006 | 67 | if (git_futils_mkpath2file(path, dirmode) < GIT_SUCCESS) |
1549cba9 RG |
68 | return git__throw(GIT_EOSERR, "Failed to create locked file %s", path); |
69 | ||
f79026b4 | 70 | return git_futils_creat_locked(path, mode); |
ec250c6e AE |
71 | } |
72 | ||
f79026b4 | 73 | git_off_t git_futils_filesize(git_file fd) |
ec250c6e | 74 | { |
7dd8a9f7 | 75 | struct stat sb; |
f79026b4 | 76 | if (p_fstat(fd, &sb)) |
5ad739e8 VM |
77 | return GIT_ERROR; |
78 | ||
ec250c6e AE |
79 | return sb.st_size; |
80 | } | |
4188d28f | 81 | |
c3da9f06 | 82 | int git_futils_readbuffer_updated(git_fbuffer *obj, const char *path, time_t *mtime, int *updated) |
75d58430 RJ |
83 | { |
84 | git_file fd; | |
90d4d2f0 | 85 | size_t len; |
c3da9f06 | 86 | struct stat st; |
42fd40db | 87 | unsigned char *buff; |
75d58430 RJ |
88 | |
89 | assert(obj && path && *path); | |
90 | ||
c3da9f06 CMN |
91 | if (updated != NULL) |
92 | *updated = 0; | |
75d58430 | 93 | |
c3da9f06 CMN |
94 | if (p_stat(path, &st) < 0) |
95 | return git__throw(GIT_ENOTFOUND, "Failed to stat file %s", path); | |
96 | ||
97 | if (S_ISDIR(st.st_mode)) | |
98 | return git__throw(GIT_ERROR, "Can't read a dir into a buffer"); | |
99 | ||
100 | /* | |
101 | * If we were given a time, we only want to read the file if it | |
102 | * has been modified. | |
103 | */ | |
104 | if (mtime != NULL && *mtime >= st.st_mtime) | |
105 | return GIT_SUCCESS; | |
106 | ||
107 | if (mtime != NULL) | |
108 | *mtime = st.st_mtime; | |
109 | if (!git__is_sizet(st.st_size+1)) | |
d3ca89fe | 110 | return git__throw(GIT_ERROR, "Failed to read file `%s`. An error occured while calculating its size", path); |
c3da9f06 CMN |
111 | |
112 | len = (size_t) st.st_size; | |
113 | ||
114 | if ((fd = p_open(path, O_RDONLY)) < 0) | |
115 | return git__throw(GIT_EOSERR, "Failed to open %s for reading", path); | |
90d4d2f0 RJ |
116 | |
117 | if ((buff = git__malloc(len + 1)) == NULL) { | |
f79026b4 | 118 | p_close(fd); |
77c3999c | 119 | return GIT_ENOMEM; |
75d58430 RJ |
120 | } |
121 | ||
f79026b4 VM |
122 | if (p_read(fd, buff, len) < 0) { |
123 | p_close(fd); | |
3286c408 | 124 | git__free(buff); |
77c3999c | 125 | return git__throw(GIT_ERROR, "Failed to read file `%s`", path); |
75d58430 | 126 | } |
42fd40db | 127 | buff[len] = '\0'; |
75d58430 | 128 | |
f79026b4 | 129 | p_close(fd); |
75d58430 | 130 | |
c3da9f06 CMN |
131 | if (mtime != NULL) |
132 | *mtime = st.st_mtime; | |
133 | if (updated != NULL) | |
134 | *updated = 1; | |
135 | ||
75d58430 | 136 | obj->data = buff; |
87d9869f | 137 | obj->len = len; |
75d58430 RJ |
138 | |
139 | return GIT_SUCCESS; | |
140 | } | |
141 | ||
c3da9f06 CMN |
142 | int git_futils_readbuffer(git_fbuffer *obj, const char *path) |
143 | { | |
144 | return git_futils_readbuffer_updated(obj, path, NULL, NULL); | |
145 | } | |
146 | ||
97769280 RB |
147 | void git_futils_fbuffer_rtrim(git_fbuffer *obj) |
148 | { | |
149 | unsigned char *buff = obj->data; | |
150 | while (obj->len > 0 && isspace(buff[obj->len - 1])) | |
151 | obj->len--; | |
152 | buff[obj->len] = '\0'; | |
153 | } | |
154 | ||
f79026b4 | 155 | void git_futils_freebuffer(git_fbuffer *obj) |
75d58430 RJ |
156 | { |
157 | assert(obj); | |
3286c408 | 158 | git__free(obj->data); |
75d58430 RJ |
159 | obj->data = NULL; |
160 | } | |
161 | ||
ca481fc4 | 162 | |
ce8cd006 | 163 | int git_futils_mv_withpath(const char *from, const char *to, const mode_t dirmode) |
19a30a3f | 164 | { |
ce8cd006 | 165 | if (git_futils_mkpath2file(to, dirmode) < GIT_SUCCESS) |
77c3999c | 166 | return GIT_EOSERR; /* The callee already takes care of setting the correct error message. */ |
19a30a3f | 167 | |
0c49ec2d | 168 | return p_rename(from, to); /* The callee already takes care of setting the correct error message. */ |
19a30a3f VM |
169 | } |
170 | ||
f79026b4 | 171 | int git_futils_mmap_ro(git_map *out, git_file fd, git_off_t begin, size_t len) |
20e7f426 | 172 | { |
f79026b4 | 173 | return p_mmap(out, len, GIT_PROT_READ, GIT_MAP_SHARED, fd, begin); |
20e7f426 SP |
174 | } |
175 | ||
f79026b4 | 176 | void git_futils_mmap_free(git_map *out) |
20e7f426 | 177 | { |
f79026b4 | 178 | p_munmap(out); |
20e7f426 SP |
179 | } |
180 | ||
97769280 | 181 | int git_futils_mkdir_r(const char *path, const char *base, const mode_t mode) |
1a5204a7 | 182 | { |
c90292ce | 183 | int error, root_path_offset; |
97769280 RB |
184 | git_buf make_path = GIT_BUF_INIT; |
185 | size_t start; | |
40c44d2f | 186 | char *pp, *sp; |
f0b2bfe5 | 187 | |
97769280 RB |
188 | if (base != NULL) { |
189 | start = strlen(base); | |
190 | error = git_buf_joinpath(&make_path, base, path); | |
191 | } else { | |
192 | start = 0; | |
193 | error = git_buf_puts(&make_path, path); | |
194 | } | |
195 | if (error < GIT_SUCCESS) | |
196 | return git__rethrow(error, "Failed to create `%s` tree structure", path); | |
f0b2bfe5 | 197 | |
97769280 | 198 | pp = make_path.ptr + start; |
40c44d2f | 199 | |
97769280 | 200 | root_path_offset = git_path_root(make_path.ptr); |
c90292ce | 201 | if (root_path_offset > 0) |
202 | pp += root_path_offset; /* On Windows, will skip the drive name (eg. C: or D:) */ | |
2e29957a | 203 | |
87d9869f | 204 | while (error == GIT_SUCCESS && (sp = strchr(pp, '/')) != NULL) { |
1744fafe | 205 | if (sp != pp && git_path_isdir(make_path.ptr) < GIT_SUCCESS) { |
d5f25204 | 206 | *sp = 0; |
97769280 | 207 | error = p_mkdir(make_path.ptr, mode); |
412de9a6 | 208 | |
209 | /* Do not choke while trying to recreate an existing directory */ | |
210 | if (errno == EEXIST) | |
211 | error = GIT_SUCCESS; | |
212 | ||
d5f25204 VM |
213 | *sp = '/'; |
214 | } | |
40c44d2f | 215 | |
d5f25204 VM |
216 | pp = sp + 1; |
217 | } | |
f0b2bfe5 | 218 | |
a993e4fe | 219 | if (*pp != '\0' && error == GIT_SUCCESS) { |
97769280 | 220 | error = p_mkdir(make_path.ptr, mode); |
a993e4fe RG |
221 | if (errno == EEXIST) |
222 | error = GIT_SUCCESS; | |
223 | } | |
40c44d2f | 224 | |
97769280 | 225 | git_buf_free(&make_path); |
77c3999c | 226 | |
227 | if (error < GIT_SUCCESS) | |
228 | return git__throw(error, "Failed to recursively create `%s` tree structure", path); | |
229 | ||
230 | return GIT_SUCCESS; | |
170d3f2f | 231 | } |
232 | ||
97769280 | 233 | static int _rmdir_recurs_foreach(void *opaque, git_buf *path) |
42b3a460 MS |
234 | { |
235 | int error = GIT_SUCCESS; | |
858dba58 | 236 | int force = *(int *)opaque; |
42b3a460 | 237 | |
1744fafe RB |
238 | if (git_path_isdir(path->ptr) == GIT_SUCCESS) { |
239 | error = git_path_direach(path, _rmdir_recurs_foreach, opaque); | |
97769280 RB |
240 | if (error < GIT_SUCCESS) |
241 | return git__rethrow(error, "Failed to remove directory `%s`", path->ptr); | |
242 | return p_rmdir(path->ptr); | |
42b3a460 | 243 | |
858dba58 | 244 | } else if (force) { |
97769280 | 245 | return p_unlink(path->ptr); |
858dba58 VM |
246 | } |
247 | ||
97769280 | 248 | return git__rethrow(error, "Failed to remove directory. `%s` is not empty", path->ptr); |
42b3a460 MS |
249 | } |
250 | ||
858dba58 | 251 | int git_futils_rmdir_r(const char *path, int force) |
42b3a460 | 252 | { |
97769280 RB |
253 | int error; |
254 | git_buf p = GIT_BUF_INIT; | |
255 | ||
256 | error = git_buf_sets(&p, path); | |
257 | if (error == GIT_SUCCESS) | |
258 | error = _rmdir_recurs_foreach(&force, &p); | |
259 | git_buf_free(&p); | |
260 | return error; | |
42b3a460 MS |
261 | } |
262 | ||
73b51450 RB |
263 | int git_futils_find_global_file(git_buf *path, const char *filename) |
264 | { | |
265 | int error; | |
266 | const char *home = getenv("HOME"); | |
267 | ||
268 | #ifdef GIT_WIN32 | |
269 | if (home == NULL) | |
270 | home = getenv("USERPROFILE"); | |
271 | #endif | |
272 | ||
273 | if (home == NULL) | |
274 | return git__throw(GIT_EOSERR, "Failed to open global %s file. " | |
275 | "Cannot locate the user's home directory.", filename); | |
276 | ||
277 | if ((error = git_buf_joinpath(path, home, filename)) < GIT_SUCCESS) | |
278 | return error; | |
279 | ||
1744fafe | 280 | if (git_path_exists(path->ptr) < GIT_SUCCESS) { |
73b51450 RB |
281 | git_buf_clear(path); |
282 | return GIT_ENOTFOUND; | |
283 | } | |
284 | ||
285 | return GIT_SUCCESS; | |
286 | } | |
287 | ||
288 | #ifdef GIT_WIN32 | |
289 | typedef struct { | |
290 | wchar_t *path; | |
291 | DWORD len; | |
292 | } win32_path; | |
293 | ||
294 | static const win32_path *win32_system_root(void) | |
295 | { | |
296 | static win32_path s_root = { 0, 0 }; | |
297 | ||
298 | if (s_root.path == NULL) { | |
299 | const wchar_t *root_tmpl = L"%PROGRAMFILES%\\Git\\etc\\"; | |
300 | ||
301 | s_root.len = ExpandEnvironmentStringsW(root_tmpl, NULL, 0); | |
302 | ||
303 | if (s_root.len <= 0) { | |
304 | git__throw(GIT_EOSERR, "Failed to expand environment strings"); | |
305 | return NULL; | |
306 | } | |
307 | ||
308 | s_root.path = git__calloc(s_root.len, sizeof(wchar_t)); | |
309 | if (s_root.path == NULL) | |
310 | return NULL; | |
311 | ||
312 | if (ExpandEnvironmentStringsW(root_tmpl, s_root.path, s_root.len) != s_root.len) { | |
313 | git__throw(GIT_EOSERR, "Failed to expand environment strings"); | |
314 | git__free(s_root.path); | |
315 | s_root.path = NULL; | |
316 | return NULL; | |
317 | } | |
318 | } | |
319 | ||
320 | return &s_root; | |
321 | } | |
322 | ||
323 | static int win32_find_system_file(git_buf *path, const char *filename) | |
324 | { | |
325 | int error = GIT_SUCCESS; | |
326 | const win32_path *root = win32_system_root(); | |
327 | size_t len; | |
328 | wchar_t *file_utf16 = NULL, *scan; | |
329 | char *file_utf8 = NULL; | |
330 | ||
331 | if (!root || !filename || (len = strlen(filename)) == 0) | |
332 | return GIT_ENOTFOUND; | |
333 | ||
334 | /* allocate space for wchar_t path to file */ | |
335 | file_utf16 = git__calloc(root->len + len + 2, sizeof(wchar_t)); | |
336 | if (!file_utf16) | |
337 | return GIT_ENOMEM; | |
338 | ||
339 | /* append root + '\\' + filename as wchar_t */ | |
340 | memcpy(file_utf16, root->path, root->len * sizeof(wchar_t)); | |
341 | ||
342 | if (*filename == '/' || *filename == '\\') | |
343 | filename++; | |
344 | ||
345 | if (gitwin_append_utf16(file_utf16 + root->len - 1, filename, len + 1) != | |
f46e6226 | 346 | (int)len + 1) { |
73b51450 RB |
347 | error = git__throw(GIT_EOSERR, "Failed to build file path"); |
348 | goto cleanup; | |
349 | } | |
350 | ||
351 | for (scan = file_utf16; *scan; scan++) | |
352 | if (*scan == L'/') | |
353 | *scan = L'\\'; | |
354 | ||
355 | /* check access */ | |
356 | if (_waccess(file_utf16, F_OK) < 0) { | |
357 | error = GIT_ENOTFOUND; | |
358 | goto cleanup; | |
359 | } | |
360 | ||
361 | /* convert to utf8 */ | |
362 | if ((file_utf8 = gitwin_from_utf16(file_utf16)) == NULL) | |
363 | error = GIT_ENOMEM; | |
364 | ||
365 | if (file_utf8) { | |
366 | git_path_mkposix(file_utf8); | |
367 | git_buf_attach(path, file_utf8, 0); | |
368 | } | |
369 | ||
370 | cleanup: | |
371 | git__free(file_utf16); | |
372 | ||
373 | return error; | |
374 | } | |
375 | #endif | |
376 | ||
377 | int git_futils_find_system_file(git_buf *path, const char *filename) | |
378 | { | |
379 | if (git_buf_joinpath(path, "/etc", filename) < GIT_SUCCESS) | |
380 | return git_buf_lasterror(path); | |
381 | ||
1744fafe | 382 | if (git_path_exists(path->ptr) == GIT_SUCCESS) |
73b51450 RB |
383 | return GIT_SUCCESS; |
384 | ||
385 | git_buf_clear(path); | |
386 | ||
387 | #ifdef GIT_WIN32 | |
388 | return win32_find_system_file(path, filename); | |
389 | #else | |
390 | return GIT_ENOTFOUND; | |
391 | #endif | |
392 | } |