]> git.proxmox.com Git - libgit2.git/blame - src/fileops.c
Fix a win32 warning message
[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
b6c93aef
RB
82mode_t git_futils_canonical_mode(mode_t raw_mode)
83{
84 if (S_ISREG(raw_mode))
85 return S_IFREG | GIT_CANONICAL_PERMS(raw_mode);
86 else if (S_ISLNK(raw_mode))
87 return S_IFLNK;
88 else if (S_ISDIR(raw_mode))
89 return S_IFDIR;
90 else if (S_ISGITLINK(raw_mode))
91 return S_IFGITLINK;
92 else
93 return 0;
94}
95
13224ea4 96int git_futils_readbuffer_updated(git_buf *buf, const char *path, time_t *mtime, int *updated)
75d58430
RJ
97{
98 git_file fd;
90d4d2f0 99 size_t len;
c3da9f06 100 struct stat st;
75d58430 101
13224ea4 102 assert(buf && path && *path);
75d58430 103
c3da9f06
CMN
104 if (updated != NULL)
105 *updated = 0;
75d58430 106
13224ea4
VM
107 if ((fd = p_open(path, O_RDONLY)) < 0) {
108 return git__throw(GIT_ENOTFOUND, "Failed to read file '%s': %s", path, strerror(errno));
109 }
c3da9f06 110
13224ea4
VM
111 if (p_fstat(fd, &st) < 0 || S_ISDIR(st.st_mode) || !git__is_sizet(st.st_size+1)) {
112 close(fd);
113 return git__throw(GIT_EOSERR, "Failed to stat file '%s'", path);
114 }
c3da9f06
CMN
115
116 /*
117 * If we were given a time, we only want to read the file if it
118 * has been modified.
119 */
13224ea4
VM
120 if (mtime != NULL && *mtime >= st.st_mtime) {
121 close(fd);
122 return 0;
123 }
c3da9f06
CMN
124
125 if (mtime != NULL)
126 *mtime = st.st_mtime;
c3da9f06
CMN
127
128 len = (size_t) st.st_size;
129
13224ea4 130 git_buf_clear(buf);
90d4d2f0 131
13224ea4
VM
132 if (git_buf_grow(buf, len + 1) < 0) {
133 close(fd);
77c3999c 134 return GIT_ENOMEM;
75d58430
RJ
135 }
136
13224ea4
VM
137 buf->ptr[len] = '\0';
138
139 while (len > 0) {
140 ssize_t read_size = p_read(fd, buf->ptr, len);
141
142 if (read_size < 0) {
143 close(fd);
144 return git__throw(GIT_EOSERR, "Failed to read from FD");
145 }
146
147 len -= read_size;
148 buf->size += read_size;
75d58430
RJ
149 }
150
f79026b4 151 p_close(fd);
75d58430 152
c3da9f06
CMN
153 if (mtime != NULL)
154 *mtime = st.st_mtime;
13224ea4 155
c3da9f06
CMN
156 if (updated != NULL)
157 *updated = 1;
158
13224ea4 159 return 0;
c3da9f06
CMN
160}
161
13224ea4 162int git_futils_readbuffer(git_buf *buf, const char *path)
97769280 163{
13224ea4 164 return git_futils_readbuffer_updated(buf, path, NULL, NULL);
97769280
RB
165}
166
ce8cd006 167int git_futils_mv_withpath(const char *from, const char *to, const mode_t dirmode)
19a30a3f 168{
ce8cd006 169 if (git_futils_mkpath2file(to, dirmode) < GIT_SUCCESS)
77c3999c 170 return GIT_EOSERR; /* The callee already takes care of setting the correct error message. */
19a30a3f 171
0c49ec2d 172 return p_rename(from, to); /* The callee already takes care of setting the correct error message. */
19a30a3f
VM
173}
174
f79026b4 175int git_futils_mmap_ro(git_map *out, git_file fd, git_off_t begin, size_t len)
20e7f426 176{
f79026b4 177 return p_mmap(out, len, GIT_PROT_READ, GIT_MAP_SHARED, fd, begin);
20e7f426
SP
178}
179
74fa4bfa
RB
180int git_futils_mmap_ro_file(git_map *out, const char *path)
181{
182 git_file fd = p_open(path, O_RDONLY /* | O_NOATIME */);
da9abdd6
RB
183 git_off_t len = git_futils_filesize(fd);
184 int result;
185 if (!git__is_sizet(len))
186 return git__throw(GIT_ERROR, "File `%s` too large to mmap", path);
187 result = git_futils_mmap_ro(out, fd, 0, (size_t)len);
74fa4bfa
RB
188 p_close(fd);
189 return result;
190}
191
f79026b4 192void git_futils_mmap_free(git_map *out)
20e7f426 193{
f79026b4 194 p_munmap(out);
20e7f426
SP
195}
196
97769280 197int git_futils_mkdir_r(const char *path, const char *base, const mode_t mode)
1a5204a7 198{
c90292ce 199 int error, root_path_offset;
97769280
RB
200 git_buf make_path = GIT_BUF_INIT;
201 size_t start;
40c44d2f 202 char *pp, *sp;
f0b2bfe5 203
97769280
RB
204 if (base != NULL) {
205 start = strlen(base);
206 error = git_buf_joinpath(&make_path, base, path);
207 } else {
208 start = 0;
209 error = git_buf_puts(&make_path, path);
210 }
211 if (error < GIT_SUCCESS)
212 return git__rethrow(error, "Failed to create `%s` tree structure", path);
f0b2bfe5 213
97769280 214 pp = make_path.ptr + start;
40c44d2f 215
97769280 216 root_path_offset = git_path_root(make_path.ptr);
c90292ce 217 if (root_path_offset > 0)
218 pp += root_path_offset; /* On Windows, will skip the drive name (eg. C: or D:) */
2e29957a 219
87d9869f 220 while (error == GIT_SUCCESS && (sp = strchr(pp, '/')) != NULL) {
1744fafe 221 if (sp != pp && git_path_isdir(make_path.ptr) < GIT_SUCCESS) {
d5f25204 222 *sp = 0;
97769280 223 error = p_mkdir(make_path.ptr, mode);
412de9a6 224
225 /* Do not choke while trying to recreate an existing directory */
226 if (errno == EEXIST)
227 error = GIT_SUCCESS;
228
d5f25204
VM
229 *sp = '/';
230 }
40c44d2f 231
d5f25204
VM
232 pp = sp + 1;
233 }
f0b2bfe5 234
a993e4fe 235 if (*pp != '\0' && error == GIT_SUCCESS) {
97769280 236 error = p_mkdir(make_path.ptr, mode);
a993e4fe
RG
237 if (errno == EEXIST)
238 error = GIT_SUCCESS;
239 }
40c44d2f 240
97769280 241 git_buf_free(&make_path);
77c3999c 242
243 if (error < GIT_SUCCESS)
244 return git__throw(error, "Failed to recursively create `%s` tree structure", path);
245
246 return GIT_SUCCESS;
170d3f2f 247}
248
97769280 249static int _rmdir_recurs_foreach(void *opaque, git_buf *path)
42b3a460
MS
250{
251 int error = GIT_SUCCESS;
858dba58 252 int force = *(int *)opaque;
42b3a460 253
1744fafe
RB
254 if (git_path_isdir(path->ptr) == GIT_SUCCESS) {
255 error = git_path_direach(path, _rmdir_recurs_foreach, opaque);
97769280
RB
256 if (error < GIT_SUCCESS)
257 return git__rethrow(error, "Failed to remove directory `%s`", path->ptr);
258 return p_rmdir(path->ptr);
42b3a460 259
858dba58 260 } else if (force) {
97769280 261 return p_unlink(path->ptr);
858dba58
VM
262 }
263
97769280 264 return git__rethrow(error, "Failed to remove directory. `%s` is not empty", path->ptr);
42b3a460
MS
265}
266
858dba58 267int git_futils_rmdir_r(const char *path, int force)
42b3a460 268{
97769280
RB
269 int error;
270 git_buf p = GIT_BUF_INIT;
271
272 error = git_buf_sets(&p, path);
273 if (error == GIT_SUCCESS)
274 error = _rmdir_recurs_foreach(&force, &p);
275 git_buf_free(&p);
276 return error;
42b3a460
MS
277}
278
73b51450
RB
279int git_futils_find_global_file(git_buf *path, const char *filename)
280{
281 int error;
282 const char *home = getenv("HOME");
283
284#ifdef GIT_WIN32
285 if (home == NULL)
286 home = getenv("USERPROFILE");
287#endif
288
289 if (home == NULL)
290 return git__throw(GIT_EOSERR, "Failed to open global %s file. "
291 "Cannot locate the user's home directory.", filename);
292
293 if ((error = git_buf_joinpath(path, home, filename)) < GIT_SUCCESS)
294 return error;
295
1744fafe 296 if (git_path_exists(path->ptr) < GIT_SUCCESS) {
73b51450
RB
297 git_buf_clear(path);
298 return GIT_ENOTFOUND;
299 }
300
301 return GIT_SUCCESS;
302}
303
304#ifdef GIT_WIN32
305typedef struct {
306 wchar_t *path;
307 DWORD len;
308} win32_path;
309
310static const win32_path *win32_system_root(void)
311{
312 static win32_path s_root = { 0, 0 };
313
314 if (s_root.path == NULL) {
315 const wchar_t *root_tmpl = L"%PROGRAMFILES%\\Git\\etc\\";
316
317 s_root.len = ExpandEnvironmentStringsW(root_tmpl, NULL, 0);
318
319 if (s_root.len <= 0) {
320 git__throw(GIT_EOSERR, "Failed to expand environment strings");
321 return NULL;
322 }
323
324 s_root.path = git__calloc(s_root.len, sizeof(wchar_t));
325 if (s_root.path == NULL)
326 return NULL;
327
328 if (ExpandEnvironmentStringsW(root_tmpl, s_root.path, s_root.len) != s_root.len) {
329 git__throw(GIT_EOSERR, "Failed to expand environment strings");
330 git__free(s_root.path);
331 s_root.path = NULL;
332 return NULL;
333 }
334 }
335
336 return &s_root;
337}
338
339static int win32_find_system_file(git_buf *path, const char *filename)
340{
341 int error = GIT_SUCCESS;
342 const win32_path *root = win32_system_root();
343 size_t len;
344 wchar_t *file_utf16 = NULL, *scan;
345 char *file_utf8 = NULL;
346
347 if (!root || !filename || (len = strlen(filename)) == 0)
348 return GIT_ENOTFOUND;
349
350 /* allocate space for wchar_t path to file */
351 file_utf16 = git__calloc(root->len + len + 2, sizeof(wchar_t));
352 if (!file_utf16)
353 return GIT_ENOMEM;
354
355 /* append root + '\\' + filename as wchar_t */
356 memcpy(file_utf16, root->path, root->len * sizeof(wchar_t));
357
358 if (*filename == '/' || *filename == '\\')
359 filename++;
360
361 if (gitwin_append_utf16(file_utf16 + root->len - 1, filename, len + 1) !=
f46e6226 362 (int)len + 1) {
73b51450
RB
363 error = git__throw(GIT_EOSERR, "Failed to build file path");
364 goto cleanup;
365 }
366
367 for (scan = file_utf16; *scan; scan++)
368 if (*scan == L'/')
369 *scan = L'\\';
370
371 /* check access */
372 if (_waccess(file_utf16, F_OK) < 0) {
373 error = GIT_ENOTFOUND;
374 goto cleanup;
375 }
376
377 /* convert to utf8 */
378 if ((file_utf8 = gitwin_from_utf16(file_utf16)) == NULL)
379 error = GIT_ENOMEM;
380
381 if (file_utf8) {
382 git_path_mkposix(file_utf8);
383 git_buf_attach(path, file_utf8, 0);
384 }
385
386cleanup:
387 git__free(file_utf16);
388
389 return error;
390}
391#endif
392
393int git_futils_find_system_file(git_buf *path, const char *filename)
394{
395 if (git_buf_joinpath(path, "/etc", filename) < GIT_SUCCESS)
396 return git_buf_lasterror(path);
397
1744fafe 398 if (git_path_exists(path->ptr) == GIT_SUCCESS)
73b51450
RB
399 return GIT_SUCCESS;
400
401 git_buf_clear(path);
402
403#ifdef GIT_WIN32
404 return win32_find_system_file(path, filename);
405#else
406 return GIT_ENOTFOUND;
407#endif
408}