[PATCH 00/25] worktree lock, move, remove and unlock

classic Classic list List threaded Threaded
35 messages Options
12
Reply | Threaded
Open this post in threaded view
|

[PATCH 00/25] worktree lock, move, remove and unlock

Duy Nguyen
This is basically a resend from last time, which happened during rc
time. It adds 4 more commands, basically cleaning up the "TODO" list
in git-worktree.txt.

So far I've only actually used move and remove (and maybe unlock once
because worktree-add failed on me and I had to unlock it manually).
And I don't get to move worktrees a lot either so not really extensive
testing.

  [01/25] usage.c: move format processing out of die_errno()
  [02/25] usage.c: add sys_error() that prints strerror() automatically
  [03/25] copy.c: import copy_file() from busybox
  [04/25] copy.c: delete unused code in copy_file()
  [05/25] copy.c: convert bb_(p)error_msg to (sys_)error
  [06/25] copy.c: style fix
  [07/25] copy.c: convert copy_file() to copy_dir_recursively()
  [08/25] completion: support git-worktree
  [09/25] git-worktree.txt: keep subcommand listing in alphabetical order
  [10/25] path.c: add git_common_path() and strbuf_git_common_path()
  [11/25] worktree.c: use is_dot_or_dotdot()
  [12/25] worktree.c: store "id" instead of "git_dir"
  [13/25] worktree.c: add clear_worktree()
  [14/25] worktree.c: add find_worktree_by_path()
  [15/25] worktree.c: add is_main_worktree()
  [16/25] worktree.c: add validate_worktree()
  [17/25] worktree.c: add update_worktree_location()
  [18/25] worktree.c: add is_worktree_locked()
  [19/25] worktree: avoid 0{40}, too many zeroes, hard to read
  [20/25] worktree: simplify prefixing paths
  [21/25] worktree: add "lock" command
  [22/25] worktree: add "unlock" command
  [23/25] worktree: add "move" commmand
  [24/25] worktree move: accept destination as directory
  [25/25] worktree: add "remove" command

Total 11 files changed, 1028 insertions(+), 48 deletions(-)

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Reply | Threaded
Open this post in threaded view
|

[PATCH 01/25] usage.c: move format processing out of die_errno()

Duy Nguyen
Signed-off-by: Nguyễn Thái Ngọc Duy <[hidden email]>
---
 usage.c | 25 +++++++++++++++----------
 1 file changed, 15 insertions(+), 10 deletions(-)

diff --git a/usage.c b/usage.c
index 82ff131..0dba0c5 100644
--- a/usage.c
+++ b/usage.c
@@ -109,19 +109,12 @@ void NORETURN die(const char *err, ...)
  va_end(params);
 }
 
-void NORETURN die_errno(const char *fmt, ...)
+static const char *fmt_with_err(const char *fmt)
 {
- va_list params;
- char fmt_with_err[1024];
+ static char fmt_with_err[1024];
  char str_error[256], *err;
  int i, j;
 
- if (die_is_recursing()) {
- fputs("fatal: recursion detected in die_errno handler\n",
- stderr);
- exit(128);
- }
-
  err = strerror(errno);
  for (i = j = 0; err[i] && j < sizeof(str_error) - 1; ) {
  if ((str_error[j++] = err[i++]) != '%')
@@ -137,9 +130,21 @@ void NORETURN die_errno(const char *fmt, ...)
  }
  str_error[j] = 0;
  snprintf(fmt_with_err, sizeof(fmt_with_err), "%s: %s", fmt, str_error);
+ return fmt_with_err;
+}
+
+void NORETURN die_errno(const char *fmt, ...)
+{
+ va_list params;
+
+ if (die_is_recursing()) {
+ fputs("fatal: recursion detected in die_errno handler\n",
+ stderr);
+ exit(128);
+ }
 
  va_start(params, fmt);
- die_routine(fmt_with_err, params);
+ die_routine(fmt_with_err(fmt), params);
  va_end(params);
 }
 
--
2.8.0.rc0.210.gd302cd2

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Reply | Threaded
Open this post in threaded view
|

[PATCH 02/25] usage.c: add sys_error() that prints strerror() automatically

Duy Nguyen
In reply to this post by Duy Nguyen
Signed-off-by: Nguyễn Thái Ngọc Duy <[hidden email]>
---
 git-compat-util.h |  1 +
 usage.c           | 10 ++++++++++
 2 files changed, 11 insertions(+)

diff --git a/git-compat-util.h b/git-compat-util.h
index 4743954..e8e7765 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -412,6 +412,7 @@ extern NORETURN void usagef(const char *err, ...) __attribute__((format (printf,
 extern NORETURN void die(const char *err, ...) __attribute__((format (printf, 1, 2)));
 extern NORETURN void die_errno(const char *err, ...) __attribute__((format (printf, 1, 2)));
 extern int error(const char *err, ...) __attribute__((format (printf, 1, 2)));
+extern int sys_error(const char *err, ...) __attribute__((format (printf, 1, 2)));
 extern void warning(const char *err, ...) __attribute__((format (printf, 1, 2)));
 
 #ifndef NO_OPENSSL
diff --git a/usage.c b/usage.c
index 0dba0c5..e7c37f2 100644
--- a/usage.c
+++ b/usage.c
@@ -148,6 +148,16 @@ void NORETURN die_errno(const char *fmt, ...)
  va_end(params);
 }
 
+int sys_error(const char *fmt, ...)
+{
+ va_list params;
+
+ va_start(params, fmt);
+ error_routine(fmt_with_err(fmt), params);
+ va_end(params);
+ return -1;
+}
+
 #undef error
 int error(const char *err, ...)
 {
--
2.8.0.rc0.210.gd302cd2

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Reply | Threaded
Open this post in threaded view
|

[PATCH 03/25] copy.c: import copy_file() from busybox

Duy Nguyen
In reply to this post by Duy Nguyen
This is busybox's unmodified copy_file() in libbb/copy_file.c from the
GPL2+ commit f2c043acfcf9dad9fd3d65821b81f89986bbe54e (busybox: fix
uninitialized memory when displaying IPv6 addresses - 2016-01-18)

Signed-off-by: Nguyễn Thái Ngọc Duy <[hidden email]>
---
 copy.c | 331 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 331 insertions(+)

diff --git a/copy.c b/copy.c
index 574fa1f..29e9d5b 100644
--- a/copy.c
+++ b/copy.c
@@ -65,3 +65,334 @@ int copy_file_with_time(const char *dst, const char *src, int mode)
  return copy_times(dst, src);
  return status;
 }
+
+#if 0
+/* Return:
+ * -1 error, copy not made
+ *  0 copy is made or user answered "no" in interactive mode
+ *    (failures to preserve mode/owner/times are not reported in exit code)
+ */
+int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
+{
+ /* This is a recursive function, try to minimize stack usage */
+ /* NB: each struct stat is ~100 bytes */
+ struct stat source_stat;
+ struct stat dest_stat;
+ smallint retval = 0;
+ smallint dest_exists = 0;
+ smallint ovr;
+
+/* Inverse of cp -d ("cp without -d") */
+#define FLAGS_DEREF (flags & (FILEUTILS_DEREFERENCE + FILEUTILS_DEREFERENCE_L0))
+
+ if ((FLAGS_DEREF ? stat : lstat)(source, &source_stat) < 0) {
+ /* This may be a dangling symlink.
+ * Making [sym]links to dangling symlinks works, so... */
+ if (flags & (FILEUTILS_MAKE_SOFTLINK|FILEUTILS_MAKE_HARDLINK))
+ goto make_links;
+ bb_perror_msg("can't stat '%s'", source);
+ return -1;
+ }
+
+ if (lstat(dest, &dest_stat) < 0) {
+ if (errno != ENOENT) {
+ bb_perror_msg("can't stat '%s'", dest);
+ return -1;
+ }
+ } else {
+ if (source_stat.st_dev == dest_stat.st_dev
+ && source_stat.st_ino == dest_stat.st_ino
+ ) {
+ bb_error_msg("'%s' and '%s' are the same file", source, dest);
+ return -1;
+ }
+ dest_exists = 1;
+ }
+
+#if ENABLE_SELINUX
+ if ((flags & FILEUTILS_PRESERVE_SECURITY_CONTEXT) && is_selinux_enabled() > 0) {
+ security_context_t con;
+ if (lgetfilecon(source, &con) >= 0) {
+ if (setfscreatecon(con) < 0) {
+ bb_perror_msg("can't set setfscreatecon %s", con);
+ freecon(con);
+ return -1;
+ }
+ } else if (errno == ENOTSUP || errno == ENODATA) {
+ setfscreatecon_or_die(NULL);
+ } else {
+ bb_perror_msg("can't lgetfilecon %s", source);
+ return -1;
+ }
+ }
+#endif
+
+ if (S_ISDIR(source_stat.st_mode)) {
+ DIR *dp;
+ const char *tp;
+ struct dirent *d;
+ mode_t saved_umask = 0;
+
+ if (!(flags & FILEUTILS_RECUR)) {
+ bb_error_msg("omitting directory '%s'", source);
+ return -1;
+ }
+
+ /* Did we ever create source ourself before? */
+ tp = is_in_ino_dev_hashtable(&source_stat);
+ if (tp) {
+ /* We did! it's a recursion! man the lifeboats... */
+ bb_error_msg("recursion detected, omitting directory '%s'",
+ source);
+ return -1;
+ }
+
+ if (dest_exists) {
+ if (!S_ISDIR(dest_stat.st_mode)) {
+ bb_error_msg("target '%s' is not a directory", dest);
+ return -1;
+ }
+ /* race here: user can substitute a symlink between
+ * this check and actual creation of files inside dest */
+ } else {
+ /* Create DEST */
+ mode_t mode;
+ saved_umask = umask(0);
+
+ mode = source_stat.st_mode;
+ if (!(flags & FILEUTILS_PRESERVE_STATUS))
+ mode = source_stat.st_mode & ~saved_umask;
+ /* Allow owner to access new dir (at least for now) */
+ mode |= S_IRWXU;
+ if (mkdir(dest, mode) < 0) {
+ umask(saved_umask);
+ bb_perror_msg("can't create directory '%s'", dest);
+ return -1;
+ }
+ umask(saved_umask);
+ /* need stat info for add_to_ino_dev_hashtable */
+ if (lstat(dest, &dest_stat) < 0) {
+ bb_perror_msg("can't stat '%s'", dest);
+ return -1;
+ }
+ }
+ /* remember (dev,inode) of each created dir.
+ * NULL: name is not remembered */
+ add_to_ino_dev_hashtable(&dest_stat, NULL);
+
+ /* Recursively copy files in SOURCE */
+ dp = opendir(source);
+ if (dp == NULL) {
+ retval = -1;
+ goto preserve_mode_ugid_time;
+ }
+
+ while ((d = readdir(dp)) != NULL) {
+ char *new_source, *new_dest;
+
+ new_source = concat_subpath_file(source, d->d_name);
+ if (new_source == NULL)
+ continue;
+ new_dest = concat_path_file(dest, d->d_name);
+ if (copy_file(new_source, new_dest, flags & ~FILEUTILS_DEREFERENCE_L0) < 0)
+ retval = -1;
+ free(new_source);
+ free(new_dest);
+ }
+ closedir(dp);
+
+ if (!dest_exists
+ && chmod(dest, source_stat.st_mode & ~saved_umask) < 0
+ ) {
+ bb_perror_msg("can't preserve %s of '%s'", "permissions", dest);
+ /* retval = -1; - WRONG! copy *WAS* made */
+ }
+ goto preserve_mode_ugid_time;
+ }
+
+ if (flags & (FILEUTILS_MAKE_SOFTLINK|FILEUTILS_MAKE_HARDLINK)) {
+ int (*lf)(const char *oldpath, const char *newpath);
+ make_links:
+ /* Hmm... maybe
+ * if (DEREF && MAKE_SOFTLINK) source = realpath(source) ?
+ * (but realpath returns NULL on dangling symlinks...) */
+ lf = (flags & FILEUTILS_MAKE_SOFTLINK) ? symlink : link;
+ if (lf(source, dest) < 0) {
+ ovr = ask_and_unlink(dest, flags);
+ if (ovr <= 0)
+ return ovr;
+ if (lf(source, dest) < 0) {
+ bb_perror_msg("can't create link '%s'", dest);
+ return -1;
+ }
+ }
+ /* _Not_ jumping to preserve_mode_ugid_time:
+ * (sym)links don't have those */
+ return 0;
+ }
+
+ if (/* "cp thing1 thing2" without -R: just open and read() from thing1 */
+    !(flags & FILEUTILS_RECUR)
+    /* "cp [-opts] regular_file thing2" */
+ || S_ISREG(source_stat.st_mode)
+ /* DEREF uses stat, which never returns S_ISLNK() == true.
+  * So the below is never true: */
+ /* || (FLAGS_DEREF && S_ISLNK(source_stat.st_mode)) */
+ ) {
+ int src_fd;
+ int dst_fd;
+ mode_t new_mode;
+
+ if (!FLAGS_DEREF && S_ISLNK(source_stat.st_mode)) {
+ /* "cp -d symlink dst": create a link */
+ goto dont_cat;
+ }
+
+ if (ENABLE_FEATURE_PRESERVE_HARDLINKS && !FLAGS_DEREF) {
+ const char *link_target;
+ link_target = is_in_ino_dev_hashtable(&source_stat);
+ if (link_target) {
+ if (link(link_target, dest) < 0) {
+ ovr = ask_and_unlink(dest, flags);
+ if (ovr <= 0)
+ return ovr;
+ if (link(link_target, dest) < 0) {
+ bb_perror_msg("can't create link '%s'", dest);
+ return -1;
+ }
+ }
+ return 0;
+ }
+ add_to_ino_dev_hashtable(&source_stat, dest);
+ }
+
+ src_fd = open_or_warn(source, O_RDONLY);
+ if (src_fd < 0)
+ return -1;
+
+ /* Do not try to open with weird mode fields */
+ new_mode = source_stat.st_mode;
+ if (!S_ISREG(source_stat.st_mode))
+ new_mode = 0666;
+
+ // POSIX way is a security problem versus (sym)link attacks
+ if (!ENABLE_FEATURE_NON_POSIX_CP) {
+ dst_fd = open(dest, O_WRONLY|O_CREAT|O_TRUNC, new_mode);
+ } else { /* safe way: */
+ dst_fd = open(dest, O_WRONLY|O_CREAT|O_EXCL, new_mode);
+ }
+ if (dst_fd == -1) {
+ ovr = ask_and_unlink(dest, flags);
+ if (ovr <= 0) {
+ close(src_fd);
+ return ovr;
+ }
+ /* It shouldn't exist. If it exists, do not open (symlink attack?) */
+ dst_fd = open3_or_warn(dest, O_WRONLY|O_CREAT|O_EXCL, new_mode);
+ if (dst_fd < 0) {
+ close(src_fd);
+ return -1;
+ }
+ }
+
+#if ENABLE_SELINUX
+ if ((flags & (FILEUTILS_PRESERVE_SECURITY_CONTEXT|FILEUTILS_SET_SECURITY_CONTEXT))
+ && is_selinux_enabled() > 0
+ ) {
+ security_context_t con;
+ if (getfscreatecon(&con) == -1) {
+ bb_perror_msg("getfscreatecon");
+ return -1;
+ }
+ if (con) {
+ if (setfilecon(dest, con) == -1) {
+ bb_perror_msg("setfilecon:%s,%s", dest, con);
+ freecon(con);
+ return -1;
+ }
+ freecon(con);
+ }
+ }
+#endif
+ if (bb_copyfd_eof(src_fd, dst_fd) == -1)
+ retval = -1;
+ /* Careful with writing... */
+ if (close(dst_fd) < 0) {
+ bb_perror_msg("error writing to '%s'", dest);
+ retval = -1;
+ }
+ /* ...but read size is already checked by bb_copyfd_eof */
+ close(src_fd);
+ /* "cp /dev/something new_file" should not
+ * copy mode of /dev/something */
+ if (!S_ISREG(source_stat.st_mode))
+ return retval;
+ goto preserve_mode_ugid_time;
+ }
+ dont_cat:
+
+ /* Source is a symlink or a special file */
+ /* We are lazy here, a bit lax with races... */
+ if (dest_exists) {
+ errno = EEXIST;
+ ovr = ask_and_unlink(dest, flags);
+ if (ovr <= 0)
+ return ovr;
+ }
+ if (S_ISLNK(source_stat.st_mode)) {
+ char *lpath = xmalloc_readlink_or_warn(source);
+ if (lpath) {
+ int r = symlink(lpath, dest);
+ free(lpath);
+ if (r < 0) {
+ bb_perror_msg("can't create symlink '%s'", dest);
+ return -1;
+ }
+ if (flags & FILEUTILS_PRESERVE_STATUS)
+ if (lchown(dest, source_stat.st_uid, source_stat.st_gid) < 0)
+ bb_perror_msg("can't preserve %s of '%s'", "ownership", dest);
+ }
+ /* _Not_ jumping to preserve_mode_ugid_time:
+ * symlinks don't have those */
+ return 0;
+ }
+ if (S_ISBLK(source_stat.st_mode) || S_ISCHR(source_stat.st_mode)
+ || S_ISSOCK(source_stat.st_mode) || S_ISFIFO(source_stat.st_mode)
+ ) {
+ if (mknod(dest, source_stat.st_mode, source_stat.st_rdev) < 0) {
+ bb_perror_msg("can't create '%s'", dest);
+ return -1;
+ }
+ } else {
+ bb_error_msg("unrecognized file '%s' with mode %x", source, source_stat.st_mode);
+ return -1;
+ }
+
+ preserve_mode_ugid_time:
+
+ if (flags & FILEUTILS_PRESERVE_STATUS
+ /* Cannot happen: */
+ /* && !(flags & (FILEUTILS_MAKE_SOFTLINK|FILEUTILS_MAKE_HARDLINK)) */
+ ) {
+ struct timeval times[2];
+
+ times[1].tv_sec = times[0].tv_sec = source_stat.st_mtime;
+ times[1].tv_usec = times[0].tv_usec = 0;
+ /* BTW, utimes sets usec-precision time - just FYI */
+ if (utimes(dest, times) < 0)
+ bb_perror_msg("can't preserve %s of '%s'", "times", dest);
+ if (chown(dest, source_stat.st_uid, source_stat.st_gid) < 0) {
+ source_stat.st_mode &= ~(S_ISUID | S_ISGID);
+ bb_perror_msg("can't preserve %s of '%s'", "ownership", dest);
+ }
+ if (chmod(dest, source_stat.st_mode) < 0)
+ bb_perror_msg("can't preserve %s of '%s'", "permissions", dest);
+ }
+
+ if (flags & FILEUTILS_VERBOSE) {
+ printf("'%s' -> '%s'\n", source, dest);
+ }
+
+ return retval;
+}
+#endif
--
2.8.0.rc0.210.gd302cd2

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Reply | Threaded
Open this post in threaded view
|

[PATCH 04/25] copy.c: delete unused code in copy_file()

Duy Nguyen
In reply to this post by Duy Nguyen
 - selinux preservation code
 - make-link code
 - delete link dereference code
 - non-recursive copy code
 - stat no preservation code
 - verbose printing code

Signed-off-by: Nguyễn Thái Ngọc Duy <[hidden email]>
---
 copy.c | 101 +++++------------------------------------------------------------
 1 file changed, 7 insertions(+), 94 deletions(-)

diff --git a/copy.c b/copy.c
index 29e9d5b..d24a8ae 100644
--- a/copy.c
+++ b/copy.c
@@ -82,14 +82,7 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
  smallint dest_exists = 0;
  smallint ovr;
 
-/* Inverse of cp -d ("cp without -d") */
-#define FLAGS_DEREF (flags & (FILEUTILS_DEREFERENCE + FILEUTILS_DEREFERENCE_L0))
-
- if ((FLAGS_DEREF ? stat : lstat)(source, &source_stat) < 0) {
- /* This may be a dangling symlink.
- * Making [sym]links to dangling symlinks works, so... */
- if (flags & (FILEUTILS_MAKE_SOFTLINK|FILEUTILS_MAKE_HARDLINK))
- goto make_links;
+ if (lstat(source, &source_stat) < 0) {
  bb_perror_msg("can't stat '%s'", source);
  return -1;
  }
@@ -109,35 +102,12 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
  dest_exists = 1;
  }
 
-#if ENABLE_SELINUX
- if ((flags & FILEUTILS_PRESERVE_SECURITY_CONTEXT) && is_selinux_enabled() > 0) {
- security_context_t con;
- if (lgetfilecon(source, &con) >= 0) {
- if (setfscreatecon(con) < 0) {
- bb_perror_msg("can't set setfscreatecon %s", con);
- freecon(con);
- return -1;
- }
- } else if (errno == ENOTSUP || errno == ENODATA) {
- setfscreatecon_or_die(NULL);
- } else {
- bb_perror_msg("can't lgetfilecon %s", source);
- return -1;
- }
- }
-#endif
-
  if (S_ISDIR(source_stat.st_mode)) {
  DIR *dp;
  const char *tp;
  struct dirent *d;
  mode_t saved_umask = 0;
 
- if (!(flags & FILEUTILS_RECUR)) {
- bb_error_msg("omitting directory '%s'", source);
- return -1;
- }
-
  /* Did we ever create source ourself before? */
  tp = is_in_ino_dev_hashtable(&source_stat);
  if (tp) {
@@ -160,8 +130,6 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
  saved_umask = umask(0);
 
  mode = source_stat.st_mode;
- if (!(flags & FILEUTILS_PRESERVE_STATUS))
- mode = source_stat.st_mode & ~saved_umask;
  /* Allow owner to access new dir (at least for now) */
  mode |= S_IRWXU;
  if (mkdir(dest, mode) < 0) {
@@ -210,45 +178,17 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
  goto preserve_mode_ugid_time;
  }
 
- if (flags & (FILEUTILS_MAKE_SOFTLINK|FILEUTILS_MAKE_HARDLINK)) {
- int (*lf)(const char *oldpath, const char *newpath);
- make_links:
- /* Hmm... maybe
- * if (DEREF && MAKE_SOFTLINK) source = realpath(source) ?
- * (but realpath returns NULL on dangling symlinks...) */
- lf = (flags & FILEUTILS_MAKE_SOFTLINK) ? symlink : link;
- if (lf(source, dest) < 0) {
- ovr = ask_and_unlink(dest, flags);
- if (ovr <= 0)
- return ovr;
- if (lf(source, dest) < 0) {
- bb_perror_msg("can't create link '%s'", dest);
- return -1;
- }
- }
- /* _Not_ jumping to preserve_mode_ugid_time:
- * (sym)links don't have those */
- return 0;
- }
-
- if (/* "cp thing1 thing2" without -R: just open and read() from thing1 */
-    !(flags & FILEUTILS_RECUR)
-    /* "cp [-opts] regular_file thing2" */
- || S_ISREG(source_stat.st_mode)
- /* DEREF uses stat, which never returns S_ISLNK() == true.
-  * So the below is never true: */
- /* || (FLAGS_DEREF && S_ISLNK(source_stat.st_mode)) */
- ) {
+ if (S_ISREG(source_stat.st_mode) ) { /* "cp [-opts] regular_file thing2" */
  int src_fd;
  int dst_fd;
  mode_t new_mode;
 
- if (!FLAGS_DEREF && S_ISLNK(source_stat.st_mode)) {
+ if (S_ISLNK(source_stat.st_mode)) {
  /* "cp -d symlink dst": create a link */
  goto dont_cat;
  }
 
- if (ENABLE_FEATURE_PRESERVE_HARDLINKS && !FLAGS_DEREF) {
+ if (ENABLE_FEATURE_PRESERVE_HARDLINKS) {
  const char *link_target;
  link_target = is_in_ino_dev_hashtable(&source_stat);
  if (link_target) {
@@ -295,25 +235,6 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
  }
  }
 
-#if ENABLE_SELINUX
- if ((flags & (FILEUTILS_PRESERVE_SECURITY_CONTEXT|FILEUTILS_SET_SECURITY_CONTEXT))
- && is_selinux_enabled() > 0
- ) {
- security_context_t con;
- if (getfscreatecon(&con) == -1) {
- bb_perror_msg("getfscreatecon");
- return -1;
- }
- if (con) {
- if (setfilecon(dest, con) == -1) {
- bb_perror_msg("setfilecon:%s,%s", dest, con);
- freecon(con);
- return -1;
- }
- freecon(con);
- }
- }
-#endif
  if (bb_copyfd_eof(src_fd, dst_fd) == -1)
  retval = -1;
  /* Careful with writing... */
@@ -348,9 +269,8 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
  bb_perror_msg("can't create symlink '%s'", dest);
  return -1;
  }
- if (flags & FILEUTILS_PRESERVE_STATUS)
- if (lchown(dest, source_stat.st_uid, source_stat.st_gid) < 0)
- bb_perror_msg("can't preserve %s of '%s'", "ownership", dest);
+ if (lchown(dest, source_stat.st_uid, source_stat.st_gid) < 0)
+ bb_perror_msg("can't preserve %s of '%s'", "ownership", dest);
  }
  /* _Not_ jumping to preserve_mode_ugid_time:
  * symlinks don't have those */
@@ -370,10 +290,7 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
 
  preserve_mode_ugid_time:
 
- if (flags & FILEUTILS_PRESERVE_STATUS
- /* Cannot happen: */
- /* && !(flags & (FILEUTILS_MAKE_SOFTLINK|FILEUTILS_MAKE_HARDLINK)) */
- ) {
+ if (1 /*FILEUTILS_PRESERVE_STATUS*/) {
  struct timeval times[2];
 
  times[1].tv_sec = times[0].tv_sec = source_stat.st_mtime;
@@ -389,10 +306,6 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
  bb_perror_msg("can't preserve %s of '%s'", "permissions", dest);
  }
 
- if (flags & FILEUTILS_VERBOSE) {
- printf("'%s' -> '%s'\n", source, dest);
- }
-
  return retval;
 }
 #endif
--
2.8.0.rc0.210.gd302cd2

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Reply | Threaded
Open this post in threaded view
|

[PATCH 05/25] copy.c: convert bb_(p)error_msg to (sys_)error

Duy Nguyen
In reply to this post by Duy Nguyen
Signed-off-by: Nguyễn Thái Ngọc Duy <[hidden email]>
---
 copy.c | 85 ++++++++++++++++++++++++------------------------------------------
 1 file changed, 31 insertions(+), 54 deletions(-)

diff --git a/copy.c b/copy.c
index d24a8ae..cdb38d5 100644
--- a/copy.c
+++ b/copy.c
@@ -82,23 +82,16 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
  smallint dest_exists = 0;
  smallint ovr;
 
- if (lstat(source, &source_stat) < 0) {
- bb_perror_msg("can't stat '%s'", source);
- return -1;
- }
+ if (lstat(source, &source_stat) < 0)
+ return sys_error(_("can't stat '%s'"), source);
 
  if (lstat(dest, &dest_stat) < 0) {
- if (errno != ENOENT) {
- bb_perror_msg("can't stat '%s'", dest);
- return -1;
- }
+ if (errno != ENOENT)
+ return sys_error(_("can't stat '%s'"), dest);
  } else {
- if (source_stat.st_dev == dest_stat.st_dev
- && source_stat.st_ino == dest_stat.st_ino
- ) {
- bb_error_msg("'%s' and '%s' are the same file", source, dest);
- return -1;
- }
+ if (source_stat.st_dev == dest_stat.st_dev &&
+    source_stat.st_ino == dest_stat.st_ino)
+ return error(_("'%s' and '%s' are the same file"), source, dest);
  dest_exists = 1;
  }
 
@@ -110,18 +103,14 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
 
  /* Did we ever create source ourself before? */
  tp = is_in_ino_dev_hashtable(&source_stat);
- if (tp) {
+ if (tp)
  /* We did! it's a recursion! man the lifeboats... */
- bb_error_msg("recursion detected, omitting directory '%s'",
- source);
- return -1;
- }
+ return error(_("recursion detected, omitting directory '%s'"),
+     source);
 
  if (dest_exists) {
- if (!S_ISDIR(dest_stat.st_mode)) {
- bb_error_msg("target '%s' is not a directory", dest);
- return -1;
- }
+ if (!S_ISDIR(dest_stat.st_mode))
+ return error(_("target '%s' is not a directory"), dest);
  /* race here: user can substitute a symlink between
  * this check and actual creation of files inside dest */
  } else {
@@ -134,15 +123,12 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
  mode |= S_IRWXU;
  if (mkdir(dest, mode) < 0) {
  umask(saved_umask);
- bb_perror_msg("can't create directory '%s'", dest);
- return -1;
+ return sys_error(_("can't create directory '%s'"), dest);
  }
  umask(saved_umask);
  /* need stat info for add_to_ino_dev_hashtable */
- if (lstat(dest, &dest_stat) < 0) {
- bb_perror_msg("can't stat '%s'", dest);
- return -1;
- }
+ if (lstat(dest, &dest_stat) < 0)
+ return sys_error(_("can't stat '%s'"), dest);
  }
  /* remember (dev,inode) of each created dir.
  * NULL: name is not remembered */
@@ -172,7 +158,7 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
  if (!dest_exists
  && chmod(dest, source_stat.st_mode & ~saved_umask) < 0
  ) {
- bb_perror_msg("can't preserve %s of '%s'", "permissions", dest);
+ sys_error(_("can't preserve permissions of '%s'"), dest);
  /* retval = -1; - WRONG! copy *WAS* made */
  }
  goto preserve_mode_ugid_time;
@@ -196,10 +182,8 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
  ovr = ask_and_unlink(dest, flags);
  if (ovr <= 0)
  return ovr;
- if (link(link_target, dest) < 0) {
- bb_perror_msg("can't create link '%s'", dest);
- return -1;
- }
+ if (link(link_target, dest) < 0)
+ return sys_error(_("can't create link '%s'"), dest);
  }
  return 0;
  }
@@ -238,10 +222,8 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
  if (bb_copyfd_eof(src_fd, dst_fd) == -1)
  retval = -1;
  /* Careful with writing... */
- if (close(dst_fd) < 0) {
- bb_perror_msg("error writing to '%s'", dest);
- retval = -1;
- }
+ if (close(dst_fd) < 0)
+ retval = sys_error(_("error writing to '%s'"), dest);
  /* ...but read size is already checked by bb_copyfd_eof */
  close(src_fd);
  /* "cp /dev/something new_file" should not
@@ -265,12 +247,10 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
  if (lpath) {
  int r = symlink(lpath, dest);
  free(lpath);
- if (r < 0) {
- bb_perror_msg("can't create symlink '%s'", dest);
- return -1;
- }
+ if (r < 0)
+ return sys_error(_("can't create symlink '%s'"), dest);
  if (lchown(dest, source_stat.st_uid, source_stat.st_gid) < 0)
- bb_perror_msg("can't preserve %s of '%s'", "ownership", dest);
+ sys_error(_("can't preserve %s of '%s'"), "ownership", dest);
  }
  /* _Not_ jumping to preserve_mode_ugid_time:
  * symlinks don't have those */
@@ -279,14 +259,11 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
  if (S_ISBLK(source_stat.st_mode) || S_ISCHR(source_stat.st_mode)
  || S_ISSOCK(source_stat.st_mode) || S_ISFIFO(source_stat.st_mode)
  ) {
- if (mknod(dest, source_stat.st_mode, source_stat.st_rdev) < 0) {
- bb_perror_msg("can't create '%s'", dest);
- return -1;
- }
- } else {
- bb_error_msg("unrecognized file '%s' with mode %x", source, source_stat.st_mode);
- return -1;
- }
+ if (mknod(dest, source_stat.st_mode, source_stat.st_rdev) < 0)
+ return sys_error(_("can't create '%s'"), dest);
+ } else
+ return error(_("unrecognized file '%s' with mode %x"),
+     source, source_stat.st_mode);
 
  preserve_mode_ugid_time:
 
@@ -297,13 +274,13 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
  times[1].tv_usec = times[0].tv_usec = 0;
  /* BTW, utimes sets usec-precision time - just FYI */
  if (utimes(dest, times) < 0)
- bb_perror_msg("can't preserve %s of '%s'", "times", dest);
+ sys_error(_("can't preserve %s of '%s'"), "times", dest);
  if (chown(dest, source_stat.st_uid, source_stat.st_gid) < 0) {
  source_stat.st_mode &= ~(S_ISUID | S_ISGID);
- bb_perror_msg("can't preserve %s of '%s'", "ownership", dest);
+ sys_error(_("can't preserve %s of '%s'"), "ownership", dest);
  }
  if (chmod(dest, source_stat.st_mode) < 0)
- bb_perror_msg("can't preserve %s of '%s'", "permissions", dest);
+ sys_error(_("can't preserve %s of '%s'"), "permissions", dest);
  }
 
  return retval;
--
2.8.0.rc0.210.gd302cd2

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Reply | Threaded
Open this post in threaded view
|

[PATCH 06/25] copy.c: style fix

Duy Nguyen
In reply to this post by Duy Nguyen
Signed-off-by: Nguyễn Thái Ngọc Duy <[hidden email]>
---
 copy.c | 50 +++++++++++++++++++++++++++++---------------------
 1 file changed, 29 insertions(+), 21 deletions(-)

diff --git a/copy.c b/copy.c
index cdb38d5..00f8349 100644
--- a/copy.c
+++ b/copy.c
@@ -111,8 +111,10 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
  if (dest_exists) {
  if (!S_ISDIR(dest_stat.st_mode))
  return error(_("target '%s' is not a directory"), dest);
- /* race here: user can substitute a symlink between
- * this check and actual creation of files inside dest */
+ /*
+ * race here: user can substitute a symlink between
+ * this check and actual creation of files inside dest
+ */
  } else {
  /* Create DEST */
  mode_t mode;
@@ -130,22 +132,24 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
  if (lstat(dest, &dest_stat) < 0)
  return sys_error(_("can't stat '%s'"), dest);
  }
- /* remember (dev,inode) of each created dir.
- * NULL: name is not remembered */
+ /*
+ * remember (dev,inode) of each created dir. name is
+ * not remembered
+ */
  add_to_ino_dev_hashtable(&dest_stat, NULL);
 
  /* Recursively copy files in SOURCE */
  dp = opendir(source);
- if (dp == NULL) {
+ if (!dp) {
  retval = -1;
  goto preserve_mode_ugid_time;
  }
 
- while ((d = readdir(dp)) != NULL) {
+ while ((d = readdir(dp))) {
  char *new_source, *new_dest;
 
  new_source = concat_subpath_file(source, d->d_name);
- if (new_source == NULL)
+ if (!new_source)
  continue;
  new_dest = concat_path_file(dest, d->d_name);
  if (copy_file(new_source, new_dest, flags & ~FILEUTILS_DEREFERENCE_L0) < 0)
@@ -155,16 +159,15 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
  }
  closedir(dp);
 
- if (!dest_exists
- && chmod(dest, source_stat.st_mode & ~saved_umask) < 0
- ) {
+ if (!dest_exists &&
+    chmod(dest, source_stat.st_mode & ~saved_umask) < 0) {
  sys_error(_("can't preserve permissions of '%s'"), dest);
  /* retval = -1; - WRONG! copy *WAS* made */
  }
  goto preserve_mode_ugid_time;
  }
 
- if (S_ISREG(source_stat.st_mode) ) { /* "cp [-opts] regular_file thing2" */
+ if (S_ISREG(source_stat.st_mode)) { /* "cp [-opts] regular_file thing2" */
  int src_fd;
  int dst_fd;
  mode_t new_mode;
@@ -199,7 +202,7 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
  if (!S_ISREG(source_stat.st_mode))
  new_mode = 0666;
 
- // POSIX way is a security problem versus (sym)link attacks
+ /* POSIX way is a security problem versus (sym)link attacks */
  if (!ENABLE_FEATURE_NON_POSIX_CP) {
  dst_fd = open(dest, O_WRONLY|O_CREAT|O_TRUNC, new_mode);
  } else { /* safe way: */
@@ -226,13 +229,15 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
  retval = sys_error(_("error writing to '%s'"), dest);
  /* ...but read size is already checked by bb_copyfd_eof */
  close(src_fd);
- /* "cp /dev/something new_file" should not
- * copy mode of /dev/something */
+ /*
+ * "cp /dev/something new_file" should not
+ * copy mode of /dev/something
+ */
  if (!S_ISREG(source_stat.st_mode))
  return retval;
  goto preserve_mode_ugid_time;
  }
- dont_cat:
+dont_cat:
 
  /* Source is a symlink or a special file */
  /* We are lazy here, a bit lax with races... */
@@ -252,20 +257,23 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
  if (lchown(dest, source_stat.st_uid, source_stat.st_gid) < 0)
  sys_error(_("can't preserve %s of '%s'"), "ownership", dest);
  }
- /* _Not_ jumping to preserve_mode_ugid_time:
- * symlinks don't have those */
+ /*
+ * _Not_ jumping to preserve_mode_ugid_time: symlinks
+ * don't have those
+ */
  return 0;
  }
- if (S_ISBLK(source_stat.st_mode) || S_ISCHR(source_stat.st_mode)
- || S_ISSOCK(source_stat.st_mode) || S_ISFIFO(source_stat.st_mode)
- ) {
+ if (S_ISBLK(source_stat.st_mode) ||
+    S_ISCHR(source_stat.st_mode) ||
+    S_ISSOCK(source_stat.st_mode) ||
+    S_ISFIFO(source_stat.st_mode)) {
  if (mknod(dest, source_stat.st_mode, source_stat.st_rdev) < 0)
  return sys_error(_("can't create '%s'"), dest);
  } else
  return error(_("unrecognized file '%s' with mode %x"),
      source, source_stat.st_mode);
 
- preserve_mode_ugid_time:
+preserve_mode_ugid_time:
 
  if (1 /*FILEUTILS_PRESERVE_STATUS*/) {
  struct timeval times[2];
--
2.8.0.rc0.210.gd302cd2

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Reply | Threaded
Open this post in threaded view
|

[PATCH 07/25] copy.c: convert copy_file() to copy_dir_recursively()

Duy Nguyen
In reply to this post by Duy Nguyen
This finally enables busybox's copy_file() code under a new name
(because "copy_file" is already taken in Git code base). Because this
comes from busybox, POSIXy (or even Linuxy) behavior is expected. More
changes may be needed for Windows support.

Signed-off-by: Nguyễn Thái Ngọc Duy <[hidden email]>
---
 cache.h |   1 +
 copy.c  | 216 ++++++++++++++++++++++++++++++++++++++++++++++++++++------------
 2 files changed, 179 insertions(+), 38 deletions(-)

diff --git a/cache.h b/cache.h
index 9f09540..213a8d3 100644
--- a/cache.h
+++ b/cache.h
@@ -1677,6 +1677,7 @@ extern void fprintf_or_die(FILE *, const char *fmt, ...);
 extern int copy_fd(int ifd, int ofd);
 extern int copy_file(const char *dst, const char *src, int mode);
 extern int copy_file_with_time(const char *dst, const char *src, int mode);
+extern int copy_dir_recursively(const char *source, const char *dest);
 
 extern void write_or_die(int fd, const void *buf, size_t count);
 extern int write_or_whine(int fd, const void *buf, size_t count, const char *msg);
diff --git a/copy.c b/copy.c
index 00f8349..f04ac87 100644
--- a/copy.c
+++ b/copy.c
@@ -1,4 +1,6 @@
 #include "cache.h"
+#include "dir.h"
+#include "hashmap.h"
 
 int copy_fd(int ifd, int ofd)
 {
@@ -66,21 +68,126 @@ int copy_file_with_time(const char *dst, const char *src, int mode)
  return status;
 }
 
-#if 0
-/* Return:
- * -1 error, copy not made
- *  0 copy is made or user answered "no" in interactive mode
- *    (failures to preserve mode/owner/times are not reported in exit code)
+struct inode_key {
+ struct hashmap_entry entry;
+ ino_t ino;
+ dev_t dev;
+ /*
+ * Reportedly, on cramfs a file and a dir can have same ino.
+ * Need to also remember "file/dir" bit:
+ */
+ char isdir; /* bool */
+};
+
+struct inode_value {
+ struct inode_key key;
+ char name[FLEX_ARRAY];
+};
+
+#define HASH_SIZE      311u   /* Should be prime */
+static inline unsigned hash_inode(ino_t i)
+{
+ return i % HASH_SIZE;
+}
+
+static int inode_cmp(const void *entry, const void *entry_or_key,
+     const void *keydata)
+{
+ const struct inode_value *inode = entry;
+ const struct inode_key   *key   = entry_or_key;
+
+ return !(inode->key.ino   == key->ino &&
+ inode->key.dev   == key->dev &&
+ inode->key.isdir == key->isdir);
+}
+
+static const char *is_in_ino_dev_hashtable(const struct hashmap *map,
+   const struct stat *st)
+{
+ struct inode_key key;
+ struct inode_value *value;
+
+ key.entry.hash = hash_inode(st->st_ino);
+ key.ino       = st->st_ino;
+ key.dev       = st->st_dev;
+ key.isdir      = !!S_ISDIR(st->st_mode);
+ value       = hashmap_get(map, &key, NULL);
+ return value ? value->name : NULL;
+}
+
+static void add_to_ino_dev_hashtable(struct hashmap *map,
+     const struct stat *st,
+     const char *path)
+{
+ struct inode_value *v;
+ int len = strlen(path);
+
+ v = xmalloc(offsetof(struct inode_value, name) + len + 1);
+ v->key.entry.hash = hash_inode(st->st_ino);
+ v->key.ino  = st->st_ino;
+ v->key.dev  = st->st_dev;
+ v->key.isdir      = !!S_ISDIR(st->st_mode);
+ memcpy(v->name, path, len + 1);
+ hashmap_add(map, v);
+}
+
+/*
+ * Find out if the last character of a string matches the one given.
+ * Don't underrun the buffer if the string length is 0.
  */
-int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
+static inline char *last_char_is(const char *s, int c)
+{
+ if (s && *s) {
+ size_t sz = strlen(s) - 1;
+ s += sz;
+ if ( (unsigned char)*s == c)
+ return (char*)s;
+ }
+ return NULL;
+}
+
+static inline char *concat_path_file(const char *path, const char *filename)
+{
+ struct strbuf sb = STRBUF_INIT;
+ char *lc;
+
+ if (!path)
+ path = "";
+ lc = last_char_is(path, '/');
+ while (*filename == '/')
+ filename++;
+ strbuf_addf(&sb, "%s%s%s", path, (lc==NULL ? "/" : ""), filename);
+ return strbuf_detach(&sb, NULL);
+}
+
+static char *concat_subpath_file(const char *path, const char *f)
+{
+ if (f && is_dot_or_dotdot(f))
+ return NULL;
+ return concat_path_file(path, f);
+}
+
+static int do_unlink(const char *dest)
+{
+ int e = errno;
+
+ if (unlink(dest) < 0) {
+ errno = e; /* do not use errno from unlink */
+ return sys_error(_("can't create '%s'"), dest);
+ }
+ return 0;
+}
+
+static int copy_dir_1(struct hashmap *inode_map,
+      const char *source,
+      const char *dest)
 {
  /* This is a recursive function, try to minimize stack usage */
- /* NB: each struct stat is ~100 bytes */
  struct stat source_stat;
  struct stat dest_stat;
- smallint retval = 0;
- smallint dest_exists = 0;
- smallint ovr;
+ int retval = 0;
+ int dest_exists = 0;
+ int ovr;
 
  if (lstat(source, &source_stat) < 0)
  return sys_error(_("can't stat '%s'"), source);
@@ -102,7 +209,7 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
  mode_t saved_umask = 0;
 
  /* Did we ever create source ourself before? */
- tp = is_in_ino_dev_hashtable(&source_stat);
+ tp = is_in_ino_dev_hashtable(inode_map, &source_stat);
  if (tp)
  /* We did! it's a recursion! man the lifeboats... */
  return error(_("recursion detected, omitting directory '%s'"),
@@ -132,11 +239,12 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
  if (lstat(dest, &dest_stat) < 0)
  return sys_error(_("can't stat '%s'"), dest);
  }
+
  /*
  * remember (dev,inode) of each created dir. name is
  * not remembered
  */
- add_to_ino_dev_hashtable(&dest_stat, NULL);
+ add_to_ino_dev_hashtable(inode_map, &dest_stat, "");
 
  /* Recursively copy files in SOURCE */
  dp = opendir(source);
@@ -152,7 +260,7 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
  if (!new_source)
  continue;
  new_dest = concat_path_file(dest, d->d_name);
- if (copy_file(new_source, new_dest, flags & ~FILEUTILS_DEREFERENCE_L0) < 0)
+ if (copy_dir_1(inode_map, new_source, new_dest) < 0)
  retval = -1;
  free(new_source);
  free(new_dest);
@@ -177,53 +285,57 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
  goto dont_cat;
  }
 
- if (ENABLE_FEATURE_PRESERVE_HARDLINKS) {
+ if (1 /*ENABLE_FEATURE_PRESERVE_HARDLINKS*/) {
  const char *link_target;
- link_target = is_in_ino_dev_hashtable(&source_stat);
+ link_target = is_in_ino_dev_hashtable(inode_map, &source_stat);
  if (link_target) {
  if (link(link_target, dest) < 0) {
- ovr = ask_and_unlink(dest, flags);
- if (ovr <= 0)
+ ovr = do_unlink(dest);
+ if (ovr < 0)
  return ovr;
  if (link(link_target, dest) < 0)
  return sys_error(_("can't create link '%s'"), dest);
  }
  return 0;
  }
- add_to_ino_dev_hashtable(&source_stat, dest);
+ add_to_ino_dev_hashtable(inode_map, &source_stat, dest);
  }
 
- src_fd = open_or_warn(source, O_RDONLY);
+ src_fd = open(source, O_RDONLY);
  if (src_fd < 0)
- return -1;
+ return sys_error(_("can't open '%s'"), source);
 
  /* Do not try to open with weird mode fields */
  new_mode = source_stat.st_mode;
  if (!S_ISREG(source_stat.st_mode))
  new_mode = 0666;
 
- /* POSIX way is a security problem versus (sym)link attacks */
- if (!ENABLE_FEATURE_NON_POSIX_CP) {
- dst_fd = open(dest, O_WRONLY|O_CREAT|O_TRUNC, new_mode);
- } else { /* safe way: */
- dst_fd = open(dest, O_WRONLY|O_CREAT|O_EXCL, new_mode);
- }
+ dst_fd = open(dest, O_WRONLY|O_CREAT|O_EXCL, new_mode);
  if (dst_fd == -1) {
- ovr = ask_and_unlink(dest, flags);
- if (ovr <= 0) {
+ ovr = do_unlink(dest);
+ if (ovr < 0) {
  close(src_fd);
  return ovr;
  }
  /* It shouldn't exist. If it exists, do not open (symlink attack?) */
- dst_fd = open3_or_warn(dest, O_WRONLY|O_CREAT|O_EXCL, new_mode);
+ dst_fd = open(dest, O_WRONLY|O_CREAT|O_EXCL, new_mode);
  if (dst_fd < 0) {
  close(src_fd);
- return -1;
+ return sys_error(_("can't open '%s'"), dest);
  }
  }
 
- if (bb_copyfd_eof(src_fd, dst_fd) == -1)
+ switch (copy_fd(src_fd, dst_fd)) {
+ case COPY_READ_ERROR:
+ error(_("copy-fd: read returned %s"), strerror(errno));
  retval = -1;
+ break;
+ case COPY_WRITE_ERROR:
+ error(_("copy-fd: write returned %s"), strerror(errno));
+ retval = -1;
+ break;
+ }
+
  /* Careful with writing... */
  if (close(dst_fd) < 0)
  retval = sys_error(_("error writing to '%s'"), dest);
@@ -243,19 +355,28 @@ dont_cat:
  /* We are lazy here, a bit lax with races... */
  if (dest_exists) {
  errno = EEXIST;
- ovr = ask_and_unlink(dest, flags);
- if (ovr <= 0)
+ ovr = do_unlink(dest);
+ if (ovr < 0)
  return ovr;
  }
  if (S_ISLNK(source_stat.st_mode)) {
- char *lpath = xmalloc_readlink_or_warn(source);
- if (lpath) {
- int r = symlink(lpath, dest);
- free(lpath);
+ struct strbuf lpath = STRBUF_INIT;
+ if (!strbuf_readlink(&lpath, source, 0)) {
+ int r = symlink(lpath.buf, dest);
+ strbuf_release(&lpath);
  if (r < 0)
  return sys_error(_("can't create symlink '%s'"), dest);
  if (lchown(dest, source_stat.st_uid, source_stat.st_gid) < 0)
  sys_error(_("can't preserve %s of '%s'"), "ownership", dest);
+ } else {
+ /* EINVAL => "file: Invalid argument" => puzzled user */
+ const char *errmsg = _("not a symlink");
+ int err = errno;
+
+ if (err != EINVAL)
+ errmsg = strerror(err);
+ error(_("%s: cannot read link: %s"), source, errmsg);
+ strbuf_release(&lpath);
  }
  /*
  * _Not_ jumping to preserve_mode_ugid_time: symlinks
@@ -293,4 +414,23 @@ preserve_mode_ugid_time:
 
  return retval;
 }
-#endif
+
+/*
+ * Return:
+ * -1 error, copy not made
+ *  0 copy is made
+ *
+ * Failures to preserve mode/owner/times are not reported in exit
+ * code. No support for preserving SELinux security context. Symlinks
+ * and hardlinks are preserved.
+ */
+int copy_dir_recursively(const char *source, const char *dest)
+{
+ int ret;
+ struct hashmap inode_map;
+
+ hashmap_init(&inode_map, inode_cmp, 1024);
+ ret = copy_dir_1(&inode_map, source, dest);
+ hashmap_free(&inode_map, 1);
+ return ret;
+}
--
2.8.0.rc0.210.gd302cd2

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Reply | Threaded
Open this post in threaded view
|

[PATCH 08/25] completion: support git-worktree

Duy Nguyen
In reply to this post by Duy Nguyen
Signed-off-by: Nguyễn Thái Ngọc Duy <[hidden email]>
---
 contrib/completion/git-completion.bash | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index e3918c8..f66db2d 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -2594,6 +2594,29 @@ _git_whatchanged ()
  _git_log
 }
 
+_git_worktree ()
+{
+ local subcommands="add list prune"
+ local subcommand="$(__git_find_on_cmdline "$subcommands")"
+ if [ -z "$subcommand" ]; then
+ __gitcomp "$subcommands"
+ else
+ case "$subcommand,$cur" in
+ add,--*)
+ __gitcomp "--detach --force"
+ ;;
+ list,--*)
+ __gitcomp "--porcelain"
+ ;;
+ prune,--*)
+ __gitcomp "--dry-run --expire --verbose"
+ ;;
+ *)
+ ;;
+ esac
+ fi
+}
+
 __git_main ()
 {
  local i c=1 command __git_dir
--
2.8.0.rc0.210.gd302cd2

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Reply | Threaded
Open this post in threaded view
|

[PATCH 09/25] git-worktree.txt: keep subcommand listing in alphabetical order

Duy Nguyen
In reply to this post by Duy Nguyen
Signed-off-by: Nguyễn Thái Ngọc Duy <[hidden email]>
---
 Documentation/git-worktree.txt | 10 +++++-----
 builtin/worktree.c             |  2 +-
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt
index 62c76c1..1c9d7c1 100644
--- a/Documentation/git-worktree.txt
+++ b/Documentation/git-worktree.txt
@@ -10,8 +10,8 @@ SYNOPSIS
 --------
 [verse]
 'git worktree add' [-f] [--detach] [-b <new-branch>] <path> [<branch>]
-'git worktree prune' [-n] [-v] [--expire <expire>]
 'git worktree list' [--porcelain]
+'git worktree prune' [-n] [-v] [--expire <expire>]
 
 DESCRIPTION
 -----------
@@ -54,10 +54,6 @@ If `<branch>` is omitted and neither `-b` nor `-B` nor `--detached` used,
 then, as a convenience, a new branch based at HEAD is created automatically,
 as if `-b $(basename <path>)` was specified.
 
-prune::
-
-Prune working tree information in $GIT_DIR/worktrees.
-
 list::
 
 List details of each worktree.  The main worktree is listed first, followed by
@@ -65,6 +61,10 @@ each of the linked worktrees.  The output details include if the worktree is
 bare, the revision currently checked out, and the branch currently checked out
 (or 'detached HEAD' if none).
 
+prune::
+
+Prune working tree information in $GIT_DIR/worktrees.
+
 OPTIONS
 -------
 
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 38b5609..575f899 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -13,8 +13,8 @@
 
 static const char * const worktree_usage[] = {
  N_("git worktree add [<options>] <path> [<branch>]"),
- N_("git worktree prune [<options>]"),
  N_("git worktree list [<options>]"),
+ N_("git worktree prune [<options>]"),
  NULL
 };
 
--
2.8.0.rc0.210.gd302cd2

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Reply | Threaded
Open this post in threaded view
|

[PATCH 10/25] path.c: add git_common_path() and strbuf_git_common_path()

Duy Nguyen
In reply to this post by Duy Nguyen
Signed-off-by: Nguyễn Thái Ngọc Duy <[hidden email]>
---
 cache.h |  3 +++
 path.c  | 29 +++++++++++++++++++++++++++++
 2 files changed, 32 insertions(+)

diff --git a/cache.h b/cache.h
index 213a8d3..c81f654 100644
--- a/cache.h
+++ b/cache.h
@@ -767,11 +767,14 @@ extern int check_repository_format(void);
  */
 extern const char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
 extern const char *git_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
+extern const char *git_common_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
 
 extern char *mksnpath(char *buf, size_t n, const char *fmt, ...)
  __attribute__((format (printf, 3, 4)));
 extern void strbuf_git_path(struct strbuf *sb, const char *fmt, ...)
  __attribute__((format (printf, 2, 3)));
+extern void strbuf_git_common_path(struct strbuf *sb, const char *fmt, ...)
+ __attribute__((format (printf, 2, 3)));
 extern char *git_path_buf(struct strbuf *buf, const char *fmt, ...)
  __attribute__((format (printf, 2, 3)));
 extern void strbuf_git_path_submodule(struct strbuf *sb, const char *path,
diff --git a/path.c b/path.c
index 969b494..e315dd6 100644
--- a/path.c
+++ b/path.c
@@ -503,6 +503,35 @@ void strbuf_git_path_submodule(struct strbuf *buf, const char *path,
  va_end(args);
 }
 
+static void do_git_common_path(struct strbuf *buf,
+       const char *fmt,
+       va_list args)
+{
+ strbuf_addstr(buf, get_git_common_dir());
+ if (buf->len && !is_dir_sep(buf->buf[buf->len - 1]))
+ strbuf_addch(buf, '/');
+ strbuf_vaddf(buf, fmt, args);
+ strbuf_cleanup_path(buf);
+}
+
+const char *git_common_path(const char *fmt, ...)
+{
+ struct strbuf *pathname = get_pathname();
+ va_list args;
+ va_start(args, fmt);
+ do_git_common_path(pathname, fmt, args);
+ va_end(args);
+ return pathname->buf;
+}
+
+void strbuf_git_common_path(struct strbuf *sb, const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ do_git_common_path(sb, fmt, args);
+ va_end(args);
+}
+
 int validate_headref(const char *path)
 {
  struct stat st;
--
2.8.0.rc0.210.gd302cd2

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Reply | Threaded
Open this post in threaded view
|

[PATCH 11/25] worktree.c: use is_dot_or_dotdot()

Duy Nguyen
In reply to this post by Duy Nguyen
Signed-off-by: Nguyễn Thái Ngọc Duy <[hidden email]>
---
 builtin/worktree.c | 2 +-
 worktree.c         | 3 ++-
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/builtin/worktree.c b/builtin/worktree.c
index 575f899..7ff66fa 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -94,7 +94,7 @@ static void prune_worktrees(void)
  if (!dir)
  return;
  while ((d = readdir(dir)) != NULL) {
- if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
+ if (is_dot_or_dotdot(d->d_name))
  continue;
  strbuf_reset(&reason);
  if (!prune_worktree(d->d_name, &reason))
diff --git a/worktree.c b/worktree.c
index 6181a66..ddb8cb7 100644
--- a/worktree.c
+++ b/worktree.c
@@ -2,6 +2,7 @@
 #include "refs.h"
 #include "strbuf.h"
 #include "worktree.h"
+#include "dir.h"
 
 void free_worktrees(struct worktree **worktrees)
 {
@@ -173,7 +174,7 @@ struct worktree **get_worktrees(void)
  if (dir) {
  while ((d = readdir(dir)) != NULL) {
  struct worktree *linked = NULL;
- if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
+ if (is_dot_or_dotdot(d->d_name))
  continue;
 
  if ((linked = get_linked_worktree(d->d_name))) {
--
2.8.0.rc0.210.gd302cd2

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Reply | Threaded
Open this post in threaded view
|

[PATCH 12/25] worktree.c: store "id" instead of "git_dir"

Duy Nguyen
In reply to this post by Duy Nguyen
We can reconstruct git_dir from id quite easily. It's a bit hackier to
do the reverse.

Signed-off-by: Nguyễn Thái Ngọc Duy <[hidden email]>
---
 worktree.c | 29 ++++++++++++++++-------------
 worktree.h |  7 ++++++-
 2 files changed, 22 insertions(+), 14 deletions(-)

diff --git a/worktree.c b/worktree.c
index ddb8cb7..4c38414 100644
--- a/worktree.c
+++ b/worktree.c
@@ -10,7 +10,7 @@ void free_worktrees(struct worktree **worktrees)
 
  for (i = 0; worktrees[i]; i++) {
  free(worktrees[i]->path);
- free(worktrees[i]->git_dir);
+ free(worktrees[i]->id);
  free(worktrees[i]->head_ref);
  free(worktrees[i]);
  }
@@ -75,13 +75,11 @@ static struct worktree *get_main_worktree(void)
  struct worktree *worktree = NULL;
  struct strbuf path = STRBUF_INIT;
  struct strbuf worktree_path = STRBUF_INIT;
- struct strbuf gitdir = STRBUF_INIT;
  struct strbuf head_ref = STRBUF_INIT;
  int is_bare = 0;
  int is_detached = 0;
 
- strbuf_addf(&gitdir, "%s", absolute_path(get_git_common_dir()));
- strbuf_addbuf(&worktree_path, &gitdir);
+ strbuf_addstr(&worktree_path, absolute_path(get_git_common_dir()));
  is_bare = !strbuf_strip_suffix(&worktree_path, "/.git");
  if (is_bare)
  strbuf_strip_suffix(&worktree_path, "/.");
@@ -93,7 +91,7 @@ static struct worktree *get_main_worktree(void)
 
  worktree = xmalloc(sizeof(struct worktree));
  worktree->path = strbuf_detach(&worktree_path, NULL);
- worktree->git_dir = strbuf_detach(&gitdir, NULL);
+ worktree->id = NULL;
  worktree->is_bare = is_bare;
  worktree->head_ref = NULL;
  worktree->is_detached = is_detached;
@@ -101,7 +99,6 @@ static struct worktree *get_main_worktree(void)
 
 done:
  strbuf_release(&path);
- strbuf_release(&gitdir);
  strbuf_release(&worktree_path);
  strbuf_release(&head_ref);
  return worktree;
@@ -112,16 +109,13 @@ static struct worktree *get_linked_worktree(const char *id)
  struct worktree *worktree = NULL;
  struct strbuf path = STRBUF_INIT;
  struct strbuf worktree_path = STRBUF_INIT;
- struct strbuf gitdir = STRBUF_INIT;
  struct strbuf head_ref = STRBUF_INIT;
  int is_detached = 0;
 
  if (!id)
  die("Missing linked worktree name");
 
- strbuf_addf(&gitdir, "%s/worktrees/%s",
- absolute_path(get_git_common_dir()), id);
- strbuf_addf(&path, "%s/gitdir", gitdir.buf);
+ strbuf_git_common_path(&path, "worktrees/%s/gitdir", id);
  if (strbuf_read_file(&worktree_path, path.buf, 0) <= 0)
  /* invalid gitdir file */
  goto done;
@@ -141,7 +135,7 @@ static struct worktree *get_linked_worktree(const char *id)
 
  worktree = xmalloc(sizeof(struct worktree));
  worktree->path = strbuf_detach(&worktree_path, NULL);
- worktree->git_dir = strbuf_detach(&gitdir, NULL);
+ worktree->id = xstrdup(id);
  worktree->is_bare = 0;
  worktree->head_ref = NULL;
  worktree->is_detached = is_detached;
@@ -149,7 +143,6 @@ static struct worktree *get_linked_worktree(const char *id)
 
 done:
  strbuf_release(&path);
- strbuf_release(&gitdir);
  strbuf_release(&worktree_path);
  strbuf_release(&head_ref);
  return worktree;
@@ -189,6 +182,14 @@ struct worktree **get_worktrees(void)
  return list;
 }
 
+const char *get_worktree_git_dir(const struct worktree *wt)
+{
+ if (wt->id)
+ return git_common_path("worktrees/%s", wt->id);
+ else
+ return get_git_common_dir();
+}
+
 char *find_shared_symref(const char *symref, const char *target)
 {
  char *existing = NULL;
@@ -200,7 +201,9 @@ char *find_shared_symref(const char *symref, const char *target)
  for (i = 0; worktrees[i]; i++) {
  strbuf_reset(&path);
  strbuf_reset(&sb);
- strbuf_addf(&path, "%s/%s", worktrees[i]->git_dir, symref);
+ strbuf_addf(&path, "%s/%s",
+    get_worktree_git_dir(worktrees[i]),
+    symref);
 
  if (parse_ref(path.buf, &sb, NULL)) {
  continue;
diff --git a/worktree.h b/worktree.h
index b4b3dda..e89d423 100644
--- a/worktree.h
+++ b/worktree.h
@@ -3,7 +3,7 @@
 
 struct worktree {
  char *path;
- char *git_dir;
+ char *id;
  char *head_ref;
  unsigned char head_sha1[20];
  int is_detached;
@@ -23,6 +23,11 @@ struct worktree {
 extern struct worktree **get_worktrees(void);
 
 /*
+ * Return git dir of the worktree. Note that the path may be relative.
+ */
+extern const char *get_worktree_git_dir(const struct worktree *wt);
+
+/*
  * Free up the memory for worktree(s)
  */
 extern void free_worktrees(struct worktree **);
--
2.8.0.rc0.210.gd302cd2

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Reply | Threaded
Open this post in threaded view
|

[PATCH 13/25] worktree.c: add clear_worktree()

Duy Nguyen
In reply to this post by Duy Nguyen
Signed-off-by: Nguyễn Thái Ngọc Duy <[hidden email]>
---
 worktree.c | 14 +++++++++++---
 worktree.h |  5 +++++
 2 files changed, 16 insertions(+), 3 deletions(-)

diff --git a/worktree.c b/worktree.c
index 4c38414..b4e4b57 100644
--- a/worktree.c
+++ b/worktree.c
@@ -4,14 +4,22 @@
 #include "worktree.h"
 #include "dir.h"
 
+void clear_worktree(struct worktree *wt)
+{
+ if (!wt)
+ return;
+ free(wt->path);
+ free(wt->id);
+ free(wt->head_ref);
+ memset(wt, 0, sizeof(*wt));
+}
+
 void free_worktrees(struct worktree **worktrees)
 {
  int i = 0;
 
  for (i = 0; worktrees[i]; i++) {
- free(worktrees[i]->path);
- free(worktrees[i]->id);
- free(worktrees[i]->head_ref);
+ clear_worktree(worktrees[i]);
  free(worktrees[i]);
  }
  free (worktrees);
diff --git a/worktree.h b/worktree.h
index e89d423..0ba07ab 100644
--- a/worktree.h
+++ b/worktree.h
@@ -28,6 +28,11 @@ extern struct worktree **get_worktrees(void);
 extern const char *get_worktree_git_dir(const struct worktree *wt);
 
 /*
+ * Free up the memory for worktree
+ */
+extern void clear_worktree(struct worktree *);
+
+/*
  * Free up the memory for worktree(s)
  */
 extern void free_worktrees(struct worktree **);
--
2.8.0.rc0.210.gd302cd2

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Reply | Threaded
Open this post in threaded view
|

[PATCH 14/25] worktree.c: add find_worktree_by_path()

Duy Nguyen
In reply to this post by Duy Nguyen
Signed-off-by: Nguyễn Thái Ngọc Duy <[hidden email]>
---
 worktree.c | 16 ++++++++++++++++
 worktree.h |  6 ++++++
 2 files changed, 22 insertions(+)

diff --git a/worktree.c b/worktree.c
index b4e4b57..e444ad1 100644
--- a/worktree.c
+++ b/worktree.c
@@ -198,6 +198,22 @@ const char *get_worktree_git_dir(const struct worktree *wt)
  return get_git_common_dir();
 }
 
+struct worktree *find_worktree_by_path(struct worktree **list,
+       const char *path_)
+{
+ char *path = xstrdup(real_path(path_));
+ struct worktree *wt = NULL;
+
+ while (*list) {
+ wt = *list++;
+ if (!strcmp_icase(path, real_path(wt->path)))
+ break;
+ wt = NULL;
+ }
+ free(path);
+ return wt;
+}
+
 char *find_shared_symref(const char *symref, const char *target)
 {
  char *existing = NULL;
diff --git a/worktree.h b/worktree.h
index 0ba07ab..c163b6b 100644
--- a/worktree.h
+++ b/worktree.h
@@ -28,6 +28,12 @@ extern struct worktree **get_worktrees(void);
 extern const char *get_worktree_git_dir(const struct worktree *wt);
 
 /*
+ * Search a worktree by its path. Paths are normalized internally.
+ */
+extern struct worktree *find_worktree_by_path(struct worktree **list,
+      const char *path_);
+
+/*
  * Free up the memory for worktree
  */
 extern void clear_worktree(struct worktree *);
--
2.8.0.rc0.210.gd302cd2

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Reply | Threaded
Open this post in threaded view
|

[PATCH 15/25] worktree.c: add is_main_worktree()

Duy Nguyen
In reply to this post by Duy Nguyen
Signed-off-by: Nguyễn Thái Ngọc Duy <[hidden email]>
---
 worktree.c | 5 +++++
 worktree.h | 5 +++++
 2 files changed, 10 insertions(+)

diff --git a/worktree.c b/worktree.c
index e444ad1..e878f49 100644
--- a/worktree.c
+++ b/worktree.c
@@ -214,6 +214,11 @@ struct worktree *find_worktree_by_path(struct worktree **list,
  return wt;
 }
 
+int is_main_worktree(const struct worktree *wt)
+{
+ return wt && !wt->id;
+}
+
 char *find_shared_symref(const char *symref, const char *target)
 {
  char *existing = NULL;
diff --git a/worktree.h b/worktree.h
index c163b6b..c7a4d20 100644
--- a/worktree.h
+++ b/worktree.h
@@ -34,6 +34,11 @@ extern struct worktree *find_worktree_by_path(struct worktree **list,
       const char *path_);
 
 /*
+ * Return true if the given worktree is the main one.
+ */
+extern int is_main_worktree(const struct worktree *wt);
+
+/*
  * Free up the memory for worktree
  */
 extern void clear_worktree(struct worktree *);
--
2.8.0.rc0.210.gd302cd2

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Reply | Threaded
Open this post in threaded view
|

[PATCH 16/25] worktree.c: add validate_worktree()

Duy Nguyen
In reply to this post by Duy Nguyen
This function is later used by "worktree move" and "worktree remove"
to ensure that we have a good connection between the repository and
the worktree.

Signed-off-by: Nguyễn Thái Ngọc Duy <[hidden email]>
---
 worktree.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 worktree.h |  5 +++++
 2 files changed, 68 insertions(+)

diff --git a/worktree.c b/worktree.c
index e878f49..28195b1 100644
--- a/worktree.c
+++ b/worktree.c
@@ -219,6 +219,69 @@ int is_main_worktree(const struct worktree *wt)
  return wt && !wt->id;
 }
 
+static int report(int quiet, const char *fmt, ...)
+{
+ va_list params;
+
+ if (quiet)
+ return -1;
+
+ va_start(params, fmt);
+ vfprintf(stderr, fmt, params);
+ fputc('\n', stderr);
+ va_end(params);
+ return -1;
+}
+
+int validate_worktree(const struct worktree *wt, int quiet)
+{
+ struct strbuf sb = STRBUF_INIT;
+ const char *path;
+ int err;
+
+ if (is_main_worktree(wt)) {
+ /*
+ * Main worktree using .git file to point to the
+ * repository would make it impossible to know where
+ * the actual worktree is if this function is executed
+ * from another worktree. No .git file support for now.
+ */
+ strbuf_addf(&sb, "%s/.git", wt->path);
+ if (!is_directory(sb.buf)) {
+ strbuf_release(&sb);
+ return report(quiet, _("'%s/.git' at main worktree is not the repository directory"),
+      wt->path);
+ }
+ return 0;
+ }
+
+ /*
+ * Make sure "gitdir" file points to a real .git file and that
+ * file points back here.
+ */
+ if (!is_absolute_path(wt->path))
+ return report(quiet, _("'%s' file does not contain absolute path to the worktree location"),
+      git_common_path("worktrees/%s/gitdir", wt->id));
+
+ strbuf_addf(&sb, "%s/.git", wt->path);
+ if (!file_exists(sb.buf)) {
+ strbuf_release(&sb);
+ return report(quiet, _("'%s/.git' does not exist"), wt->path);
+ }
+
+ path = read_gitfile_gently(sb.buf, &err);
+ strbuf_release(&sb);
+ if (!path)
+ return report(quiet, _("'%s/.git' is not a .git file, error code %d"),
+      wt->path, err);
+
+ if (strcmp_icase(path, real_path(git_common_path("worktrees/%s", wt->id))))
+ return report(quiet, _("'%s' does not point back to"),
+      wt->path, git_common_path("worktrees/%s", wt->id));
+
+ return 0;
+}
+
 char *find_shared_symref(const char *symref, const char *target)
 {
  char *existing = NULL;
diff --git a/worktree.h b/worktree.h
index c7a4d20..0d6be18 100644
--- a/worktree.h
+++ b/worktree.h
@@ -39,6 +39,11 @@ extern struct worktree *find_worktree_by_path(struct worktree **list,
 extern int is_main_worktree(const struct worktree *wt);
 
 /*
+ * Return zero if the worktree is in good condition.
+ */
+extern int validate_worktree(const struct worktree *wt, int quiet);
+
+/*
  * Free up the memory for worktree
  */
 extern void clear_worktree(struct worktree *);
--
2.8.0.rc0.210.gd302cd2

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Reply | Threaded
Open this post in threaded view
|

[PATCH 17/25] worktree.c: add update_worktree_location()

Duy Nguyen
In reply to this post by Duy Nguyen
Signed-off-by: Nguyễn Thái Ngọc Duy <[hidden email]>
---
 worktree.c | 25 +++++++++++++++++++++++++
 worktree.h |  6 ++++++
 2 files changed, 31 insertions(+)

diff --git a/worktree.c b/worktree.c
index 28195b1..e526e25 100644
--- a/worktree.c
+++ b/worktree.c
@@ -282,6 +282,31 @@ int validate_worktree(const struct worktree *wt, int quiet)
  return 0;
 }
 
+int update_worktree_location(struct worktree *wt, const char *path_)
+{
+ struct strbuf path = STRBUF_INIT;
+ int ret = 0;
+
+ if (is_main_worktree(wt))
+ return 0;
+
+ strbuf_add_absolute_path(&path, path_);
+ if (strcmp_icase(wt->path, path.buf)) {
+ if (!write_file_gently(git_common_path("worktrees/%s/gitdir",
+       wt->id),
+       "%s/.git", real_path(path.buf))) {
+ free(wt->path);
+ wt->path = strbuf_detach(&path, NULL);
+ ret = 0;
+ } else
+ ret = sys_error(_("failed to update '%s'"),
+ git_common_path("worktrees/%s/gitdir",
+ wt->id));
+ }
+ strbuf_release(&path);
+ return ret;
+}
+
 char *find_shared_symref(const char *symref, const char *target)
 {
  char *existing = NULL;
diff --git a/worktree.h b/worktree.h
index 0d6be18..bbe40ef 100644
--- a/worktree.h
+++ b/worktree.h
@@ -44,6 +44,12 @@ extern int is_main_worktree(const struct worktree *wt);
 extern int validate_worktree(const struct worktree *wt, int quiet);
 
 /*
+ * Update worktrees/xxx/gitdir with the new path.
+ */
+extern int update_worktree_location(struct worktree *wt,
+    const char *path_);
+
+/*
  * Free up the memory for worktree
  */
 extern void clear_worktree(struct worktree *);
--
2.8.0.rc0.210.gd302cd2

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Reply | Threaded
Open this post in threaded view
|

[PATCH 18/25] worktree.c: add is_worktree_locked()

Duy Nguyen
In reply to this post by Duy Nguyen
Signed-off-by: Nguyễn Thái Ngọc Duy <[hidden email]>
---
 worktree.c | 18 ++++++++++++++++++
 worktree.h |  6 ++++++
 2 files changed, 24 insertions(+)

diff --git a/worktree.c b/worktree.c
index e526e25..37eec09 100644
--- a/worktree.c
+++ b/worktree.c
@@ -219,6 +219,24 @@ int is_main_worktree(const struct worktree *wt)
  return wt && !wt->id;
 }
 
+const char *is_worktree_locked(const struct worktree *wt)
+{
+ static struct strbuf sb = STRBUF_INIT;
+
+ if (!file_exists(git_common_path("worktrees/%s/locked", wt->id)))
+ return NULL;
+
+ strbuf_reset(&sb);
+ if (strbuf_read_file(&sb,
+     git_common_path("worktrees/%s/locked", wt->id),
+     0) < 0)
+ die_errno(_("failed to read '%s'"),
+  git_common_path("worktrees/%s/locked", wt->id));
+
+ strbuf_rtrim(&sb);
+ return sb.buf;
+}
+
 static int report(int quiet, const char *fmt, ...)
 {
  va_list params;
diff --git a/worktree.h b/worktree.h
index bbe40ef..cbd5389 100644
--- a/worktree.h
+++ b/worktree.h
@@ -39,6 +39,12 @@ extern struct worktree *find_worktree_by_path(struct worktree **list,
 extern int is_main_worktree(const struct worktree *wt);
 
 /*
+ * Return the reason string if the given worktree is locked. Return
+ * NULL otherwise.
+ */
+extern const char *is_worktree_locked(const struct worktree *wt);
+
+/*
  * Return zero if the worktree is in good condition.
  */
 extern int validate_worktree(const struct worktree *wt, int quiet);
--
2.8.0.rc0.210.gd302cd2

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Reply | Threaded
Open this post in threaded view
|

[PATCH 19/25] worktree: avoid 0{40}, too many zeroes, hard to read

Duy Nguyen
In reply to this post by Duy Nguyen
Signed-off-by: Nguyễn Thái Ngọc Duy <[hidden email]>
---
 builtin/worktree.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/builtin/worktree.c b/builtin/worktree.c
index 7ff66fa..e041d7f 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -261,7 +261,7 @@ static int add_worktree(const char *path, const char *refname,
  */
  strbuf_reset(&sb);
  strbuf_addf(&sb, "%s/HEAD", sb_repo.buf);
- write_file(sb.buf, "0000000000000000000000000000000000000000");
+ write_file(sb.buf, sha1_to_hex(null_sha1));
  strbuf_reset(&sb);
  strbuf_addf(&sb, "%s/commondir", sb_repo.buf);
  write_file(sb.buf, "../..");
--
2.8.0.rc0.210.gd302cd2

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
12