]> git.proxmox.com Git - libgit2.git/blame - src/fileops.c
Fix check for writing remote's fetch and push configurations
[libgit2.git] / src / fileops.c
CommitLineData
bb742ede 1/*
5e0de328 2 * Copyright (C) 2009-2012 the libgit2 contributors
bb742ede
VM
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 11int 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 34int 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 51int 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 59int 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 65int 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 73git_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 82int 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
142int git_futils_readbuffer(git_fbuffer *obj, const char *path)
143{
144 return git_futils_readbuffer_updated(obj, path, NULL, NULL);
145}
146
97769280
RB
147void 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 155void 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 163int 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 171int 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 176void git_futils_mmap_free(git_map *out)
20e7f426 177{
f79026b4 178 p_munmap(out);
20e7f426
SP
179}
180
97769280 181int 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 233static 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 251int 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
263int 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
289typedef struct {
290 wchar_t *path;
291 DWORD len;
292} win32_path;
293
294static 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
323static 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
370cleanup:
371 git__free(file_utf16);
372
373 return error;
374}
375#endif
376
377int 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}