rugged 0.23.3 → 0.24.0b0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +1 -1
  3. data/ext/rugged/rugged.c +24 -0
  4. data/ext/rugged/rugged_config.c +65 -0
  5. data/ext/rugged/rugged_remote.c +22 -2
  6. data/ext/rugged/rugged_repo.c +10 -5
  7. data/ext/rugged/rugged_tree.c +4 -1
  8. data/lib/rugged/version.rb +1 -1
  9. data/vendor/libgit2/CMakeLists.txt +47 -2
  10. data/vendor/libgit2/include/git2/config.h +18 -0
  11. data/vendor/libgit2/include/git2/diff.h +25 -2
  12. data/vendor/libgit2/include/git2/errors.h +0 -12
  13. data/vendor/libgit2/include/git2/index.h +11 -0
  14. data/vendor/libgit2/include/git2/remote.h +12 -1
  15. data/vendor/libgit2/include/git2/sys/config.h +14 -0
  16. data/vendor/libgit2/include/git2/sys/filter.h +4 -1
  17. data/vendor/libgit2/include/git2/sys/odb_backend.h +4 -0
  18. data/vendor/libgit2/include/git2/sys/refdb_backend.h +5 -4
  19. data/vendor/libgit2/include/git2/sys/transport.h +27 -0
  20. data/vendor/libgit2/include/git2/transport.h +25 -21
  21. data/vendor/libgit2/include/git2/version.h +2 -2
  22. data/vendor/libgit2/libgit2.pc.in +3 -2
  23. data/vendor/libgit2/src/branch.c +1 -12
  24. data/vendor/libgit2/src/checkout.c +29 -20
  25. data/vendor/libgit2/src/clone.c +2 -2
  26. data/vendor/libgit2/src/common.h +13 -4
  27. data/vendor/libgit2/src/config.c +36 -0
  28. data/vendor/libgit2/src/config.h +15 -0
  29. data/vendor/libgit2/src/config_file.c +124 -20
  30. data/vendor/libgit2/src/config_file.h +10 -0
  31. data/vendor/libgit2/src/curl_stream.c +7 -7
  32. data/vendor/libgit2/src/diff.c +89 -27
  33. data/vendor/libgit2/src/diff_print.c +1 -1
  34. data/vendor/libgit2/src/errors.c +75 -40
  35. data/vendor/libgit2/src/filebuf.c +81 -3
  36. data/vendor/libgit2/src/fileops.c +176 -75
  37. data/vendor/libgit2/src/fileops.h +7 -10
  38. data/vendor/libgit2/src/filter.c +5 -2
  39. data/vendor/libgit2/src/global.c +25 -9
  40. data/vendor/libgit2/src/global.h +1 -0
  41. data/vendor/libgit2/src/idxmap.h +92 -0
  42. data/vendor/libgit2/src/ignore.c +9 -7
  43. data/vendor/libgit2/src/index.c +246 -46
  44. data/vendor/libgit2/src/index.h +2 -0
  45. data/vendor/libgit2/src/iterator.c +377 -118
  46. data/vendor/libgit2/src/iterator.h +28 -20
  47. data/vendor/libgit2/src/merge.c +26 -13
  48. data/vendor/libgit2/src/notes.c +1 -1
  49. data/vendor/libgit2/src/odb.c +1 -2
  50. data/vendor/libgit2/src/odb_loose.c +2 -2
  51. data/vendor/libgit2/src/odb_mempack.c +6 -2
  52. data/vendor/libgit2/src/oidmap.h +2 -0
  53. data/vendor/libgit2/src/openssl_stream.c +9 -3
  54. data/vendor/libgit2/src/path.c +37 -2
  55. data/vendor/libgit2/src/path.h +12 -1
  56. data/vendor/libgit2/src/pathspec.c +12 -12
  57. data/vendor/libgit2/src/push.c +2 -1
  58. data/vendor/libgit2/src/push.h +1 -0
  59. data/vendor/libgit2/src/refdb.c +2 -6
  60. data/vendor/libgit2/src/refdb_fs.c +13 -3
  61. data/vendor/libgit2/src/remote.c +44 -15
  62. data/vendor/libgit2/src/repository.c +28 -12
  63. data/vendor/libgit2/src/stash.c +17 -12
  64. data/vendor/libgit2/src/stransport_stream.c +1 -1
  65. data/vendor/libgit2/src/submodule.c +234 -152
  66. data/vendor/libgit2/src/sysdir.c +22 -8
  67. data/vendor/libgit2/src/transaction.c +41 -0
  68. data/vendor/libgit2/src/transaction.h +14 -0
  69. data/vendor/libgit2/src/transports/cred.c +8 -0
  70. data/vendor/libgit2/src/transports/http.c +6 -0
  71. data/vendor/libgit2/src/transports/smart.c +95 -0
  72. data/vendor/libgit2/src/transports/smart.h +1 -0
  73. data/vendor/libgit2/src/transports/smart_pkt.c +9 -2
  74. data/vendor/libgit2/src/transports/ssh.c +5 -3
  75. data/vendor/libgit2/src/transports/winhttp.c +19 -1
  76. data/vendor/libgit2/src/unix/posix.h +14 -1
  77. data/vendor/libgit2/src/util.c +56 -13
  78. data/vendor/libgit2/src/util.h +13 -5
  79. data/vendor/libgit2/src/win32/path_w32.c +15 -8
  80. data/vendor/libgit2/src/win32/posix_w32.c +11 -2
  81. data/vendor/libgit2/src/win32/w32_crtdbg_stacktrace.c +343 -0
  82. data/vendor/libgit2/src/win32/w32_crtdbg_stacktrace.h +93 -0
  83. data/vendor/libgit2/src/win32/w32_stack.c +192 -0
  84. data/vendor/libgit2/src/win32/w32_stack.h +138 -0
  85. data/vendor/libgit2/src/win32/w32_util.c +29 -5
  86. data/vendor/libgit2/src/win32/w32_util.h +13 -3
  87. metadata +11 -5
@@ -358,6 +358,7 @@ static int format_binary(
358
358
  scan += chunk_len;
359
359
  pi->line.num_lines++;
360
360
  }
361
+ git_buf_putc(pi->buf, '\n');
361
362
 
362
363
  return 0;
363
364
  }
@@ -416,7 +417,6 @@ static int diff_print_patch_file_binary(
416
417
 
417
418
  if ((error = format_binary(pi, binary->new_file.type, binary->new_file.data,
418
419
  binary->new_file.datalen, binary->new_file.inflatedlen)) < 0 ||
419
- (error = git_buf_putc(pi->buf, '\n')) < 0 ||
420
420
  (error = format_binary(pi, binary->old_file.type, binary->old_file.data,
421
421
  binary->old_file.datalen, binary->old_file.inflatedlen)) < 0) {
422
422
 
@@ -18,19 +18,30 @@ static git_error g_git_oom_error = {
18
18
  GITERR_NOMEMORY
19
19
  };
20
20
 
21
- static void set_error(int error_class, char *string)
21
+ static void set_error_from_buffer(int error_class)
22
22
  {
23
23
  git_error *error = &GIT_GLOBAL->error_t;
24
+ git_buf *buf = &GIT_GLOBAL->error_buf;
24
25
 
25
- if (error->message != string)
26
- git__free(error->message);
27
-
28
- error->message = string;
26
+ error->message = buf->ptr;
29
27
  error->klass = error_class;
30
28
 
31
29
  GIT_GLOBAL->last_error = error;
32
30
  }
33
31
 
32
+ static void set_error(int error_class, char *string)
33
+ {
34
+ git_buf *buf = &GIT_GLOBAL->error_buf;
35
+
36
+ git_buf_clear(buf);
37
+ if (string) {
38
+ git_buf_puts(buf, string);
39
+ git__free(string);
40
+ }
41
+
42
+ set_error_from_buffer(error_class);
43
+ }
44
+
34
45
  void giterr_set_oom(void)
35
46
  {
36
47
  GIT_GLOBAL->last_error = &g_git_oom_error;
@@ -38,27 +49,28 @@ void giterr_set_oom(void)
38
49
 
39
50
  void giterr_set(int error_class, const char *string, ...)
40
51
  {
41
- git_buf buf = GIT_BUF_INIT;
42
52
  va_list arglist;
43
53
  #ifdef GIT_WIN32
44
54
  DWORD win32_error_code = (error_class == GITERR_OS) ? GetLastError() : 0;
45
55
  #endif
46
56
  int error_code = (error_class == GITERR_OS) ? errno : 0;
57
+ git_buf *buf = &GIT_GLOBAL->error_buf;
47
58
 
59
+ git_buf_clear(buf);
48
60
  if (string) {
49
61
  va_start(arglist, string);
50
- git_buf_vprintf(&buf, string, arglist);
62
+ git_buf_vprintf(buf, string, arglist);
51
63
  va_end(arglist);
52
64
 
53
65
  if (error_class == GITERR_OS)
54
- git_buf_PUTS(&buf, ": ");
66
+ git_buf_PUTS(buf, ": ");
55
67
  }
56
68
 
57
69
  if (error_class == GITERR_OS) {
58
70
  #ifdef GIT_WIN32
59
71
  char * win32_error = git_win32_get_error_message(win32_error_code);
60
72
  if (win32_error) {
61
- git_buf_puts(&buf, win32_error);
73
+ git_buf_puts(buf, win32_error);
62
74
  git__free(win32_error);
63
75
 
64
76
  SetLastError(0);
@@ -66,26 +78,29 @@ void giterr_set(int error_class, const char *string, ...)
66
78
  else
67
79
  #endif
68
80
  if (error_code)
69
- git_buf_puts(&buf, strerror(error_code));
81
+ git_buf_puts(buf, strerror(error_code));
70
82
 
71
83
  if (error_code)
72
84
  errno = 0;
73
85
  }
74
86
 
75
- if (!git_buf_oom(&buf))
76
- set_error(error_class, git_buf_detach(&buf));
87
+ if (!git_buf_oom(buf))
88
+ set_error_from_buffer(error_class);
77
89
  }
78
90
 
79
91
  void giterr_set_str(int error_class, const char *string)
80
92
  {
81
- char *message;
93
+ git_buf *buf = &GIT_GLOBAL->error_buf;
82
94
 
83
95
  assert(string);
84
96
 
85
- message = git__strdup(string);
97
+ if (!string)
98
+ return;
86
99
 
87
- if (message)
88
- set_error(error_class, message);
100
+ git_buf_clear(buf);
101
+ git_buf_puts(buf, string);
102
+ if (!git_buf_oom(buf))
103
+ set_error_from_buffer(error_class);
89
104
  }
90
105
 
91
106
  int giterr_set_regex(const regex_t *regex, int error_code)
@@ -116,45 +131,65 @@ void giterr_clear(void)
116
131
  #endif
117
132
  }
118
133
 
119
- int giterr_detach(git_error *cpy)
134
+ const git_error *giterr_last(void)
135
+ {
136
+ return GIT_GLOBAL->last_error;
137
+ }
138
+
139
+ int giterr_state_capture(git_error_state *state, int error_code)
120
140
  {
121
141
  git_error *error = GIT_GLOBAL->last_error;
142
+ git_buf *error_buf = &GIT_GLOBAL->error_buf;
122
143
 
123
- assert(cpy);
144
+ memset(state, 0, sizeof(git_error_state));
124
145
 
125
- if (!error)
126
- return -1;
146
+ if (!error_code)
147
+ return 0;
127
148
 
128
- cpy->message = error->message;
129
- cpy->klass = error->klass;
149
+ state->error_code = error_code;
150
+ state->oom = (error == &g_git_oom_error);
130
151
 
131
- error->message = NULL;
132
- giterr_clear();
152
+ if (error) {
153
+ state->error_msg.klass = error->klass;
133
154
 
134
- return 0;
135
- }
155
+ if (state->oom)
156
+ state->error_msg.message = g_git_oom_error.message;
157
+ else
158
+ state->error_msg.message = git_buf_detach(error_buf);
159
+ }
136
160
 
137
- const git_error *giterr_last(void)
138
- {
139
- return GIT_GLOBAL->last_error;
161
+ giterr_clear();
162
+ return error_code;
140
163
  }
141
164
 
142
- int giterr_capture(git_error_state *state, int error_code)
165
+ int giterr_state_restore(git_error_state *state)
143
166
  {
144
- state->error_code = error_code;
145
- if (error_code)
146
- giterr_detach(&state->error_msg);
147
- return error_code;
167
+ int ret = 0;
168
+
169
+ giterr_clear();
170
+
171
+ if (state && state->error_msg.message) {
172
+ if (state->oom)
173
+ giterr_set_oom();
174
+ else
175
+ set_error(state->error_msg.klass, state->error_msg.message);
176
+
177
+ ret = state->error_code;
178
+ memset(state, 0, sizeof(git_error_state));
179
+ }
180
+
181
+ return ret;
148
182
  }
149
183
 
150
- int giterr_restore(git_error_state *state)
184
+ void giterr_state_free(git_error_state *state)
151
185
  {
152
- if (state && state->error_code && state->error_msg.message)
153
- set_error(state->error_msg.klass, state->error_msg.message);
154
- else
155
- giterr_clear();
186
+ if (!state)
187
+ return;
188
+
189
+ if (!state->oom)
190
+ git__free(state->error_msg.message);
156
191
 
157
- return state ? state->error_code : 0;
192
+ memset(state, 0, sizeof(git_error_state));
158
193
  }
159
194
 
160
195
  int giterr_system_last(void)
@@ -191,6 +191,81 @@ static int write_deflate(git_filebuf *file, void *source, size_t len)
191
191
  return 0;
192
192
  }
193
193
 
194
+ #define MAX_SYMLINK_DEPTH 5
195
+
196
+ static int resolve_symlink(git_buf *out, const char *path)
197
+ {
198
+ int i, error, root;
199
+ ssize_t ret;
200
+ struct stat st;
201
+ git_buf curpath = GIT_BUF_INIT, target = GIT_BUF_INIT;
202
+
203
+ if ((error = git_buf_grow(&target, GIT_PATH_MAX + 1)) < 0 ||
204
+ (error = git_buf_puts(&curpath, path)) < 0)
205
+ return error;
206
+
207
+ for (i = 0; i < MAX_SYMLINK_DEPTH; i++) {
208
+ error = p_lstat(curpath.ptr, &st);
209
+ if (error < 0 && errno == ENOENT) {
210
+ error = git_buf_puts(out, curpath.ptr);
211
+ goto cleanup;
212
+ }
213
+
214
+ if (error < 0) {
215
+ giterr_set(GITERR_OS, "failed to stat '%s'", curpath.ptr);
216
+ error = -1;
217
+ goto cleanup;
218
+ }
219
+
220
+ if (!S_ISLNK(st.st_mode)) {
221
+ error = git_buf_puts(out, curpath.ptr);
222
+ goto cleanup;
223
+ }
224
+
225
+ ret = p_readlink(curpath.ptr, target.ptr, GIT_PATH_MAX);
226
+ if (ret < 0) {
227
+ giterr_set(GITERR_OS, "failed to read symlink '%s'", curpath.ptr);
228
+ error = -1;
229
+ goto cleanup;
230
+ }
231
+
232
+ if (ret == GIT_PATH_MAX) {
233
+ giterr_set(GITERR_INVALID, "symlink target too long");
234
+ error = -1;
235
+ goto cleanup;
236
+ }
237
+
238
+ /* readlink(2) won't NUL-terminate for us */
239
+ target.ptr[ret] = '\0';
240
+ target.size = ret;
241
+
242
+ root = git_path_root(target.ptr);
243
+ if (root >= 0) {
244
+ if ((error = git_buf_puts(&curpath, target.ptr)) < 0)
245
+ goto cleanup;
246
+ } else {
247
+ git_buf dir = GIT_BUF_INIT;
248
+
249
+ if ((error = git_path_dirname_r(&dir, curpath.ptr)) < 0)
250
+ goto cleanup;
251
+
252
+ git_buf_swap(&curpath, &dir);
253
+ git_buf_free(&dir);
254
+
255
+ if ((error = git_path_apply_relative(&curpath, target.ptr)) < 0)
256
+ goto cleanup;
257
+ }
258
+ }
259
+
260
+ giterr_set(GITERR_INVALID, "maximum symlink depth reached");
261
+ error = -1;
262
+
263
+ cleanup:
264
+ git_buf_free(&curpath);
265
+ git_buf_free(&target);
266
+ return error;
267
+ }
268
+
194
269
  int git_filebuf_open(git_filebuf *file, const char *path, int flags, mode_t mode)
195
270
  {
196
271
  int compression, error = -1;
@@ -265,11 +340,14 @@ int git_filebuf_open(git_filebuf *file, const char *path, int flags, mode_t mode
265
340
  file->path_lock = git_buf_detach(&tmp_path);
266
341
  GITERR_CHECK_ALLOC(file->path_lock);
267
342
  } else {
268
- path_len = strlen(path);
343
+ git_buf resolved_path = GIT_BUF_INIT;
344
+
345
+ if ((error = resolve_symlink(&resolved_path, path)) < 0)
346
+ goto cleanup;
269
347
 
270
348
  /* Save the original path of the file */
271
- file->path_original = git__strdup(path);
272
- GITERR_CHECK_ALLOC(file->path_original);
349
+ path_len = resolved_path.size;
350
+ file->path_original = git_buf_detach(&resolved_path);
273
351
 
274
352
  /* create the locking path by appending ".lock" to the original */
275
353
  GITERR_CHECK_ALLOC_ADD(&alloc_len, path_len, GIT_FILELOCK_EXTLENGTH);
@@ -18,7 +18,7 @@ GIT__USE_STRMAP
18
18
  int git_futils_mkpath2file(const char *file_path, const mode_t mode)
19
19
  {
20
20
  return git_futils_mkdir(
21
- file_path, NULL, mode,
21
+ file_path, mode,
22
22
  GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST | GIT_MKDIR_VERIFY_DIR);
23
23
  }
24
24
 
@@ -289,97 +289,230 @@ void git_futils_mmap_free(git_map *out)
289
289
  p_munmap(out);
290
290
  }
291
291
 
292
- GIT_INLINE(int) validate_existing(
293
- const char *make_path,
292
+ GIT_INLINE(int) mkdir_validate_dir(
293
+ const char *path,
294
294
  struct stat *st,
295
295
  mode_t mode,
296
296
  uint32_t flags,
297
- struct git_futils_mkdir_perfdata *perfdata)
297
+ struct git_futils_mkdir_options *opts)
298
298
  {
299
+ /* with exclusive create, existing dir is an error */
300
+ if ((flags & GIT_MKDIR_EXCL) != 0) {
301
+ giterr_set(GITERR_FILESYSTEM,
302
+ "Failed to make directory '%s': directory exists", path);
303
+ return GIT_EEXISTS;
304
+ }
305
+
299
306
  if ((S_ISREG(st->st_mode) && (flags & GIT_MKDIR_REMOVE_FILES)) ||
300
307
  (S_ISLNK(st->st_mode) && (flags & GIT_MKDIR_REMOVE_SYMLINKS))) {
301
- if (p_unlink(make_path) < 0) {
308
+ if (p_unlink(path) < 0) {
302
309
  giterr_set(GITERR_OS, "Failed to remove %s '%s'",
303
- S_ISLNK(st->st_mode) ? "symlink" : "file", make_path);
310
+ S_ISLNK(st->st_mode) ? "symlink" : "file", path);
304
311
  return GIT_EEXISTS;
305
312
  }
306
313
 
307
- perfdata->mkdir_calls++;
314
+ opts->perfdata.mkdir_calls++;
308
315
 
309
- if (p_mkdir(make_path, mode) < 0) {
310
- giterr_set(GITERR_OS, "Failed to make directory '%s'", make_path);
316
+ if (p_mkdir(path, mode) < 0) {
317
+ giterr_set(GITERR_OS, "Failed to make directory '%s'", path);
311
318
  return GIT_EEXISTS;
312
319
  }
313
320
  }
314
321
 
315
322
  else if (S_ISLNK(st->st_mode)) {
316
323
  /* Re-stat the target, make sure it's a directory */
317
- perfdata->stat_calls++;
324
+ opts->perfdata.stat_calls++;
318
325
 
319
- if (p_stat(make_path, st) < 0) {
320
- giterr_set(GITERR_OS, "Failed to make directory '%s'", make_path);
326
+ if (p_stat(path, st) < 0) {
327
+ giterr_set(GITERR_OS, "Failed to make directory '%s'", path);
321
328
  return GIT_EEXISTS;
322
329
  }
323
330
  }
324
331
 
325
332
  else if (!S_ISDIR(st->st_mode)) {
326
333
  giterr_set(GITERR_FILESYSTEM,
327
- "Failed to make directory '%s': directory exists", make_path);
334
+ "Failed to make directory '%s': directory exists", path);
328
335
  return GIT_EEXISTS;
329
336
  }
330
337
 
331
338
  return 0;
332
339
  }
333
340
 
334
- int git_futils_mkdir_ext(
341
+ GIT_INLINE(int) mkdir_validate_mode(
335
342
  const char *path,
336
- const char *base,
343
+ struct stat *st,
344
+ bool terminal_path,
337
345
  mode_t mode,
338
346
  uint32_t flags,
339
347
  struct git_futils_mkdir_options *opts)
340
348
  {
341
- int error = -1;
342
- git_buf make_path = GIT_BUF_INIT;
343
- ssize_t root = 0, min_root_len, root_len;
344
- char lastch = '/', *tail;
345
- struct stat st;
349
+ if (((terminal_path && (flags & GIT_MKDIR_CHMOD) != 0) ||
350
+ (flags & GIT_MKDIR_CHMOD_PATH) != 0) && st->st_mode != mode) {
346
351
 
347
- /* build path and find "root" where we should start calling mkdir */
348
- if (git_path_join_unrooted(&make_path, path, base, &root) < 0)
349
- return -1;
352
+ opts->perfdata.chmod_calls++;
350
353
 
351
- if (make_path.size == 0) {
352
- giterr_set(GITERR_OS, "Attempt to create empty path");
353
- goto done;
354
+ if (p_chmod(path, mode) < 0) {
355
+ giterr_set(GITERR_OS, "failed to set permissions on '%s'", path);
356
+ return -1;
357
+ }
358
+ }
359
+
360
+ return 0;
361
+ }
362
+
363
+ GIT_INLINE(int) mkdir_canonicalize(
364
+ git_buf *path,
365
+ uint32_t flags)
366
+ {
367
+ ssize_t root_len;
368
+
369
+ if (path->size == 0) {
370
+ giterr_set(GITERR_OS, "attempt to create empty path");
371
+ return -1;
354
372
  }
355
373
 
356
374
  /* Trim trailing slashes (except the root) */
357
- if ((root_len = git_path_root(make_path.ptr)) < 0)
375
+ if ((root_len = git_path_root(path->ptr)) < 0)
358
376
  root_len = 0;
359
377
  else
360
378
  root_len++;
361
379
 
362
- while (make_path.size > (size_t)root_len &&
363
- make_path.ptr[make_path.size - 1] == '/')
364
- make_path.ptr[--make_path.size] = '\0';
380
+ while (path->size > (size_t)root_len && path->ptr[path->size - 1] == '/')
381
+ path->ptr[--path->size] = '\0';
365
382
 
366
383
  /* if we are not supposed to made the last element, truncate it */
367
384
  if ((flags & GIT_MKDIR_SKIP_LAST2) != 0) {
368
- git_path_dirname_r(&make_path, make_path.ptr);
385
+ git_path_dirname_r(path, path->ptr);
369
386
  flags |= GIT_MKDIR_SKIP_LAST;
370
387
  }
371
388
  if ((flags & GIT_MKDIR_SKIP_LAST) != 0) {
372
- git_path_dirname_r(&make_path, make_path.ptr);
389
+ git_path_dirname_r(path, path->ptr);
373
390
  }
374
391
 
375
392
  /* We were either given the root path (or trimmed it to
376
- * the root), we don't have anything to do.
393
+ * the root), we don't have anything to do.
394
+ */
395
+ if (path->size <= (size_t)root_len)
396
+ git_buf_clear(path);
397
+
398
+ return 0;
399
+ }
400
+
401
+ int git_futils_mkdir(
402
+ const char *path,
403
+ mode_t mode,
404
+ uint32_t flags)
405
+ {
406
+ git_buf make_path = GIT_BUF_INIT, parent_path = GIT_BUF_INIT;
407
+ const char *relative;
408
+ struct git_futils_mkdir_options opts = { 0 };
409
+ struct stat st;
410
+ size_t depth = 0;
411
+ int len = 0, root_len, error;
412
+
413
+ if ((error = git_buf_puts(&make_path, path)) < 0 ||
414
+ (error = mkdir_canonicalize(&make_path, flags)) < 0 ||
415
+ (error = git_buf_puts(&parent_path, make_path.ptr)) < 0 ||
416
+ make_path.size == 0)
417
+ goto done;
418
+
419
+ root_len = git_path_root(make_path.ptr);
420
+
421
+ /* find the first parent directory that exists. this will be used
422
+ * as the base to dirname_relative.
377
423
  */
378
- if (make_path.size <= (size_t)root_len) {
379
- error = 0;
424
+ for (relative = make_path.ptr; parent_path.size; ) {
425
+ error = p_lstat(parent_path.ptr, &st);
426
+
427
+ if (error == 0) {
428
+ break;
429
+ } else if (errno != ENOENT) {
430
+ giterr_set(GITERR_OS, "failed to stat '%s'", parent_path.ptr);
431
+ goto done;
432
+ }
433
+
434
+ depth++;
435
+
436
+ /* examine the parent of the current path */
437
+ if ((len = git_path_dirname_r(&parent_path, parent_path.ptr)) < 0) {
438
+ error = len;
439
+ goto done;
440
+ }
441
+
442
+ assert(len);
443
+
444
+ /* we've walked all the given path's parents and it's either relative
445
+ * or rooted. either way, give up and make the entire path.
446
+ */
447
+ if ((len == 1 && parent_path.ptr[0] == '.') || len == root_len+1) {
448
+ relative = make_path.ptr;
449
+ break;
450
+ }
451
+
452
+ relative = make_path.ptr + len + 1;
453
+
454
+ /* not recursive? just make this directory relative to its parent. */
455
+ if ((flags & GIT_MKDIR_PATH) == 0)
456
+ break;
457
+ }
458
+
459
+ /* we found an item at the location we're trying to create,
460
+ * validate it.
461
+ */
462
+ if (depth == 0) {
463
+ error = mkdir_validate_dir(make_path.ptr, &st, mode, flags, &opts);
464
+
465
+ if (!error)
466
+ error = mkdir_validate_mode(
467
+ make_path.ptr, &st, true, mode, flags, &opts);
468
+
380
469
  goto done;
381
470
  }
382
471
 
472
+ /* we already took `SKIP_LAST` and `SKIP_LAST2` into account when
473
+ * canonicalizing `make_path`.
474
+ */
475
+ flags &= ~(GIT_MKDIR_SKIP_LAST2 | GIT_MKDIR_SKIP_LAST);
476
+
477
+ error = git_futils_mkdir_relative(relative,
478
+ parent_path.size ? parent_path.ptr : NULL, mode, flags, &opts);
479
+
480
+ done:
481
+ git_buf_free(&make_path);
482
+ git_buf_free(&parent_path);
483
+ return error;
484
+ }
485
+
486
+ int git_futils_mkdir_r(const char *path, const mode_t mode)
487
+ {
488
+ return git_futils_mkdir(path, mode, GIT_MKDIR_PATH);
489
+ }
490
+
491
+ int git_futils_mkdir_relative(
492
+ const char *relative_path,
493
+ const char *base,
494
+ mode_t mode,
495
+ uint32_t flags,
496
+ struct git_futils_mkdir_options *opts)
497
+ {
498
+ git_buf make_path = GIT_BUF_INIT;
499
+ ssize_t root = 0, min_root_len;
500
+ char lastch = '/', *tail;
501
+ struct stat st;
502
+ struct git_futils_mkdir_options empty_opts = {0};
503
+ int error;
504
+
505
+ if (!opts)
506
+ opts = &empty_opts;
507
+
508
+ /* build path and find "root" where we should start calling mkdir */
509
+ if (git_path_join_unrooted(&make_path, relative_path, base, &root) < 0)
510
+ return -1;
511
+
512
+ if ((error = mkdir_canonicalize(&make_path, flags)) < 0 ||
513
+ make_path.size == 0)
514
+ goto done;
515
+
383
516
  /* if we are not supposed to make the whole path, reset root */
384
517
  if ((flags & GIT_MKDIR_PATH) == 0)
385
518
  root = git_buf_rfind(&make_path, '/');
@@ -437,32 +570,15 @@ retry_lstat:
437
570
  goto done;
438
571
  }
439
572
  } else {
440
- /* with exclusive create, existing dir is an error */
441
- if ((flags & GIT_MKDIR_EXCL) != 0) {
442
- giterr_set(GITERR_FILESYSTEM, "Failed to make directory '%s': directory exists", make_path.ptr);
443
- error = GIT_EEXISTS;
573
+ if ((error = mkdir_validate_dir(
574
+ make_path.ptr, &st, mode, flags, opts)) < 0)
444
575
  goto done;
445
- }
446
-
447
- if ((error = validate_existing(
448
- make_path.ptr, &st, mode, flags, &opts->perfdata)) < 0)
449
- goto done;
450
576
  }
451
577
 
452
578
  /* chmod if requested and necessary */
453
- if (((flags & GIT_MKDIR_CHMOD_PATH) != 0 ||
454
- (lastch == '\0' && (flags & GIT_MKDIR_CHMOD) != 0)) &&
455
- st.st_mode != mode) {
456
-
457
- opts->perfdata.chmod_calls++;
458
-
459
- if ((error = p_chmod(make_path.ptr, mode)) < 0 &&
460
- lastch == '\0') {
461
- giterr_set(GITERR_OS, "Failed to set permissions on '%s'",
462
- make_path.ptr);
463
- goto done;
464
- }
465
- }
579
+ if ((error = mkdir_validate_mode(
580
+ make_path.ptr, &st, (lastch == '\0'), mode, flags, opts)) < 0)
581
+ goto done;
466
582
 
467
583
  if (opts->dir_map && opts->pool) {
468
584
  char *cache_path;
@@ -501,21 +617,6 @@ done:
501
617
  return error;
502
618
  }
503
619
 
504
- int git_futils_mkdir(
505
- const char *path,
506
- const char *base,
507
- mode_t mode,
508
- uint32_t flags)
509
- {
510
- struct git_futils_mkdir_options options = {0};
511
- return git_futils_mkdir_ext(path, base, mode, flags, &options);
512
- }
513
-
514
- int git_futils_mkdir_r(const char *path, const char *base, const mode_t mode)
515
- {
516
- return git_futils_mkdir(path, base, mode, GIT_MKDIR_PATH);
517
- }
518
-
519
620
  typedef struct {
520
621
  const char *base;
521
622
  size_t baselen;
@@ -777,7 +878,7 @@ static int _cp_r_mkdir(cp_r_info *info, git_buf *from)
777
878
  /* create root directory the first time we need to create a directory */
778
879
  if ((info->flags & GIT_CPDIR__MKDIR_DONE_FOR_TO_ROOT) == 0) {
779
880
  error = git_futils_mkdir(
780
- info->to_root, NULL, info->dirmode,
881
+ info->to_root, info->dirmode,
781
882
  (info->flags & GIT_CPDIR_CHMOD_DIRS) ? GIT_MKDIR_CHMOD : 0);
782
883
 
783
884
  info->flags |= GIT_CPDIR__MKDIR_DONE_FOR_TO_ROOT;
@@ -785,9 +886,9 @@ static int _cp_r_mkdir(cp_r_info *info, git_buf *from)
785
886
 
786
887
  /* create directory with root as base to prevent excess chmods */
787
888
  if (!error)
788
- error = git_futils_mkdir(
889
+ error = git_futils_mkdir_relative(
789
890
  from->ptr + info->from_prefix, info->to_root,
790
- info->dirmode, info->mkdir_flags);
891
+ info->dirmode, info->mkdir_flags, NULL);
791
892
 
792
893
  return error;
793
894
  }