colors (at most two, one for foreground and one for background)
and attributes (as many as you want), separated by spaces.
+
-The basic colors accepted are `normal`, `black`, `red`, `green`, `yellow`,
-`blue`, `magenta`, `cyan` and `white`. The first color given is the
-foreground; the second is the background. All the basic colors except
-`normal` have a bright variant that can be specified by prefixing the
-color with `bright`, like `brightred`.
+The basic colors accepted are `normal`, `black`, `red`, `green`,
+`yellow`, `blue`, `magenta`, `cyan`, `white` and `default`. The first
+color given is the foreground; the second is the background. All the
+basic colors except `normal` and `default` have a bright variant that can
+be specified by prefixing the color with `bright`, like `brightred`.
++
+The color `normal` makes no change to the color. It is the same as an
+empty string, but can be used as the foreground color when specifying a
+background color alone (for example, "normal red").
++
+The color `default` explicitly resets the color to the terminal default,
+for example to specify a cleared background. Although it varies between
+terminals, this is usually not the same as setting to "white black".
+
Colors may also be given as numbers between 0 and 255; these use ANSI
256-color mode (but note that not all terminals may support this). If
be turned off by prefixing them with `no` or `no-` (e.g., `noreverse`,
`no-ul`, etc).
+
+The pseudo-attribute `reset` resets all colors and attributes before
+applying the specified coloring. For example, `reset green` will result
+in a green foreground and default background without any active
+attributes.
++
An empty color string produces no color effect at all. This can be used
to avoid coloring specific elements without disabling color entirely.
+
include::config/reset.txt[]
+ include::config/safe.txt[]
+
include::config/sendemail.txt[]
include::config/sequencer.txt[]
#include "../git-compat-util.h"
#include "win32.h"
+ #include <aclapi.h>
#include <conio.h>
#include <wchar.h>
#include "../strbuf.h"
#include "win32/lazyload.h"
#include "../config.h"
#include "dir.h"
+#define SECURITY_WIN32
+#include <sspi.h>
#define HCAST(type, handle) ((type)(intptr_t)handle)
/* a pointer to the original strftime in case we can't find the UCRT version */
static size_t (*fallback)(char *, size_t, const char *, const struct tm *) = strftime;
size_t ret;
- DECLARE_PROC_ADDR(ucrtbase.dll, size_t, strftime, char *, size_t,
+ DECLARE_PROC_ADDR(ucrtbase.dll, size_t, __cdecl, strftime, char *, size_t,
const char *, const struct tm *);
if (INIT_PROC_ADDR(strftime))
}
if (!ret || ret >= ARRAY_SIZE(wpointer))
return NULL;
+ if (GetFileAttributesW(wpointer) == INVALID_FILE_ATTRIBUTES) {
+ errno = ENOENT;
+ return NULL;
+ }
if (xwcstoutf(pointer, wpointer, len) < 0)
return NULL;
convert_slashes(pointer);
static char *get_extended_user_info(enum EXTENDED_NAME_FORMAT type)
{
- DECLARE_PROC_ADDR(secur32.dll, BOOL, GetUserNameExW,
+ DECLARE_PROC_ADDR(secur32.dll, BOOL, SEC_ENTRY, GetUserNameExW,
enum EXTENDED_NAME_FORMAT, LPCWSTR, PULONG);
static wchar_t wbuffer[1024];
DWORD len;
}
}
+ static PSID get_current_user_sid(void)
+ {
+ HANDLE token;
+ DWORD len = 0;
+ PSID result = NULL;
+
+ if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token))
+ return NULL;
+
+ if (!GetTokenInformation(token, TokenUser, NULL, 0, &len)) {
+ TOKEN_USER *info = xmalloc((size_t)len);
+ if (GetTokenInformation(token, TokenUser, info, len, &len)) {
+ len = GetLengthSid(info->User.Sid);
+ result = xmalloc(len);
+ if (!CopySid(len, result, info->User.Sid)) {
+ error(_("failed to copy SID (%ld)"),
+ GetLastError());
+ FREE_AND_NULL(result);
+ }
+ }
+ FREE_AND_NULL(info);
+ }
+ CloseHandle(token);
+
+ return result;
+ }
+
+ int is_path_owned_by_current_sid(const char *path)
+ {
+ WCHAR wpath[MAX_PATH];
+ PSID sid = NULL;
+ PSECURITY_DESCRIPTOR descriptor = NULL;
+ DWORD err;
+
+ static wchar_t home[MAX_PATH];
+
+ int result = 0;
+
+ if (xutftowcs_path(wpath, path) < 0)
+ return 0;
+
+ /*
+ * On Windows, the home directory is owned by the administrator, but for
+ * all practical purposes, it belongs to the user. Do pretend that it is
+ * owned by the user.
+ */
+ if (!*home) {
+ DWORD size = ARRAY_SIZE(home);
+ DWORD len = GetEnvironmentVariableW(L"HOME", home, size);
+ if (!len || len > size)
+ wcscpy(home, L"::N/A::");
+ }
+ if (!wcsicmp(wpath, home))
+ return 1;
+
+ /* Get the owner SID */
+ err = GetNamedSecurityInfoW(wpath, SE_FILE_OBJECT,
+ OWNER_SECURITY_INFORMATION |
+ DACL_SECURITY_INFORMATION,
+ &sid, NULL, NULL, NULL, &descriptor);
+
+ if (err != ERROR_SUCCESS)
+ error(_("failed to get owner for '%s' (%ld)"), path, err);
+ else if (sid && IsValidSid(sid)) {
+ /* Now, verify that the SID matches the current user's */
+ static PSID current_user_sid;
+
+ if (!current_user_sid)
+ current_user_sid = get_current_user_sid();
+
+ if (current_user_sid &&
+ IsValidSid(current_user_sid) &&
+ EqualSid(sid, current_user_sid))
+ result = 1;
+ }
+
+ /*
+ * We can release the security descriptor struct only now because `sid`
+ * actually points into this struct.
+ */
+ if (descriptor)
+ LocalFree(descriptor);
+
+ return result;
+ }
+
int is_valid_win32_path(const char *path, int allow_literal_nul)
{
const char *p = path;
#ifndef GIT_COMPAT_UTIL_H
#define GIT_COMPAT_UTIL_H
+#if __STDC_VERSION__ - 0 < 199901L
+/*
+ * Git is in a testing period for mandatory C99 support in the compiler. If
+ * your compiler is reasonably recent, you can try to enable C99 support (or,
+ * for MSVC, C11 support). If you encounter a problem and can't enable C99
+ * support with your compiler (such as with "-std=gnu99") and don't have access
+ * to one with this support, such as GCC or Clang, you can remove this #if
+ * directive, but please report the details of your system to
+ * git@vger.kernel.org.
+ */
+#error "Required C99 support is in a test phase. Please see git-compat-util.h for more details."
+#endif
+
#ifdef USE_MSVC_CRTDBG
/*
* For these to work they must appear very early in each
/*
* See if our compiler is known to support flexible array members.
*/
-#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && (!defined(__SUNPRO_C) || (__SUNPRO_C > 0x580))
-# define FLEX_ARRAY /* empty */
+
+/*
+ * Check vendor specific quirks first, before checking the
+ * __STDC_VERSION__, as vendor compilers can lie and we need to be
+ * able to work them around. Note that by not defining FLEX_ARRAY
+ * here, we can fall back to use the "safer but a bit wasteful" one
+ * later.
+ */
+#if defined(__SUNPRO_C) && (__SUNPRO_C <= 0x580)
#elif defined(__GNUC__)
# if (__GNUC__ >= 3)
# define FLEX_ARRAY /* empty */
# else
# define FLEX_ARRAY 0 /* older GNU extension */
# endif
+#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
+# define FLEX_ARRAY /* empty */
#endif
/*
#define unsigned_mult_overflows(a, b) \
((a) && (b) > maximum_unsigned_value_of_type(a) / (a))
+/*
+ * Returns true if the left shift of "a" by "shift" bits will
+ * overflow. The type of "a" must be unsigned.
+ */
+#define unsigned_left_shift_overflows(a, shift) \
+ ((shift) < bitsizeof(a) && \
+ (a) > maximum_unsigned_value_of_type(a) >> (shift))
+
#ifdef __GNUC__
#define TYPEOF(x) (__typeof__(x))
#else
#define is_valid_path(path) 1
#endif
+ #ifndef is_path_owned_by_current_user
+ static inline int is_path_owned_by_current_uid(const char *path)
+ {
+ struct stat st;
+ if (lstat(path, &st))
+ return 0;
+ return st.st_uid == geteuid();
+ }
+
+ #define is_path_owned_by_current_user is_path_owned_by_current_uid
+ #endif
+
#ifndef find_last_dir_sep
static inline char *git_find_last_dir_sep(const char *path)
{
struct strbuf;
/* General helper functions */
-void vreportf(const char *prefix, const char *err, va_list params);
NORETURN void usage(const char *err);
NORETURN void usagef(const char *err, ...) __attribute__((format (printf, 1, 2)));
NORETURN void die(const char *err, ...) __attribute__((format (printf, 1, 2)));
NORETURN void die_errno(const char *err, ...) __attribute__((format (printf, 1, 2)));
+int die_message(const char *err, ...) __attribute__((format (printf, 1, 2)));
+int die_message_errno(const char *err, ...) __attribute__((format (printf, 1, 2)));
int error(const char *err, ...) __attribute__((format (printf, 1, 2)));
int error_errno(const char *err, ...) __attribute__((format (printf, 1, 2)));
void warning(const char *err, ...) __attribute__((format (printf, 1, 2)));
typedef void (*report_fn)(const char *, va_list params);
void set_die_routine(NORETURN_PTR report_fn routine);
+report_fn get_die_message_routine(void);
void set_error_routine(report_fn routine);
report_fn get_error_routine(void);
void set_warn_routine(report_fn routine);
#ifdef NO_UNSETENV
#define unsetenv gitunsetenv
-void gitunsetenv(const char *);
+int gitunsetenv(const char *);
#endif
#ifdef NO_STRCASESTR
return a - b;
}
+static inline size_t st_left_shift(size_t a, unsigned shift)
+{
+ if (unsigned_left_shift_overflows(a, shift))
+ die("size_t overflow: %"PRIuMAX" << %u",
+ (uintmax_t)a, shift);
+ return a << shift;
+}
+
+static inline unsigned long cast_size_t_to_ulong(size_t a)
+{
+ if (a != (unsigned long)a)
+ die("object too large to read on this platform: %"
+ PRIuMAX" is cut off to %lu",
+ (uintmax_t)a, (unsigned long)a);
+ return (unsigned long)a;
+}
+
#ifdef HAVE_ALLOCA_H
# include <alloca.h>
# define xalloca(size) (alloca(size))
#include "string-list.h"
#include "chdir-notify.h"
#include "promisor-remote.h"
+ #include "quote.h"
static int inside_git_dir = -1;
static int inside_work_tree = -1;
static struct startup_info the_startup_info;
struct startup_info *startup_info = &the_startup_info;
+const char *tmp_original_cwd;
/*
* The input parameter must contain an absolute path, and it must already be
initialized = 1;
}
+static void setup_original_cwd(void)
+{
+ struct strbuf tmp = STRBUF_INIT;
+ const char *worktree = NULL;
+ int offset = -1;
+
+ if (!tmp_original_cwd)
+ return;
+
+ /*
+ * startup_info->original_cwd points to the current working
+ * directory we inherited from our parent process, which is a
+ * directory we want to avoid removing.
+ *
+ * For convience, we would like to have the path relative to the
+ * worktree instead of an absolute path.
+ *
+ * Yes, startup_info->original_cwd is usually the same as 'prefix',
+ * but differs in two ways:
+ * - prefix has a trailing '/'
+ * - if the user passes '-C' to git, that modifies the prefix but
+ * not startup_info->original_cwd.
+ */
+
+ /* Normalize the directory */
+ strbuf_realpath(&tmp, tmp_original_cwd, 1);
+ free((char*)tmp_original_cwd);
+ tmp_original_cwd = NULL;
+ startup_info->original_cwd = strbuf_detach(&tmp, NULL);
+
+ /*
+ * Get our worktree; we only protect the current working directory
+ * if it's in the worktree.
+ */
+ worktree = get_git_work_tree();
+ if (!worktree)
+ goto no_prevention_needed;
+
+ offset = dir_inside_of(startup_info->original_cwd, worktree);
+ if (offset >= 0) {
+ /*
+ * If startup_info->original_cwd == worktree, that is already
+ * protected and we don't need original_cwd as a secondary
+ * protection measure.
+ */
+ if (!*(startup_info->original_cwd + offset))
+ goto no_prevention_needed;
+
+ /*
+ * original_cwd was inside worktree; precompose it just as
+ * we do prefix so that built up paths will match
+ */
+ startup_info->original_cwd = \
+ precompose_string_if_needed(startup_info->original_cwd
+ + offset);
+ return;
+ }
+
+no_prevention_needed:
+ free((char*)startup_info->original_cwd);
+ startup_info->original_cwd = NULL;
+}
+
static int read_worktree_config(const char *var, const char *value, void *vdata)
{
struct repository_format *data = vdata;
}
}
+ struct safe_directory_data {
+ const char *path;
+ int is_safe;
+ };
+
+ static int safe_directory_cb(const char *key, const char *value, void *d)
+ {
+ struct safe_directory_data *data = d;
+
+ if (!value || !*value)
+ data->is_safe = 0;
+ else {
+ const char *interpolated = NULL;
+
+ if (!git_config_pathname(&interpolated, key, value) &&
+ !fspathcmp(data->path, interpolated ? interpolated : value))
+ data->is_safe = 1;
+
+ free((char *)interpolated);
+ }
+
+ return 0;
+ }
+
+ static int ensure_valid_ownership(const char *path)
+ {
+ struct safe_directory_data data = { .path = path };
+
+ if (is_path_owned_by_current_user(path))
+ return 1;
+
+ read_very_early_config(safe_directory_cb, &data);
+
+ return data.is_safe;
+ }
+
enum discovery_result {
GIT_DIR_NONE = 0,
GIT_DIR_EXPLICIT,
/* these are errors */
GIT_DIR_HIT_CEILING = -1,
GIT_DIR_HIT_MOUNT_POINT = -2,
- GIT_DIR_INVALID_GITFILE = -3
+ GIT_DIR_INVALID_GITFILE = -3,
+ GIT_DIR_INVALID_OWNERSHIP = -4
};
/*
}
strbuf_setlen(dir, offset);
if (gitdirenv) {
+ if (!ensure_valid_ownership(dir->buf))
+ return GIT_DIR_INVALID_OWNERSHIP;
strbuf_addstr(gitdir, gitdirenv);
return GIT_DIR_DISCOVERED;
}
if (is_git_directory(dir->buf)) {
+ if (!ensure_valid_ownership(dir->buf))
+ return GIT_DIR_INVALID_OWNERSHIP;
strbuf_addstr(gitdir, ".");
return GIT_DIR_BARE;
}
dir.buf);
*nongit_ok = 1;
break;
+ case GIT_DIR_INVALID_OWNERSHIP:
+ if (!nongit_ok) {
+ struct strbuf quoted = STRBUF_INIT;
+
+ sq_quote_buf_pretty("ed, dir.buf);
+ die(_("unsafe repository ('%s' is owned by someone else)\n"
+ "To add an exception for this directory, call:\n"
+ "\n"
+ "\tgit config --global --add safe.directory %s"),
+ dir.buf, quoted.buf);
+ }
+ *nongit_ok = 1;
+ break;
case GIT_DIR_NONE:
/*
* As a safeguard against setup_git_directory_gently_1 returning
setenv(GIT_PREFIX_ENVIRONMENT, "", 1);
}
+ setup_original_cwd();
strbuf_release(&dir);
strbuf_release(&gitdir);
ancestor() {
# We do some math with the expected ancestor length.
expected=$3
- if test -n "$rootoff" && test "x$expected" != x-1; then
- expected=$(($expected-$rootslash))
- test $expected -lt 0 ||
- expected=$(($expected+$rootoff))
- fi
- test_expect_success "longest ancestor: $1 $2 => $expected" \
+ case "$rootoff,$expected,$2" in
+ *,*,//*) ;; # leave UNC paths alone
+ [0-9]*,[0-9]*,/*)
+ # On Windows, expect MSYS2 pseudo root translation for
+ # Unix-style absolute paths
+ expected=$(($expected-$rootslash+$rootoff))
+ ;;
+ esac
+ test_expect_success $4 "longest ancestor: $1 $2 => $expected" \
"actual=\$(test-tool path-utils longest_ancestor_length '$1' '$2') &&
test \"\$actual\" = '$expected'"
}
ancestor /foo/bar /foo:/bar 4
ancestor /foo/bar /bar -1
+ # Windows-specific: DOS drives, network shares
+ ancestor C:/Users/me C:/ 2 MINGW
+ ancestor D:/Users/me C:/ -1 MINGW
+ ancestor //server/share/my-directory //server/share/ 14 MINGW
+
test_expect_success 'strip_path_suffix' '
test c:/msysgit = $(test-tool path-utils strip_path_suffix \
c:/msysgit/libexec//git-core libexec/git-core)
mkdir second &&
ln -s ../first second/other &&
mkdir third &&
- dir="$(cd .git; pwd -P)" &&
+ dir="$(cd .git && pwd -P)" &&
dir2=third/../second/other/.git &&
test "$dir" = "$(test-tool path-utils real_path $dir2)" &&
file="$dir"/index &&
basename=blub &&
test "$dir/$basename" = "$(cd .git && test-tool path-utils real_path "$basename")" &&
ln -s ../first/file .git/syml &&
- sym="$(cd first; pwd -P)"/file &&
+ sym="$(cd first && pwd -P)"/file &&
test "$sym" = "$(test-tool path-utils real_path "$dir2/syml")"
'