make_directory.c 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. /* vi: set sw=4 ts=4: */
  2. /*
  3. * parse_mode implementation for busybox
  4. *
  5. * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
  6. *
  7. * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  8. */
  9. /* Mar 5, 2003 Manuel Novoa III
  10. *
  11. * This is the main work function for the 'mkdir' applet. As such, it
  12. * strives to be SUSv3 compliant in it's behaviour when recursively
  13. * making missing parent dirs, and in it's mode setting of the final
  14. * directory 'path'.
  15. *
  16. * To recursively build all missing intermediate directories, make
  17. * sure that (flags & FILEUTILS_RECUR) is non-zero. Newly created
  18. * intermediate directories will have at least u+wx perms.
  19. *
  20. * To set specific permissions on 'path', pass the appropriate 'mode'
  21. * val. Otherwise, pass -1 to get default permissions.
  22. */
  23. #include "libbb.h"
  24. /* This function is used from NOFORK applets. It must not allocate anything */
  25. int FAST_FUNC bb_make_directory(char *path, long mode, int flags)
  26. {
  27. mode_t cur_mask;
  28. mode_t org_mask;
  29. const char *fail_msg;
  30. char *s;
  31. char c;
  32. struct stat st;
  33. /* "path" can be a result of dirname().
  34. * dirname("no_slashes") returns ".", possibly read-only.
  35. * musl dirname() can return read-only "/" too.
  36. * We need writable string. And for "/", "." (and ".."?)
  37. * nothing needs to be created anyway.
  38. */
  39. if (LONE_CHAR(path, '/'))
  40. return 0;
  41. if (path[0] == '.') {
  42. if (path[1] == '\0')
  43. return 0; /* "." */
  44. // if (path[1] == '.' && path[2] == '\0')
  45. // return 0; /* ".." */
  46. }
  47. org_mask = cur_mask = (mode_t)-1L;
  48. s = path;
  49. while (1) {
  50. c = '\0';
  51. if (flags & FILEUTILS_RECUR) { /* Get the parent */
  52. /* Bypass leading non-'/'s and then subsequent '/'s */
  53. while (*s) {
  54. if (*s == '/') {
  55. do {
  56. ++s;
  57. } while (*s == '/');
  58. c = *s; /* Save the current char */
  59. *s = '\0'; /* and replace it with nul */
  60. break;
  61. }
  62. ++s;
  63. }
  64. }
  65. if (c != '\0') {
  66. /* Intermediate dirs: must have wx for user */
  67. if (cur_mask == (mode_t)-1L) { /* wasn't done yet? */
  68. mode_t new_mask;
  69. org_mask = umask(0);
  70. cur_mask = 0;
  71. /* Clear u=wx in umask - this ensures
  72. * they won't be cleared on mkdir */
  73. new_mask = (org_mask & ~(mode_t)0300);
  74. //bb_error_msg("org_mask:%o cur_mask:%o", org_mask, new_mask);
  75. if (new_mask != cur_mask) {
  76. cur_mask = new_mask;
  77. umask(new_mask);
  78. }
  79. }
  80. } else {
  81. /* Last component: uses original umask */
  82. //bb_error_msg("1 org_mask:%o", org_mask);
  83. if (org_mask != cur_mask) {
  84. cur_mask = org_mask;
  85. umask(org_mask);
  86. }
  87. }
  88. if (mkdir(path, 0777) < 0) {
  89. /* If we failed for any other reason than the directory
  90. * already exists, output a diagnostic and return -1 */
  91. if ((errno != EEXIST && errno != EISDIR)
  92. || !(flags & FILEUTILS_RECUR)
  93. || ((stat(path, &st) < 0) || !S_ISDIR(st.st_mode))
  94. ) {
  95. fail_msg = "create";
  96. break;
  97. }
  98. /* Since the directory exists, don't attempt to change
  99. * permissions if it was the full target. Note that
  100. * this is not an error condition. */
  101. if (!c) {
  102. goto ret0;
  103. }
  104. } else {
  105. if (flags & FILEUTILS_VERBOSE) {
  106. printf("created directory: '%s'\n", path);
  107. }
  108. }
  109. if (!c) {
  110. /* Done. If necessary, update perms on the newly
  111. * created directory. Failure to update here _is_
  112. * an error. */
  113. if ((mode != -1) && (chmod(path, mode) < 0)) {
  114. fail_msg = "set permissions of";
  115. if (flags & FILEUTILS_IGNORE_CHMOD_ERR) {
  116. flags = 0;
  117. goto print_err;
  118. }
  119. break;
  120. }
  121. goto ret0;
  122. }
  123. /* Remove any inserted nul from the path (recursive mode) */
  124. *s = c;
  125. } /* while (1) */
  126. flags = -1;
  127. print_err:
  128. bb_perror_msg("can't %s directory '%s'", fail_msg, path);
  129. goto ret;
  130. ret0:
  131. flags = 0;
  132. ret:
  133. //bb_error_msg("2 org_mask:%o", org_mask);
  134. if (org_mask != cur_mask)
  135. umask(org_mask);
  136. return flags;
  137. }