mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-28 15:32:35 +00:00
Merge remote-tracking branch 'ethomson/submodule_8dot3'
This commit is contained in:
commit
83ad46f726
35
src/path.c
35
src/path.c
@ -1351,30 +1351,31 @@ static bool verify_dotgit_hfs(const char *path, size_t len)
|
||||
|
||||
GIT_INLINE(bool) verify_dotgit_ntfs(git_repository *repo, const char *path, size_t len)
|
||||
{
|
||||
const char *shortname = NULL;
|
||||
size_t i, start, shortname_len = 0;
|
||||
git_buf *reserved = git_repository__reserved_names_win32;
|
||||
size_t reserved_len = git_repository__reserved_names_win32_len;
|
||||
size_t start = 0, i;
|
||||
|
||||
/* See if the repo has a custom shortname (not "GIT~1") */
|
||||
if (repo &&
|
||||
(shortname = git_repository__8dot3_name(repo)) &&
|
||||
shortname != git_repository__8dot3_default)
|
||||
shortname_len = strlen(shortname);
|
||||
if (repo)
|
||||
git_repository__reserved_names(&reserved, &reserved_len, repo, true);
|
||||
|
||||
if (len >= 4 && strncasecmp(path, ".git", 4) == 0)
|
||||
start = 4;
|
||||
else if (len >= git_repository__8dot3_default_len &&
|
||||
strncasecmp(path, git_repository__8dot3_default, git_repository__8dot3_default_len) == 0)
|
||||
start = git_repository__8dot3_default_len;
|
||||
else if (shortname_len && len >= shortname_len &&
|
||||
strncasecmp(path, shortname, shortname_len) == 0)
|
||||
start = shortname_len;
|
||||
else
|
||||
for (i = 0; i < reserved_len; i++) {
|
||||
git_buf *r = &reserved[i];
|
||||
|
||||
if (len >= r->size &&
|
||||
strncasecmp(path, r->ptr, r->size) == 0) {
|
||||
start = r->size;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!start)
|
||||
return true;
|
||||
|
||||
/* Reject paths beginning with ".git\" */
|
||||
/* Reject paths like ".git\" */
|
||||
if (path[start] == '\\')
|
||||
return false;
|
||||
|
||||
/* Reject paths like '.git ' or '.git.' */
|
||||
for (i = start; i < len; i++) {
|
||||
if (path[i] != ' ' && path[i] != '.')
|
||||
return true;
|
||||
|
153
src/repository.c
153
src/repository.c
@ -38,8 +38,16 @@
|
||||
|
||||
#define GIT_REPO_VERSION 0
|
||||
|
||||
const char *git_repository__8dot3_default = "GIT~1";
|
||||
size_t git_repository__8dot3_default_len = 5;
|
||||
git_buf git_repository__reserved_names_win32[] = {
|
||||
{ DOT_GIT, 0, CONST_STRLEN(DOT_GIT) },
|
||||
{ GIT_DIR_SHORTNAME, 0, CONST_STRLEN(GIT_DIR_SHORTNAME) }
|
||||
};
|
||||
size_t git_repository__reserved_names_win32_len = 2;
|
||||
|
||||
git_buf git_repository__reserved_names_posix[] = {
|
||||
{ DOT_GIT, 0, CONST_STRLEN(DOT_GIT) },
|
||||
};
|
||||
size_t git_repository__reserved_names_posix_len = 1;
|
||||
|
||||
static void set_odb(git_repository *repo, git_odb *odb)
|
||||
{
|
||||
@ -111,6 +119,8 @@ void git_repository__cleanup(git_repository *repo)
|
||||
|
||||
void git_repository_free(git_repository *repo)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (repo == NULL)
|
||||
return;
|
||||
|
||||
@ -121,10 +131,12 @@ void git_repository_free(git_repository *repo)
|
||||
git_diff_driver_registry_free(repo->diff_drivers);
|
||||
repo->diff_drivers = NULL;
|
||||
|
||||
for (i = 0; i < repo->reserved_names.size; i++)
|
||||
git_buf_free(git_array_get(repo->reserved_names, i));
|
||||
|
||||
git__free(repo->path_repository);
|
||||
git__free(repo->workdir);
|
||||
git__free(repo->namespace);
|
||||
git__free(repo->name_8dot3);
|
||||
git__free(repo->ident_name);
|
||||
git__free(repo->ident_email);
|
||||
|
||||
@ -156,18 +168,26 @@ static bool valid_repository_path(git_buf *repository_path)
|
||||
static git_repository *repository_alloc(void)
|
||||
{
|
||||
git_repository *repo = git__calloc(1, sizeof(git_repository));
|
||||
if (!repo)
|
||||
return NULL;
|
||||
|
||||
if (git_cache_init(&repo->objects) < 0) {
|
||||
git__free(repo);
|
||||
return NULL;
|
||||
}
|
||||
if (repo == NULL ||
|
||||
git_cache_init(&repo->objects) < 0)
|
||||
goto on_error;
|
||||
|
||||
git_array_init_to_size(repo->reserved_names, 4);
|
||||
if (!repo->reserved_names.ptr)
|
||||
goto on_error;
|
||||
|
||||
/* set all the entries in the cvar cache to `unset` */
|
||||
git_repository__cvar_cache_clear(repo);
|
||||
|
||||
return repo;
|
||||
|
||||
on_error:
|
||||
if (repo)
|
||||
git_cache_free(&repo->objects);
|
||||
|
||||
git__free(repo);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int git_repository_new(git_repository **out)
|
||||
@ -327,6 +347,7 @@ static int read_gitfile(git_buf *path_out, const char *file_path)
|
||||
static int find_repo(
|
||||
git_buf *repo_path,
|
||||
git_buf *parent_path,
|
||||
git_buf *link_path,
|
||||
const char *start_path,
|
||||
uint32_t flags,
|
||||
const char *ceiling_dirs)
|
||||
@ -369,9 +390,14 @@ static int find_repo(
|
||||
git_buf repo_link = GIT_BUF_INIT;
|
||||
|
||||
if (!(error = read_gitfile(&repo_link, path.ptr))) {
|
||||
if (valid_repository_path(&repo_link))
|
||||
if (valid_repository_path(&repo_link)) {
|
||||
git_buf_swap(repo_path, &repo_link);
|
||||
|
||||
if (link_path)
|
||||
error = git_buf_put(link_path,
|
||||
path.ptr, path.size);
|
||||
}
|
||||
|
||||
git_buf_free(&repo_link);
|
||||
break;
|
||||
}
|
||||
@ -458,13 +484,16 @@ int git_repository_open_ext(
|
||||
const char *ceiling_dirs)
|
||||
{
|
||||
int error;
|
||||
git_buf path = GIT_BUF_INIT, parent = GIT_BUF_INIT;
|
||||
git_buf path = GIT_BUF_INIT, parent = GIT_BUF_INIT,
|
||||
link_path = GIT_BUF_INIT;
|
||||
git_repository *repo;
|
||||
|
||||
if (repo_ptr)
|
||||
*repo_ptr = NULL;
|
||||
|
||||
error = find_repo(&path, &parent, start_path, flags, ceiling_dirs);
|
||||
error = find_repo(
|
||||
&path, &parent, &link_path, start_path, flags, ceiling_dirs);
|
||||
|
||||
if (error < 0 || !repo_ptr)
|
||||
return error;
|
||||
|
||||
@ -474,6 +503,11 @@ int git_repository_open_ext(
|
||||
repo->path_repository = git_buf_detach(&path);
|
||||
GITERR_CHECK_ALLOC(repo->path_repository);
|
||||
|
||||
if (link_path.size) {
|
||||
repo->path_gitlink = git_buf_detach(&link_path);
|
||||
GITERR_CHECK_ALLOC(repo->path_gitlink);
|
||||
}
|
||||
|
||||
if ((flags & GIT_REPOSITORY_OPEN_BARE) != 0)
|
||||
repo->is_bare = 1;
|
||||
else {
|
||||
@ -525,7 +559,7 @@ int git_repository_discover(
|
||||
|
||||
git_buf_sanitize(out);
|
||||
|
||||
return find_repo(out, NULL, start_path, flags, ceiling_dirs);
|
||||
return find_repo(out, NULL, NULL, start_path, flags, ceiling_dirs);
|
||||
}
|
||||
|
||||
static int load_config(
|
||||
@ -810,28 +844,89 @@ const char *git_repository_get_namespace(git_repository *repo)
|
||||
return repo->namespace;
|
||||
}
|
||||
|
||||
const char *git_repository__8dot3_name(git_repository *repo)
|
||||
{
|
||||
if (!repo->has_8dot3) {
|
||||
repo->has_8dot3 = 1;
|
||||
|
||||
#ifdef GIT_WIN32
|
||||
if (!repo->is_bare) {
|
||||
repo->name_8dot3 = git_win32_path_8dot3_name(repo->path_repository);
|
||||
static int reserved_names_add8dot3(git_repository *repo, const char *path)
|
||||
{
|
||||
char *name = git_win32_path_8dot3_name(path);
|
||||
const char *def = GIT_DIR_SHORTNAME;
|
||||
size_t name_len, def_len = CONST_STRLEN(GIT_DIR_SHORTNAME);
|
||||
git_buf *buf;
|
||||
|
||||
/* We anticipate the 8.3 name is "GIT~1", so use a static for
|
||||
* easy testing in the common case */
|
||||
if (repo->name_8dot3 &&
|
||||
strcasecmp(repo->name_8dot3, git_repository__8dot3_default) == 0)
|
||||
repo->has_8dot3_default = 1;
|
||||
}
|
||||
#endif
|
||||
if (!name)
|
||||
return 0;
|
||||
|
||||
name_len = strlen(name);
|
||||
|
||||
if (name_len == def_len && memcmp(name, def, def_len) == 0) {
|
||||
git__free(name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return repo->has_8dot3_default ?
|
||||
git_repository__8dot3_default : repo->name_8dot3;
|
||||
if ((buf = git_array_alloc(repo->reserved_names)) == NULL)
|
||||
return -1;
|
||||
|
||||
git_buf_attach(buf, name, name_len);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool git_repository__reserved_names(
|
||||
git_buf **out, size_t *outlen, git_repository *repo, bool include_ntfs)
|
||||
{
|
||||
GIT_UNUSED(include_ntfs);
|
||||
|
||||
if (repo->reserved_names.size == 0) {
|
||||
git_buf *buf;
|
||||
size_t i;
|
||||
|
||||
/* Add the static defaults */
|
||||
for (i = 0; i < git_repository__reserved_names_win32_len; i++) {
|
||||
if ((buf = git_array_alloc(repo->reserved_names)) == NULL)
|
||||
goto on_error;
|
||||
|
||||
buf->ptr = git_repository__reserved_names_win32[i].ptr;
|
||||
buf->size = git_repository__reserved_names_win32[i].size;
|
||||
}
|
||||
|
||||
/* Try to add any repo-specific reserved names */
|
||||
if (!repo->is_bare) {
|
||||
const char *reserved_path = repo->path_gitlink ?
|
||||
repo->path_gitlink : repo->path_repository;
|
||||
|
||||
if (reserved_names_add8dot3(repo, reserved_path) < 0)
|
||||
goto on_error;
|
||||
}
|
||||
}
|
||||
|
||||
*out = repo->reserved_names.ptr;
|
||||
*outlen = repo->reserved_names.size;
|
||||
|
||||
return true;
|
||||
|
||||
/* Always give good defaults, even on OOM */
|
||||
on_error:
|
||||
*out = git_repository__reserved_names_win32;
|
||||
*outlen = git_repository__reserved_names_win32_len;
|
||||
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
bool git_repository__reserved_names(
|
||||
git_buf **out, size_t *outlen, git_repository *repo, bool include_ntfs)
|
||||
{
|
||||
GIT_UNUSED(repo);
|
||||
|
||||
if (include_ntfs) {
|
||||
*out = git_repository__reserved_names_win32;
|
||||
*outlen = git_repository__reserved_names_win32_len;
|
||||
} else {
|
||||
*out = git_repository__reserved_names_posix;
|
||||
*outlen = git_repository__reserved_names_posix_len;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int check_repositoryformatversion(git_config *config)
|
||||
{
|
||||
int version;
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "git2/object.h"
|
||||
#include "git2/config.h"
|
||||
|
||||
#include "array.h"
|
||||
#include "cache.h"
|
||||
#include "refs.h"
|
||||
#include "buffer.h"
|
||||
@ -27,6 +28,9 @@
|
||||
#define GIT_DIR_MODE 0755
|
||||
#define GIT_BARE_DIR_MODE 0777
|
||||
|
||||
/* Default DOS-compatible 8.3 "short name" for a git repository, "GIT~1" */
|
||||
#define GIT_DIR_SHORTNAME "GIT~1"
|
||||
|
||||
/** Cvar cache identifiers */
|
||||
typedef enum {
|
||||
GIT_CVAR_AUTO_CRLF = 0, /* core.autocrlf */
|
||||
@ -124,16 +128,17 @@ struct git_repository {
|
||||
git_diff_driver_registry *diff_drivers;
|
||||
|
||||
char *path_repository;
|
||||
char *path_gitlink;
|
||||
char *workdir;
|
||||
char *namespace;
|
||||
char *name_8dot3;
|
||||
|
||||
char *ident_name;
|
||||
char *ident_email;
|
||||
|
||||
unsigned is_bare:1,
|
||||
has_8dot3:1,
|
||||
has_8dot3_default:1;
|
||||
git_array_t(git_buf) reserved_names;
|
||||
|
||||
unsigned is_bare:1;
|
||||
|
||||
unsigned int lru_counter;
|
||||
|
||||
git_atomic attr_session_key;
|
||||
@ -188,19 +193,24 @@ int git_repository__set_orig_head(git_repository *repo, const git_oid *orig_head
|
||||
|
||||
int git_repository__cleanup_files(git_repository *repo, const char *files[], size_t files_len);
|
||||
|
||||
/*
|
||||
* Gets the DOS-compatible 8.3 "short name". This will return only the
|
||||
* short name for the repository directory (ie, "git~1" for ".git"). This
|
||||
* will always return a pointer to `git_repository__8dot3_default` when
|
||||
* "GIT~1" is the short name. This will return NULL for bare repositories,
|
||||
* and systems that do not have a short name.
|
||||
*/
|
||||
const char *git_repository__8dot3_name(git_repository *repo);
|
||||
/* The default "reserved names" for a repository */
|
||||
extern git_buf git_repository__reserved_names_win32[];
|
||||
extern size_t git_repository__reserved_names_win32_len;
|
||||
|
||||
/* The default DOS-compatible 8.3 "short name" for a git repository,
|
||||
* "GIT~1".
|
||||
extern git_buf git_repository__reserved_names_posix[];
|
||||
extern size_t git_repository__reserved_names_posix_len;
|
||||
|
||||
/*
|
||||
* Gets any "reserved names" in the repository. This will return paths
|
||||
* that should not be allowed in the repository (like ".git") to avoid
|
||||
* conflicting with the repository path, or with alternate mechanisms to
|
||||
* the repository path (eg, "GIT~1"). Every attempt will be made to look
|
||||
* up all possible reserved names - if there was a conflict for the shortname
|
||||
* GIT~1, for example, this function will try to look up the alternate
|
||||
* shortname. If that fails, this function returns false, but out and outlen
|
||||
* will still be populated with good defaults.
|
||||
*/
|
||||
extern const char *git_repository__8dot3_default;
|
||||
extern size_t git_repository__8dot3_default_len;
|
||||
bool git_repository__reserved_names(
|
||||
git_buf **out, size_t *outlen, git_repository *repo, bool include_ntfs);
|
||||
|
||||
#endif
|
||||
|
108
tests/repo/reservedname.c
Normal file
108
tests/repo/reservedname.c
Normal file
@ -0,0 +1,108 @@
|
||||
#include "clar_libgit2.h"
|
||||
#include "../submodule/submodule_helpers.h"
|
||||
#include "repository.h"
|
||||
|
||||
void test_repo_reservedname__cleanup(void)
|
||||
{
|
||||
cl_git_sandbox_cleanup();
|
||||
}
|
||||
|
||||
void test_repo_reservedname__includes_shortname_on_win32(void)
|
||||
{
|
||||
git_repository *repo;
|
||||
git_buf *reserved;
|
||||
size_t reserved_len;
|
||||
|
||||
repo = cl_git_sandbox_init("nasty");
|
||||
cl_assert(git_repository__reserved_names(&reserved, &reserved_len, repo, false));
|
||||
|
||||
#ifdef GIT_WIN32
|
||||
cl_assert_equal_i(2, reserved_len);
|
||||
cl_assert_equal_s(".git", reserved[0].ptr);
|
||||
cl_assert_equal_s("GIT~1", reserved[1].ptr);
|
||||
#else
|
||||
cl_assert_equal_i(1, reserved_len);
|
||||
cl_assert_equal_s(".git", reserved[0].ptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
void test_repo_reservedname__includes_shortname_when_requested(void)
|
||||
{
|
||||
git_repository *repo;
|
||||
git_buf *reserved;
|
||||
size_t reserved_len;
|
||||
|
||||
repo = cl_git_sandbox_init("nasty");
|
||||
cl_assert(git_repository__reserved_names(&reserved, &reserved_len, repo, true));
|
||||
|
||||
cl_assert_equal_i(2, reserved_len);
|
||||
cl_assert_equal_s(".git", reserved[0].ptr);
|
||||
cl_assert_equal_s("GIT~1", reserved[1].ptr);
|
||||
}
|
||||
|
||||
/* Ensures that custom shortnames are included: creates a GIT~1 so that the
|
||||
* .git folder itself will have to be named GIT~2
|
||||
*/
|
||||
void test_repo_reservedname__custom_shortname_recognized(void)
|
||||
{
|
||||
#ifdef GIT_WIN32
|
||||
git_repository *repo;
|
||||
git_buf *reserved;
|
||||
size_t reserved_len;
|
||||
|
||||
if (!cl_sandbox_supports_8dot3())
|
||||
clar__skip();
|
||||
|
||||
repo = cl_git_sandbox_init("nasty");
|
||||
|
||||
cl_must_pass(p_rename("nasty/.git", "nasty/_temp"));
|
||||
cl_git_write2file("nasty/git~1", "", 0, O_RDWR|O_CREAT, 0666);
|
||||
cl_must_pass(p_rename("nasty/_temp", "nasty/.git"));
|
||||
|
||||
cl_assert(git_repository__reserved_names(&reserved, &reserved_len, repo, true));
|
||||
|
||||
cl_assert_equal_i(3, reserved_len);
|
||||
cl_assert_equal_s(".git", reserved[0].ptr);
|
||||
cl_assert_equal_s("GIT~1", reserved[1].ptr);
|
||||
cl_assert_equal_s("GIT~2", reserved[2].ptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* When looking at the short name for a submodule, we need to prevent
|
||||
* people from overwriting the `.git` file in the submodule working
|
||||
* directory itself. We don't want to look at the actual repository
|
||||
* path, since it will be in the super's repository above us, and
|
||||
* typically named with the name of our subrepository. Consequently,
|
||||
* preventing access to the short name of the actual repository path
|
||||
* would prevent us from creating files with the same name as the
|
||||
* subrepo. (Eg, a submodule named "libgit2" could not contain a file
|
||||
* named "libgit2", which would be unfortunate.)
|
||||
*/
|
||||
void test_repo_reservedname__submodule_pointer(void)
|
||||
{
|
||||
#ifdef GIT_WIN32
|
||||
git_repository *super_repo, *sub_repo;
|
||||
git_submodule *sub;
|
||||
git_buf *sub_reserved;
|
||||
size_t sub_reserved_len;
|
||||
|
||||
if (!cl_sandbox_supports_8dot3())
|
||||
clar__skip();
|
||||
|
||||
super_repo = setup_fixture_submod2();
|
||||
|
||||
assert_submodule_exists(super_repo, "sm_unchanged");
|
||||
|
||||
cl_git_pass(git_submodule_lookup(&sub, super_repo, "sm_unchanged"));
|
||||
cl_git_pass(git_submodule_open(&sub_repo, sub));
|
||||
|
||||
cl_assert(git_repository__reserved_names(&sub_reserved, &sub_reserved_len, sub_repo, true));
|
||||
|
||||
cl_assert_equal_i(2, sub_reserved_len);
|
||||
cl_assert_equal_s(".git", sub_reserved[0].ptr);
|
||||
cl_assert_equal_s("GIT~1", sub_reserved[1].ptr);
|
||||
|
||||
git_submodule_free(sub);
|
||||
git_repository_free(sub_repo);
|
||||
#endif
|
||||
}
|
Loading…
Reference in New Issue
Block a user