make_directory.c 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  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 tarball for details.
  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. /* Happens on bb_make_directory(dirname("no_slashes"),...) */
  34. if (LONE_CHAR(path, '.'))
  35. return 0;
  36. org_mask = cur_mask = (mode_t)-1L;
  37. s = path;
  38. while (1) {
  39. c = '\0';
  40. if (flags & FILEUTILS_RECUR) { /* Get the parent */
  41. /* Bypass leading non-'/'s and then subsequent '/'s */
  42. while (*s) {
  43. if (*s == '/') {
  44. do {
  45. ++s;
  46. } while (*s == '/');
  47. c = *s; /* Save the current char */
  48. *s = '\0'; /* and replace it with nul */
  49. break;
  50. }
  51. ++s;
  52. }
  53. }
  54. if (c != '\0') {
  55. /* Intermediate dirs: must have wx for user */
  56. if (cur_mask == (mode_t)-1L) { /* wasn't done yet? */
  57. mode_t new_mask;
  58. org_mask = umask(0);
  59. cur_mask = 0;
  60. /* Clear u=wx in umask - this ensures
  61. * they won't be cleared on mkdir */
  62. new_mask = (org_mask & ~(mode_t)0300);
  63. //bb_error_msg("org_mask:%o cur_mask:%o", org_mask, new_mask);
  64. if (new_mask != cur_mask) {
  65. cur_mask = new_mask;
  66. umask(new_mask);
  67. }
  68. }
  69. } else {
  70. /* Last component: uses original umask */
  71. //bb_error_msg("1 org_mask:%o", org_mask);
  72. if (org_mask != cur_mask) {
  73. cur_mask = org_mask;
  74. umask(org_mask);
  75. }
  76. }
  77. if (mkdir(path, 0777) < 0) {
  78. /* If we failed for any other reason than the directory
  79. * already exists, output a diagnostic and return -1 */
  80. if ((errno != EEXIST && errno != EISDIR)
  81. || !(flags & FILEUTILS_RECUR)
  82. || ((stat(path, &st) < 0) || !S_ISDIR(st.st_mode))
  83. ) {
  84. fail_msg = "create";
  85. break;
  86. }
  87. /* Since the directory exists, don't attempt to change
  88. * permissions if it was the full target. Note that
  89. * this is not an error condition. */
  90. if (!c) {
  91. goto ret0;
  92. }
  93. }
  94. if (!c) {
  95. /* Done. If necessary, update perms on the newly
  96. * created directory. Failure to update here _is_
  97. * an error. */
  98. if ((mode != -1) && (chmod(path, mode) < 0)) {
  99. fail_msg = "set permissions of";
  100. break;
  101. }
  102. goto ret0;
  103. }
  104. /* Remove any inserted nul from the path (recursive mode) */
  105. *s = c;
  106. } /* while (1) */
  107. bb_perror_msg("can't %s directory '%s'", fail_msg, path);
  108. flags = -1;
  109. goto ret;
  110. ret0:
  111. flags = 0;
  112. ret:
  113. //bb_error_msg("2 org_mask:%o", org_mask);
  114. if (org_mask != cur_mask)
  115. umask(org_mask);
  116. return flags;
  117. }