mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-06 23:44:06 +00:00
327 lines
7.6 KiB
C
327 lines
7.6 KiB
C
#include "common.h"
|
|
#include "fileops.h"
|
|
#include <ctype.h>
|
|
|
|
int git_futils_mv_atomic(const char *from, const char *to)
|
|
{
|
|
#ifdef GIT_WIN32
|
|
/*
|
|
* Win32 POSIX compilance my ass. If the destination
|
|
* file exists, the `rename` call fails. This is as
|
|
* close as it gets with the Win32 API.
|
|
*/
|
|
return MoveFileEx(from, to, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) ? GIT_SUCCESS : GIT_EOSERR;
|
|
#else
|
|
/* Don't even try this on Win32 */
|
|
if (!link(from, to)) {
|
|
p_unlink(from);
|
|
return GIT_SUCCESS;
|
|
}
|
|
|
|
if (!rename(from, to))
|
|
return GIT_SUCCESS;
|
|
|
|
return GIT_ERROR;
|
|
#endif
|
|
}
|
|
|
|
int git_futils_mkpath2file(const char *file_path)
|
|
{
|
|
const int mode = 0755; /* or 0777 ? */
|
|
int error = GIT_SUCCESS;
|
|
char target_folder_path[GIT_PATH_MAX];
|
|
|
|
error = git_path_dirname_r(target_folder_path, sizeof(target_folder_path), file_path);
|
|
if (error < GIT_SUCCESS)
|
|
return git__throw(GIT_EINVALIDPATH, "Failed to recursively build `%s` tree structure. Unable to parse parent folder name", file_path);
|
|
|
|
/* Does the containing folder exist? */
|
|
if (git_futils_isdir(target_folder_path)) {
|
|
git_path_join(target_folder_path, target_folder_path, ""); /* Ensure there's a trailing slash */
|
|
|
|
/* Let's create the tree structure */
|
|
error = git_futils_mkdir_r(target_folder_path, mode);
|
|
if (error < GIT_SUCCESS)
|
|
return error; /* The callee already takes care of setting the correct error message. */
|
|
}
|
|
|
|
return GIT_SUCCESS;
|
|
}
|
|
|
|
int git_futils_mktmp(char *path_out, const char *filename)
|
|
{
|
|
int fd;
|
|
|
|
strcpy(path_out, filename);
|
|
strcat(path_out, "_git2_XXXXXX");
|
|
|
|
#if defined(_MSC_VER)
|
|
/* FIXME: there may be race conditions when multi-threading
|
|
* with the library */
|
|
if (_mktemp_s(path_out, GIT_PATH_MAX) != 0)
|
|
return git__throw(GIT_EOSERR, "Failed to make temporary file %s", path_out);
|
|
|
|
fd = p_creat(path_out, 0744);
|
|
#else
|
|
fd = mkstemp(path_out);
|
|
#endif
|
|
|
|
return fd >= 0 ? fd : git__throw(GIT_EOSERR, "Failed to create temporary file %s", path_out);
|
|
}
|
|
|
|
int git_futils_creat_withpath(const char *path, int mode)
|
|
{
|
|
if (git_futils_mkpath2file(path) < GIT_SUCCESS)
|
|
return git__throw(GIT_EOSERR, "Failed to create file %s", path);
|
|
|
|
return p_creat(path, mode);
|
|
}
|
|
|
|
int git_futils_creat_locked(const char *path, int mode)
|
|
{
|
|
int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_EXCL, mode);
|
|
return fd >= 0 ? fd : git__throw(GIT_EOSERR, "Failed to create locked file. Could not open %s", path);
|
|
}
|
|
|
|
int git_futils_creat_locked_withpath(const char *path, int mode)
|
|
{
|
|
if (git_futils_mkpath2file(path) < GIT_SUCCESS)
|
|
return git__throw(GIT_EOSERR, "Failed to create locked file %s", path);
|
|
|
|
return git_futils_creat_locked(path, mode);
|
|
}
|
|
|
|
int git_futils_isdir(const char *path)
|
|
{
|
|
#ifdef GIT_WIN32
|
|
DWORD attr = GetFileAttributes(path);
|
|
if (attr == INVALID_FILE_ATTRIBUTES)
|
|
return GIT_ERROR;
|
|
|
|
return (attr & FILE_ATTRIBUTE_DIRECTORY) ? GIT_SUCCESS : GIT_ERROR;
|
|
|
|
#else
|
|
struct stat st;
|
|
if (p_stat(path, &st) < GIT_SUCCESS)
|
|
return GIT_ERROR;
|
|
|
|
return S_ISDIR(st.st_mode) ? GIT_SUCCESS : GIT_ERROR;
|
|
#endif
|
|
}
|
|
|
|
int git_futils_isfile(const char *path)
|
|
{
|
|
struct stat st;
|
|
int stat_error;
|
|
|
|
assert(path);
|
|
stat_error = p_stat(path, &st);
|
|
|
|
if (stat_error < GIT_SUCCESS)
|
|
return -1;
|
|
|
|
if (!S_ISREG(st.st_mode))
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int git_futils_exists(const char *path)
|
|
{
|
|
assert(path);
|
|
return access(path, F_OK);
|
|
}
|
|
|
|
git_off_t git_futils_filesize(git_file fd)
|
|
{
|
|
struct stat sb;
|
|
if (p_fstat(fd, &sb))
|
|
return GIT_ERROR;
|
|
|
|
return sb.st_size;
|
|
}
|
|
|
|
int git_futils_readbuffer(git_fbuffer *obj, const char *path)
|
|
{
|
|
git_file fd;
|
|
size_t len;
|
|
git_off_t size;
|
|
unsigned char *buff;
|
|
|
|
assert(obj && path && *path);
|
|
|
|
if ((fd = p_open(path, O_RDONLY)) < 0)
|
|
return git__throw(GIT_ERROR, "Failed to open %s for reading", path);
|
|
|
|
if (((size = git_futils_filesize(fd)) < 0) || !git__is_sizet(size+1)) {
|
|
p_close(fd);
|
|
return git__throw(GIT_ERROR, "Failed to read file `%s`. An error occured while calculating its size", path);
|
|
}
|
|
len = (size_t) size;
|
|
|
|
if ((buff = git__malloc(len + 1)) == NULL) {
|
|
p_close(fd);
|
|
return GIT_ENOMEM;
|
|
}
|
|
|
|
if (p_read(fd, buff, len) < 0) {
|
|
p_close(fd);
|
|
free(buff);
|
|
return git__throw(GIT_ERROR, "Failed to read file `%s`", path);
|
|
}
|
|
buff[len] = '\0';
|
|
|
|
p_close(fd);
|
|
|
|
obj->data = buff;
|
|
obj->len = len;
|
|
|
|
return GIT_SUCCESS;
|
|
}
|
|
|
|
void git_futils_freebuffer(git_fbuffer *obj)
|
|
{
|
|
assert(obj);
|
|
free(obj->data);
|
|
obj->data = NULL;
|
|
}
|
|
|
|
|
|
int git_futils_mv_withpath(const char *from, const char *to)
|
|
{
|
|
if (git_futils_mkpath2file(to) < GIT_SUCCESS)
|
|
return GIT_EOSERR; /* The callee already takes care of setting the correct error message. */
|
|
|
|
return git_futils_mv_atomic(from, to); /* The callee already takes care of setting the correct error message. */
|
|
}
|
|
|
|
int git_futils_mmap_ro(git_map *out, git_file fd, git_off_t begin, size_t len)
|
|
{
|
|
return p_mmap(out, len, GIT_PROT_READ, GIT_MAP_SHARED, fd, begin);
|
|
}
|
|
|
|
void git_futils_mmap_free(git_map *out)
|
|
{
|
|
p_munmap(out);
|
|
}
|
|
|
|
int git_futils_direach(
|
|
char *path,
|
|
size_t path_sz,
|
|
int (*fn)(void *, char *),
|
|
void *arg)
|
|
{
|
|
size_t wd_len = strlen(path);
|
|
DIR *dir;
|
|
struct dirent *de;
|
|
|
|
if (!wd_len || path_sz < wd_len + 2)
|
|
return git__throw(GIT_EINVALIDARGS, "Failed to process `%s` tree structure. Path is either empty or buffer size is too short", path);
|
|
|
|
while (path[wd_len - 1] == '/')
|
|
wd_len--;
|
|
path[wd_len++] = '/';
|
|
path[wd_len] = '\0';
|
|
|
|
dir = opendir(path);
|
|
if (!dir)
|
|
return git__throw(GIT_EOSERR, "Failed to process `%s` tree structure. An error occured while opening the directory", path);
|
|
|
|
while ((de = readdir(dir)) != NULL) {
|
|
size_t de_len;
|
|
int result;
|
|
|
|
/* always skip '.' and '..' */
|
|
if (de->d_name[0] == '.') {
|
|
if (de->d_name[1] == '\0')
|
|
continue;
|
|
if (de->d_name[1] == '.' && de->d_name[2] == '\0')
|
|
continue;
|
|
}
|
|
|
|
de_len = strlen(de->d_name);
|
|
if (path_sz < wd_len + de_len + 1) {
|
|
closedir(dir);
|
|
return git__throw(GIT_ERROR, "Failed to process `%s` tree structure. Buffer size is too short", path);
|
|
}
|
|
|
|
strcpy(path + wd_len, de->d_name);
|
|
result = fn(arg, path);
|
|
if (result < GIT_SUCCESS) {
|
|
closedir(dir);
|
|
return result; /* The callee is reponsible for setting the correct error message */
|
|
}
|
|
if (result > 0) {
|
|
closedir(dir);
|
|
return result;
|
|
}
|
|
}
|
|
|
|
closedir(dir);
|
|
return GIT_SUCCESS;
|
|
}
|
|
|
|
int git_futils_mkdir_r(const char *path, int mode)
|
|
{
|
|
int error, root_path_offset;
|
|
char *pp, *sp;
|
|
char *path_copy = git__strdup(path);
|
|
|
|
if (path_copy == NULL)
|
|
return GIT_ENOMEM;
|
|
|
|
error = GIT_SUCCESS;
|
|
pp = path_copy;
|
|
|
|
root_path_offset = git_path_root(pp);
|
|
if (root_path_offset > 0)
|
|
pp += root_path_offset; /* On Windows, will skip the drive name (eg. C: or D:) */
|
|
|
|
while (error == GIT_SUCCESS && (sp = strchr(pp, '/')) != NULL) {
|
|
if (sp != pp && git_futils_isdir(path_copy) < GIT_SUCCESS) {
|
|
*sp = 0;
|
|
error = p_mkdir(path_copy, mode);
|
|
|
|
/* Do not choke while trying to recreate an existing directory */
|
|
if (errno == EEXIST)
|
|
error = GIT_SUCCESS;
|
|
|
|
*sp = '/';
|
|
}
|
|
|
|
pp = sp + 1;
|
|
}
|
|
|
|
if (*pp != '\0' && error == GIT_SUCCESS) {
|
|
error = p_mkdir(path, mode);
|
|
if (errno == EEXIST)
|
|
error = GIT_SUCCESS;
|
|
}
|
|
|
|
free(path_copy);
|
|
|
|
if (error < GIT_SUCCESS)
|
|
return git__throw(error, "Failed to recursively create `%s` tree structure", path);
|
|
|
|
return GIT_SUCCESS;
|
|
}
|
|
|
|
int git_futils_cmp_path(const char *name1, int len1, int isdir1,
|
|
const char *name2, int len2, int isdir2)
|
|
{
|
|
int len = len1 < len2 ? len1 : len2;
|
|
int cmp;
|
|
|
|
cmp = memcmp(name1, name2, len);
|
|
if (cmp)
|
|
return cmp;
|
|
if (len1 < len2)
|
|
return ((!isdir1 && !isdir2) ? -1 :
|
|
(isdir1 ? '/' - name2[len1] : name2[len1] - '/'));
|
|
if (len1 > len2)
|
|
return ((!isdir1 && !isdir2) ? 1 :
|
|
(isdir2 ? name1[len2] - '/' : '/' - name1[len2]));
|
|
return 0;
|
|
}
|
|
|