]> git.proxmox.com Git - libgit2.git/blob - src/fileops.c
Merge pull request #474 from schu/clang-unused
[libgit2.git] / src / fileops.c
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 */
7 #include "common.h"
8 #include "fileops.h"
9 #include <ctype.h>
10
11 int git_futils_mkpath2file(const char *file_path, const mode_t mode)
12 {
13 int error;
14 git_buf target_folder = GIT_BUF_INIT;
15
16 error = git_path_dirname_r(&target_folder, file_path);
17 if (error < GIT_SUCCESS) {
18 git_buf_free(&target_folder);
19 return git__throw(GIT_EINVALIDPATH, "Failed to recursively build `%s` tree structure. Unable to parse parent folder name", file_path);
20 } else {
21 /* reset error */
22 error = GIT_SUCCESS;
23 }
24
25 /* Does the containing folder exist? */
26 if (git_path_isdir(target_folder.ptr) != GIT_SUCCESS)
27 /* Let's create the tree structure */
28 error = git_futils_mkdir_r(target_folder.ptr, NULL, mode);
29
30 git_buf_free(&target_folder);
31 return error;
32 }
33
34 int git_futils_mktmp(git_buf *path_out, const char *filename)
35 {
36 int fd;
37
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);
44
45 if ((fd = p_mkstemp(path_out->ptr)) < 0)
46 return git__throw(GIT_EOSERR, "Failed to create temporary file %s", path_out->ptr);
47
48 return fd;
49 }
50
51 int git_futils_creat_withpath(const char *path, const mode_t dirmode, const mode_t mode)
52 {
53 if (git_futils_mkpath2file(path, dirmode) < GIT_SUCCESS)
54 return git__throw(GIT_EOSERR, "Failed to create file %s", path);
55
56 return p_creat(path, mode);
57 }
58
59 int git_futils_creat_locked(const char *path, const mode_t mode)
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
65 int git_futils_creat_locked_withpath(const char *path, const mode_t dirmode, const mode_t mode)
66 {
67 if (git_futils_mkpath2file(path, dirmode) < GIT_SUCCESS)
68 return git__throw(GIT_EOSERR, "Failed to create locked file %s", path);
69
70 return git_futils_creat_locked(path, mode);
71 }
72
73 git_off_t git_futils_filesize(git_file fd)
74 {
75 struct stat sb;
76 if (p_fstat(fd, &sb))
77 return GIT_ERROR;
78
79 return sb.st_size;
80 }
81
82 int git_futils_readbuffer_updated(git_fbuffer *obj, const char *path, time_t *mtime, int *updated)
83 {
84 git_file fd;
85 size_t len;
86 struct stat st;
87 unsigned char *buff;
88
89 assert(obj && path && *path);
90
91 if (updated != NULL)
92 *updated = 0;
93
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))
110 return git__throw(GIT_ERROR, "Failed to read file `%s`. An error occured while calculating its size", path);
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);
116
117 if ((buff = git__malloc(len + 1)) == NULL) {
118 p_close(fd);
119 return GIT_ENOMEM;
120 }
121
122 if (p_read(fd, buff, len) < 0) {
123 p_close(fd);
124 git__free(buff);
125 return git__throw(GIT_ERROR, "Failed to read file `%s`", path);
126 }
127 buff[len] = '\0';
128
129 p_close(fd);
130
131 if (mtime != NULL)
132 *mtime = st.st_mtime;
133 if (updated != NULL)
134 *updated = 1;
135
136 obj->data = buff;
137 obj->len = len;
138
139 return GIT_SUCCESS;
140 }
141
142 int git_futils_readbuffer(git_fbuffer *obj, const char *path)
143 {
144 return git_futils_readbuffer_updated(obj, path, NULL, NULL);
145 }
146
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
155 void git_futils_freebuffer(git_fbuffer *obj)
156 {
157 assert(obj);
158 git__free(obj->data);
159 obj->data = NULL;
160 }
161
162
163 int git_futils_mv_withpath(const char *from, const char *to, const mode_t dirmode)
164 {
165 if (git_futils_mkpath2file(to, dirmode) < GIT_SUCCESS)
166 return GIT_EOSERR; /* The callee already takes care of setting the correct error message. */
167
168 return p_rename(from, to); /* The callee already takes care of setting the correct error message. */
169 }
170
171 int git_futils_mmap_ro(git_map *out, git_file fd, git_off_t begin, size_t len)
172 {
173 return p_mmap(out, len, GIT_PROT_READ, GIT_MAP_SHARED, fd, begin);
174 }
175
176 void git_futils_mmap_free(git_map *out)
177 {
178 p_munmap(out);
179 }
180
181 int git_futils_mkdir_r(const char *path, const char *base, const mode_t mode)
182 {
183 int error, root_path_offset;
184 git_buf make_path = GIT_BUF_INIT;
185 size_t start;
186 char *pp, *sp;
187
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);
197
198 pp = make_path.ptr + start;
199
200 root_path_offset = git_path_root(make_path.ptr);
201 if (root_path_offset > 0)
202 pp += root_path_offset; /* On Windows, will skip the drive name (eg. C: or D:) */
203
204 while (error == GIT_SUCCESS && (sp = strchr(pp, '/')) != NULL) {
205 if (sp != pp && git_path_isdir(make_path.ptr) < GIT_SUCCESS) {
206 *sp = 0;
207 error = p_mkdir(make_path.ptr, mode);
208
209 /* Do not choke while trying to recreate an existing directory */
210 if (errno == EEXIST)
211 error = GIT_SUCCESS;
212
213 *sp = '/';
214 }
215
216 pp = sp + 1;
217 }
218
219 if (*pp != '\0' && error == GIT_SUCCESS) {
220 error = p_mkdir(make_path.ptr, mode);
221 if (errno == EEXIST)
222 error = GIT_SUCCESS;
223 }
224
225 git_buf_free(&make_path);
226
227 if (error < GIT_SUCCESS)
228 return git__throw(error, "Failed to recursively create `%s` tree structure", path);
229
230 return GIT_SUCCESS;
231 }
232
233 static int _rmdir_recurs_foreach(void *opaque, git_buf *path)
234 {
235 int error = GIT_SUCCESS;
236 int force = *(int *)opaque;
237
238 if (git_path_isdir(path->ptr) == GIT_SUCCESS) {
239 error = git_path_direach(path, _rmdir_recurs_foreach, opaque);
240 if (error < GIT_SUCCESS)
241 return git__rethrow(error, "Failed to remove directory `%s`", path->ptr);
242 return p_rmdir(path->ptr);
243
244 } else if (force) {
245 return p_unlink(path->ptr);
246 }
247
248 return git__rethrow(error, "Failed to remove directory. `%s` is not empty", path->ptr);
249 }
250
251 int git_futils_rmdir_r(const char *path, int force)
252 {
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;
261 }
262
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
280 if (git_path_exists(path->ptr) < GIT_SUCCESS) {
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) !=
346 (int)len + 1) {
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
382 if (git_path_exists(path->ptr) == GIT_SUCCESS)
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 }