]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Sync with 2.40.2
authorJohannes Schindelin <johannes.schindelin@gmx.de>
Wed, 17 Apr 2024 09:38:18 +0000 (11:38 +0200)
committerJohannes Schindelin <johannes.schindelin@gmx.de>
Fri, 19 Apr 2024 10:38:42 +0000 (12:38 +0200)
* maint-2.40: (39 commits)
  Git 2.40.2
  Git 2.39.4
  fsck: warn about symlink pointing inside a gitdir
  core.hooksPath: add some protection while cloning
  init.templateDir: consider this config setting protected
  clone: prevent hooks from running during a clone
  Add a helper function to compare file contents
  init: refactor the template directory discovery into its own function
  find_hook(): refactor the `STRIP_EXTENSION` logic
  clone: when symbolic links collide with directories, keep the latter
  entry: report more colliding paths
  t5510: verify that D/F confusion cannot lead to an RCE
  submodule: require the submodule path to contain directories only
  clone_submodule: avoid using `access()` on directories
  submodules: submodule paths must not contain symlinks
  clone: prevent clashing git dirs when cloning submodule in parallel
  t7423: add tests for symlinked submodule directories
  has_dir_name(): do not get confused by characters < '/'
  docs: document security issues around untrusted .git dirs
  upload-pack: disable lazy-fetching by default
  ...

28 files changed:
1  2 
Documentation/git.txt
builtin/clone.c
builtin/init-db.c
builtin/submodule--helper.c
builtin/upload-pack.c
config.c
copy.c
copy.h
dir.c
dir.h
entry.c
fsck.c
fsck.h
hook.c
http.c
path.c
promisor-remote.c
read-cache.c
remote-curl.c
repository.c
setup.c
setup.h
submodule.c
t/helper/test-path-utils.c
t/t0060-path-utils.sh
t/t1450-fsck.sh
t/t5510-fetch.sh
t/test-lib.sh

Simple merge
diff --cc builtin/clone.c
index 15f9912b4cae0583ac62d387120fcaa24a58ff23,5fa29014007a3a3a1078b60b0f3a2401b5f939e2..d6545d036f8301ef034b78bf2776c4595d8bcf15
@@@ -929,7 -938,8 +958,9 @@@ int cmd_clone(int argc, const char **ar
        int err = 0, complete_refs_before_fetch = 1;
        int submodule_progress;
        int filter_submodules = 0;
 +      int hash_algo;
+       const char *template_dir;
+       char *template_dir_dup = NULL;
  
        struct transport_ls_refs_options transport_ls_refs_options =
                TRANSPORT_LS_REFS_OPTIONS_INIT;
index aef40361052ed9ff363661f19533dc466f300d34,a101e7f94c119b5b1eb5da9ea359e5eb34bb37ab..848f7ec70ff64dc335d965fc66ae86a60b2751a9
  #include "refs.h"
  #include "builtin.h"
  #include "exec-cmd.h"
 +#include "object-file.h"
  #include "parse-options.h"
 +#include "path.h"
 +#include "setup.h"
  #include "worktree.h"
 +#include "wrapper.h"
  
- #ifndef DEFAULT_GIT_TEMPLATE_DIR
- #define DEFAULT_GIT_TEMPLATE_DIR "/usr/share/git-core/templates"
- #endif
  #ifdef NO_TRUSTABLE_FILEMODE
  #define TEST_FILEMODE 0
  #else
Simple merge
Simple merge
diff --cc config.c
Simple merge
diff --cc copy.c
index 882c79cffb0d38434dde66a307b32b460440f875,8492f6fc8319af3d7e1fe69b6d541fe086b93902..1b4069cfea47b6b23ead6cdc26af850f3f95a9bb
--- 1/copy.c
--- 2/copy.c
+++ b/copy.c
@@@ -1,7 -1,4 +1,10 @@@
 -#include "cache.h"
 +#include "git-compat-util.h"
 +#include "copy.h"
 +#include "path.h"
 +#include "wrapper.h"
++#include "gettext.h"
++#include "strbuf.h"
++#include "abspath.h"
  
  int copy_fd(int ifd, int ofd)
  {
diff --cc copy.h
index 2af77cba8649b322151fa1b410e6a9522690cde9,0000000000000000000000000000000000000000..057259a3a7a726252d922f6a42161773e52f980a
mode 100644,000000..100644
--- 1/copy.h
--- /dev/null
+++ b/copy.h
@@@ -1,10 -1,0 +1,24 @@@
 +#ifndef COPY_H
 +#define COPY_H
 +
 +#define COPY_READ_ERROR (-2)
 +#define COPY_WRITE_ERROR (-3)
 +int copy_fd(int ifd, int ofd);
 +int copy_file(const char *dst, const char *src, int mode);
 +int copy_file_with_time(const char *dst, const char *src, int mode);
 +
++/*
++ * Compare the file mode and contents of two given files.
++ *
++ * If both files are actually symbolic links, the function returns 1 if the link
++ * targets are identical or 0 if they are not.
++ *
++ * If any of the two files cannot be accessed or in case of read failures, this
++ * function returns 0.
++ *
++ * If the file modes and contents are identical, the function returns 1,
++ * otherwise it returns 0.
++ */
++int do_files_match(const char *path1, const char *path2);
++
 +#endif /* COPY_H */
diff --cc dir.c
Simple merge
diff --cc dir.h
Simple merge
diff --cc entry.c
Simple merge
diff --cc fsck.c
Simple merge
diff --cc fsck.h
Simple merge
diff --cc hook.c
index 3ca5e60895d65f01a87ebd2e055e5fe2dc41436c,655e2fe44eca57e01a3dadfe20b8322603f175ce..22a976c6d302fee3d8e81974c2a3075db50b917e
--- 1/hook.c
--- 2/hook.c
+++ b/hook.c
@@@ -1,11 -1,31 +1,38 @@@
 -#include "cache.h"
 +#include "git-compat-util.h"
 +#include "advice.h"
 +#include "gettext.h"
  #include "hook.h"
 +#include "path.h"
  #include "run-command.h"
  #include "config.h"
 +#include "strbuf.h"
++#include "environment.h"
++#include "setup.h"
++#include "copy.h"
+ static int identical_to_template_hook(const char *name, const char *path)
+ {
+       const char *env = getenv("GIT_CLONE_TEMPLATE_DIR");
+       const char *template_dir = get_template_dir(env && *env ? env : NULL);
+       struct strbuf template_path = STRBUF_INIT;
+       int found_template_hook, ret;
+       strbuf_addf(&template_path, "%s/hooks/%s", template_dir, name);
+       found_template_hook = access(template_path.buf, X_OK) >= 0;
+ #ifdef STRIP_EXTENSION
+       if (!found_template_hook) {
+               strbuf_addstr(&template_path, STRIP_EXTENSION);
+               found_template_hook = access(template_path.buf, X_OK) >= 0;
+       }
+ #endif
+       if (!found_template_hook)
+               return 0;
+       ret = do_files_match(template_path.buf, path);
+       strbuf_release(&template_path);
+       return ret;
+ }
  
  const char *find_hook(const char *name)
  {
diff --cc http.c
Simple merge
diff --cc path.c
Simple merge
Simple merge
diff --cc read-cache.c
Simple merge
diff --cc remote-curl.c
index acf7b2bb40ac4edc07e98934a51c4d69711bc6cb,322cd0f9f4a32b3adc09c7fee718dbf1f36f0c5c..a0777e3f7a41527b4e8aa9ca9fbf48fe6d1dfe9e
@@@ -1,9 -1,6 +1,10 @@@
 -#include "cache.h"
 +#include "git-compat-util.h"
+ #include "git-curl-compat.h"
 +#include "alloc.h"
  #include "config.h"
 +#include "environment.h"
 +#include "gettext.h"
 +#include "hex.h"
  #include "remote.h"
  #include "connect.h"
  #include "strbuf.h"
diff --cc repository.c
Simple merge
diff --cc setup.c
index 458582207ea7bd4aa328b94d531a98f413c7ca14,c3301f5ab824b5c7d8d12c835dd2819368c6ffd0..84324e35c6967f8ae41ff62d5766f1864f57b7be
+++ b/setup.c
@@@ -11,8 -6,7 +11,9 @@@
  #include "chdir-notify.h"
  #include "promisor-remote.h"
  #include "quote.h"
 +#include "trace2.h"
 +#include "wrapper.h"
+ #include "exec-cmd.h"
  
  static int inside_git_dir = -1;
  static int inside_work_tree = -1;
diff --cc setup.h
index 4c1ca9d0c94b88060033de50294da4d2e242bf15,0000000000000000000000000000000000000000..e7708f573904729eae7b00b3e3c3ea5ccb8ac7a0
mode 100644,000000..100644
--- /dev/null
+++ b/setup.h
@@@ -1,168 -1,0 +1,182 @@@
 +#ifndef SETUP_H
 +#define SETUP_H
 +
 +#include "string-list.h"
 +
 +int is_inside_git_dir(void);
 +int is_inside_work_tree(void);
 +int get_common_dir_noenv(struct strbuf *sb, const char *gitdir);
 +int get_common_dir(struct strbuf *sb, const char *gitdir);
 +
 +/*
 + * Return true if the given path is a git directory; note that this _just_
 + * looks at the directory itself. If you want to know whether "foo/.git"
 + * is a repository, you must feed that path, not just "foo".
 + */
 +int is_git_directory(const char *path);
 +
 +/*
 + * Return 1 if the given path is the root of a git repository or
 + * submodule, else 0. Will not return 1 for bare repositories with the
 + * exception of creating a bare repository in "foo/.git" and calling
 + * is_git_repository("foo").
 + *
 + * If we run into read errors, we err on the side of saying "yes, it is",
 + * as we usually consider sub-repos precious, and would prefer to err on the
 + * side of not disrupting or deleting them.
 + */
 +int is_nonbare_repository_dir(struct strbuf *path);
 +
 +#define READ_GITFILE_ERR_STAT_FAILED 1
 +#define READ_GITFILE_ERR_NOT_A_FILE 2
 +#define READ_GITFILE_ERR_OPEN_FAILED 3
 +#define READ_GITFILE_ERR_READ_FAILED 4
 +#define READ_GITFILE_ERR_INVALID_FORMAT 5
 +#define READ_GITFILE_ERR_NO_PATH 6
 +#define READ_GITFILE_ERR_NOT_A_REPO 7
 +#define READ_GITFILE_ERR_TOO_LARGE 8
 +void read_gitfile_error_die(int error_code, const char *path, const char *dir);
 +const char *read_gitfile_gently(const char *path, int *return_error_code);
 +#define read_gitfile(path) read_gitfile_gently((path), NULL)
 +const char *resolve_gitdir_gently(const char *suspect, int *return_error_code);
 +#define resolve_gitdir(path) resolve_gitdir_gently((path), NULL)
 +
++/*
++ * Check if a repository is safe and die if it is not, by verifying the
++ * ownership of the worktree (if any), the git directory, and the gitfile (if
++ * any).
++ *
++ * Exemptions for known-safe repositories can be added via `safe.directory`
++ * config settings; for non-bare repositories, their worktree needs to be
++ * added, for bare ones their git directory.
++ */
++void die_upon_dubious_ownership(const char *gitfile, const char *worktree,
++                              const char *gitdir);
++
 +void setup_work_tree(void);
 +/*
 + * Find the commondir and gitdir of the repository that contains the current
 + * working directory, without changing the working directory or other global
 + * state. The result is appended to commondir and gitdir.  If the discovered
 + * gitdir does not correspond to a worktree, then 'commondir' and 'gitdir' will
 + * both have the same result appended to the buffer.  The return value is
 + * either 0 upon success and non-zero if no repository was found.
 + */
 +int discover_git_directory(struct strbuf *commondir,
 +                         struct strbuf *gitdir);
 +const char *setup_git_directory_gently(int *);
 +const char *setup_git_directory(void);
 +char *prefix_path(const char *prefix, int len, const char *path);
 +char *prefix_path_gently(const char *prefix, int len, int *remaining, const char *path);
 +
 +int check_filename(const char *prefix, const char *name);
 +void verify_filename(const char *prefix,
 +                   const char *name,
 +                   int diagnose_misspelt_rev);
 +void verify_non_filename(const char *prefix, const char *name);
 +int path_inside_repo(const char *prefix, const char *path);
 +
 +void sanitize_stdfds(void);
 +int daemonize(void);
 +
 +/*
 + * GIT_REPO_VERSION is the version we write by default. The
 + * _READ variant is the highest number we know how to
 + * handle.
 + */
 +#define GIT_REPO_VERSION 0
 +#define GIT_REPO_VERSION_READ 1
 +
 +/*
 + * You _have_ to initialize a `struct repository_format` using
 + * `= REPOSITORY_FORMAT_INIT` before calling `read_repository_format()`.
 + */
 +struct repository_format {
 +      int version;
 +      int precious_objects;
 +      char *partial_clone; /* value of extensions.partialclone */
 +      int worktree_config;
 +      int is_bare;
 +      int hash_algo;
 +      int sparse_index;
 +      char *work_tree;
 +      struct string_list unknown_extensions;
 +      struct string_list v1_only_extensions;
 +};
 +
 +/*
 + * Always use this to initialize a `struct repository_format`
 + * to a well-defined, default state before calling
 + * `read_repository()`.
 + */
 +#define REPOSITORY_FORMAT_INIT \
 +{ \
 +      .version = -1, \
 +      .is_bare = -1, \
 +      .hash_algo = GIT_HASH_SHA1, \
 +      .unknown_extensions = STRING_LIST_INIT_DUP, \
 +      .v1_only_extensions = STRING_LIST_INIT_DUP, \
 +}
 +
 +/*
 + * Read the repository format characteristics from the config file "path" into
 + * "format" struct. Returns the numeric version. On error, or if no version is
 + * found in the configuration, -1 is returned, format->version is set to -1,
 + * and all other fields in the struct are set to the default configuration
 + * (REPOSITORY_FORMAT_INIT). Always initialize the struct using
 + * REPOSITORY_FORMAT_INIT before calling this function.
 + */
 +int read_repository_format(struct repository_format *format, const char *path);
 +
 +/*
 + * Free the memory held onto by `format`, but not the struct itself.
 + * (No need to use this after `read_repository_format()` fails.)
 + */
 +void clear_repository_format(struct repository_format *format);
 +
 +/*
 + * Verify that the repository described by repository_format is something we
 + * can read. If it is, return 0. Otherwise, return -1, and "err" will describe
 + * any errors encountered.
 + */
 +int verify_repository_format(const struct repository_format *format,
 +                           struct strbuf *err);
 +
 +/*
 + * Check the repository format version in the path found in get_git_dir(),
 + * and die if it is a version we don't understand. Generally one would
 + * set_git_dir() before calling this, and use it only for "are we in a valid
 + * repo?".
 + *
 + * If successful and fmt is not NULL, fill fmt with data.
 + */
 +void check_repository_format(struct repository_format *fmt);
 +
++const char *get_template_dir(const char *option_template);
++
 +/*
 + * NOTE NOTE NOTE!!
 + *
 + * PERM_UMASK, OLD_PERM_GROUP and OLD_PERM_EVERYBODY enumerations must
 + * not be changed. Old repositories have core.sharedrepository written in
 + * numeric format, and therefore these values are preserved for compatibility
 + * reasons.
 + */
 +enum sharedrepo {
 +      PERM_UMASK          = 0,
 +      OLD_PERM_GROUP      = 1,
 +      OLD_PERM_EVERYBODY  = 2,
 +      PERM_GROUP          = 0660,
 +      PERM_EVERYBODY      = 0664
 +};
 +int git_config_perm(const char *var, const char *value);
 +
 +struct startup_info {
 +      int have_repository;
 +      const char *prefix;
 +      const char *original_cwd;
 +};
 +extern struct startup_info *startup_info;
 +extern const char *tmp_original_cwd;
 +
 +#endif /* SETUP_H */
diff --cc submodule.c
Simple merge
index 2ef53d5f7a27badae6dac4baaa64829bea3f85dd,0e0de2180767d936a450e1abbec5c388930585ce..164b6a6832ae1d8057a5a58a5a23f62e59f57314
@@@ -1,12 -1,7 +1,13 @@@
  #include "test-tool.h"
  #include "cache.h"
 +#include "abspath.h"
 +#include "environment.h"
 +#include "path.h"
 +#include "setup.h"
  #include "string-list.h"
 +#include "trace.h"
  #include "utf8.h"
++#include "copy.h"
  
  /*
   * A "string_list_each_func_t" function that normalizes an entry from
Simple merge
diff --cc t/t1450-fsck.sh
index 8c442adb1ae8296a75e04e5a6b5b79482ec6ca35,6277ce6b20b2e0a3edbe6215ee274c7d06f02842..21d65b207b15f0f4cde0823fc04932539e9ee276
@@@ -1020,34 -1023,41 +1020,71 @@@ test_expect_success 'fsck error on gita
        test_cmp expected actual
  '
  
 +test_expect_success 'fsck detects problems in worktree index' '
 +      test_when_finished "git worktree remove -f wt" &&
 +      git worktree add wt &&
 +
 +      echo "this will be removed to break the worktree index" >wt/file &&
 +      git -C wt add file &&
 +      blob=$(git -C wt rev-parse :file) &&
 +      remove_object $blob &&
 +
 +      test_must_fail git fsck --name-objects >actual 2>&1 &&
 +      cat >expect <<-EOF &&
 +      missing blob $blob (.git/worktrees/wt/index:file)
 +      EOF
 +      test_cmp expect actual
 +'
 +
 +test_expect_success 'fsck reports problems in main index without filename' '
 +      test_when_finished "rm -f .git/index && git read-tree HEAD" &&
 +      echo "this object will be removed to break the main index" >file &&
 +      git add file &&
 +      blob=$(git rev-parse :file) &&
 +      remove_object $blob &&
 +
 +      test_must_fail git fsck --name-objects >actual 2>&1 &&
 +      cat >expect <<-EOF &&
 +      missing blob $blob (:file)
 +      EOF
 +      test_cmp expect actual
 +'
 +
+ test_expect_success 'fsck warning on symlink target with excessive length' '
+       symlink_target=$(printf "pattern %032769d" 1 | git hash-object -w --stdin) &&
+       test_when_finished "remove_object $symlink_target" &&
+       tree=$(printf "120000 blob %s\t%s\n" $symlink_target symlink | git mktree) &&
+       test_when_finished "remove_object $tree" &&
+       cat >expected <<-EOF &&
+       warning in blob $symlink_target: symlinkTargetLength: symlink target too long
+       EOF
+       git fsck --no-dangling >actual 2>&1 &&
+       test_cmp expected actual
+ '
+ test_expect_success 'fsck warning on symlink target pointing inside git dir' '
+       gitdir=$(printf ".git" | git hash-object -w --stdin) &&
+       ntfs_gitdir=$(printf "GIT~1" | git hash-object -w --stdin) &&
+       hfs_gitdir=$(printf ".${u200c}git" | git hash-object -w --stdin) &&
+       inside_gitdir=$(printf "nested/.git/config" | git hash-object -w --stdin) &&
+       benign_target=$(printf "legit/config" | git hash-object -w --stdin) &&
+       tree=$(printf "120000 blob %s\t%s\n" \
+               $benign_target benign_target \
+               $gitdir gitdir \
+               $hfs_gitdir hfs_gitdir \
+               $inside_gitdir inside_gitdir \
+               $ntfs_gitdir ntfs_gitdir |
+               git mktree) &&
+       for o in $gitdir $ntfs_gitdir $hfs_gitdir $inside_gitdir $benign_target $tree
+       do
+               test_when_finished "remove_object $o" || return 1
+       done &&
+       printf "warning in blob %s: symlinkPointsToGitDir: symlink target points to git dir\n" \
+               $gitdir $hfs_gitdir $inside_gitdir $ntfs_gitdir |
+       sort >expected &&
+       git fsck --no-dangling >actual 2>&1 &&
+       sort actual >actual.sorted &&
+       test_cmp expected actual.sorted
+ '
  test_done
Simple merge
diff --cc t/test-lib.sh
Simple merge