Commit 83ad46f7 by Carlos Martín Nieto

Merge remote-tracking branch 'ethomson/submodule_8dot3'

parents 4c2e6b1e 4196dd8e
...@@ -1351,30 +1351,31 @@ static bool verify_dotgit_hfs(const char *path, size_t len) ...@@ -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) GIT_INLINE(bool) verify_dotgit_ntfs(git_repository *repo, const char *path, size_t len)
{ {
const char *shortname = NULL; git_buf *reserved = git_repository__reserved_names_win32;
size_t i, start, shortname_len = 0; 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 && if (repo)
(shortname = git_repository__8dot3_name(repo)) && git_repository__reserved_names(&reserved, &reserved_len, repo, true);
shortname != git_repository__8dot3_default)
shortname_len = strlen(shortname); for (i = 0; i < reserved_len; i++) {
git_buf *r = &reserved[i];
if (len >= 4 && strncasecmp(path, ".git", 4) == 0)
start = 4; if (len >= r->size &&
else if (len >= git_repository__8dot3_default_len && strncasecmp(path, r->ptr, r->size) == 0) {
strncasecmp(path, git_repository__8dot3_default, git_repository__8dot3_default_len) == 0) start = r->size;
start = git_repository__8dot3_default_len; break;
else if (shortname_len && len >= shortname_len && }
strncasecmp(path, shortname, shortname_len) == 0) }
start = shortname_len;
else if (!start)
return true; return true;
/* Reject paths beginning with ".git\" */ /* Reject paths like ".git\" */
if (path[start] == '\\') if (path[start] == '\\')
return false; return false;
/* Reject paths like '.git ' or '.git.' */
for (i = start; i < len; i++) { for (i = start; i < len; i++) {
if (path[i] != ' ' && path[i] != '.') if (path[i] != ' ' && path[i] != '.')
return true; return true;
......
...@@ -38,8 +38,16 @@ ...@@ -38,8 +38,16 @@
#define GIT_REPO_VERSION 0 #define GIT_REPO_VERSION 0
const char *git_repository__8dot3_default = "GIT~1"; git_buf git_repository__reserved_names_win32[] = {
size_t git_repository__8dot3_default_len = 5; { 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) static void set_odb(git_repository *repo, git_odb *odb)
{ {
...@@ -111,6 +119,8 @@ void git_repository__cleanup(git_repository *repo) ...@@ -111,6 +119,8 @@ void git_repository__cleanup(git_repository *repo)
void git_repository_free(git_repository *repo) void git_repository_free(git_repository *repo)
{ {
size_t i;
if (repo == NULL) if (repo == NULL)
return; return;
...@@ -121,10 +131,12 @@ void git_repository_free(git_repository *repo) ...@@ -121,10 +131,12 @@ void git_repository_free(git_repository *repo)
git_diff_driver_registry_free(repo->diff_drivers); git_diff_driver_registry_free(repo->diff_drivers);
repo->diff_drivers = NULL; 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->path_repository);
git__free(repo->workdir); git__free(repo->workdir);
git__free(repo->namespace); git__free(repo->namespace);
git__free(repo->name_8dot3);
git__free(repo->ident_name); git__free(repo->ident_name);
git__free(repo->ident_email); git__free(repo->ident_email);
...@@ -156,18 +168,26 @@ static bool valid_repository_path(git_buf *repository_path) ...@@ -156,18 +168,26 @@ static bool valid_repository_path(git_buf *repository_path)
static git_repository *repository_alloc(void) static git_repository *repository_alloc(void)
{ {
git_repository *repo = git__calloc(1, sizeof(git_repository)); git_repository *repo = git__calloc(1, sizeof(git_repository));
if (!repo)
return NULL;
if (git_cache_init(&repo->objects) < 0) { if (repo == NULL ||
git__free(repo); git_cache_init(&repo->objects) < 0)
return NULL; 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` */ /* set all the entries in the cvar cache to `unset` */
git_repository__cvar_cache_clear(repo); git_repository__cvar_cache_clear(repo);
return repo; return repo;
on_error:
if (repo)
git_cache_free(&repo->objects);
git__free(repo);
return NULL;
} }
int git_repository_new(git_repository **out) int git_repository_new(git_repository **out)
...@@ -327,6 +347,7 @@ static int read_gitfile(git_buf *path_out, const char *file_path) ...@@ -327,6 +347,7 @@ static int read_gitfile(git_buf *path_out, const char *file_path)
static int find_repo( static int find_repo(
git_buf *repo_path, git_buf *repo_path,
git_buf *parent_path, git_buf *parent_path,
git_buf *link_path,
const char *start_path, const char *start_path,
uint32_t flags, uint32_t flags,
const char *ceiling_dirs) const char *ceiling_dirs)
...@@ -369,9 +390,14 @@ static int find_repo( ...@@ -369,9 +390,14 @@ static int find_repo(
git_buf repo_link = GIT_BUF_INIT; git_buf repo_link = GIT_BUF_INIT;
if (!(error = read_gitfile(&repo_link, path.ptr))) { 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); 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); git_buf_free(&repo_link);
break; break;
} }
...@@ -458,13 +484,16 @@ int git_repository_open_ext( ...@@ -458,13 +484,16 @@ int git_repository_open_ext(
const char *ceiling_dirs) const char *ceiling_dirs)
{ {
int error; 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; git_repository *repo;
if (repo_ptr) if (repo_ptr)
*repo_ptr = NULL; *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) if (error < 0 || !repo_ptr)
return error; return error;
...@@ -474,6 +503,11 @@ int git_repository_open_ext( ...@@ -474,6 +503,11 @@ int git_repository_open_ext(
repo->path_repository = git_buf_detach(&path); repo->path_repository = git_buf_detach(&path);
GITERR_CHECK_ALLOC(repo->path_repository); 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) if ((flags & GIT_REPOSITORY_OPEN_BARE) != 0)
repo->is_bare = 1; repo->is_bare = 1;
else { else {
...@@ -525,7 +559,7 @@ int git_repository_discover( ...@@ -525,7 +559,7 @@ int git_repository_discover(
git_buf_sanitize(out); 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( static int load_config(
...@@ -810,27 +844,88 @@ const char *git_repository_get_namespace(git_repository *repo) ...@@ -810,27 +844,88 @@ const char *git_repository_get_namespace(git_repository *repo)
return repo->namespace; return repo->namespace;
} }
const char *git_repository__8dot3_name(git_repository *repo) #ifdef GIT_WIN32
static int reserved_names_add8dot3(git_repository *repo, const char *path)
{ {
if (!repo->has_8dot3) { char *name = git_win32_path_8dot3_name(path);
repo->has_8dot3 = 1; const char *def = GIT_DIR_SHORTNAME;
size_t name_len, def_len = CONST_STRLEN(GIT_DIR_SHORTNAME);
git_buf *buf;
#ifdef GIT_WIN32 if (!name)
return 0;
name_len = strlen(name);
if (name_len == def_len && memcmp(name, def, def_len) == 0) {
git__free(name);
return 0;
}
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) { if (!repo->is_bare) {
repo->name_8dot3 = git_win32_path_8dot3_name(repo->path_repository); const char *reserved_path = repo->path_gitlink ?
repo->path_gitlink : repo->path_repository;
/* We anticipate the 8.3 name is "GIT~1", so use a static for if (reserved_names_add8dot3(repo, reserved_path) < 0)
* easy testing in the common case */ goto on_error;
if (repo->name_8dot3 &&
strcasecmp(repo->name_8dot3, git_repository__8dot3_default) == 0)
repo->has_8dot3_default = 1;
} }
#endif
} }
return repo->has_8dot3_default ? *out = repo->reserved_names.ptr;
git_repository__8dot3_default : repo->name_8dot3; *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) static int check_repositoryformatversion(git_config *config)
{ {
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "git2/object.h" #include "git2/object.h"
#include "git2/config.h" #include "git2/config.h"
#include "array.h"
#include "cache.h" #include "cache.h"
#include "refs.h" #include "refs.h"
#include "buffer.h" #include "buffer.h"
...@@ -27,6 +28,9 @@ ...@@ -27,6 +28,9 @@
#define GIT_DIR_MODE 0755 #define GIT_DIR_MODE 0755
#define GIT_BARE_DIR_MODE 0777 #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 */ /** Cvar cache identifiers */
typedef enum { typedef enum {
GIT_CVAR_AUTO_CRLF = 0, /* core.autocrlf */ GIT_CVAR_AUTO_CRLF = 0, /* core.autocrlf */
...@@ -124,16 +128,17 @@ struct git_repository { ...@@ -124,16 +128,17 @@ struct git_repository {
git_diff_driver_registry *diff_drivers; git_diff_driver_registry *diff_drivers;
char *path_repository; char *path_repository;
char *path_gitlink;
char *workdir; char *workdir;
char *namespace; char *namespace;
char *name_8dot3;
char *ident_name; char *ident_name;
char *ident_email; char *ident_email;
unsigned is_bare:1, git_array_t(git_buf) reserved_names;
has_8dot3:1,
has_8dot3_default:1; unsigned is_bare:1;
unsigned int lru_counter; unsigned int lru_counter;
git_atomic attr_session_key; git_atomic attr_session_key;
...@@ -188,19 +193,24 @@ int git_repository__set_orig_head(git_repository *repo, const git_oid *orig_head ...@@ -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); int git_repository__cleanup_files(git_repository *repo, const char *files[], size_t files_len);
/* /* The default "reserved names" for a repository */
* Gets the DOS-compatible 8.3 "short name". This will return only the extern git_buf git_repository__reserved_names_win32[];
* short name for the repository directory (ie, "git~1" for ".git"). This extern size_t git_repository__reserved_names_win32_len;
* will always return a pointer to `git_repository__8dot3_default` when
* "GIT~1" is the short name. This will return NULL for bare repositories, extern git_buf git_repository__reserved_names_posix[];
* and systems that do not have a short name. extern size_t git_repository__reserved_names_posix_len;
*/
const char *git_repository__8dot3_name(git_repository *repo);
/* The default DOS-compatible 8.3 "short name" for a git repository, /*
* "GIT~1". * 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; bool git_repository__reserved_names(
extern size_t git_repository__8dot3_default_len; git_buf **out, size_t *outlen, git_repository *repo, bool include_ntfs);
#endif #endif
#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
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment