0ef67f51b6fe5c16c4327201e0a3dbea54370df11e4b57be9b89a9215e80fc296bb91017d75745a7abb6efffafb1dc90c5d8183ce1247c74638f68b25bbdbb 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. import { chownr, chownrSync } from 'chownr';
  2. import fs from 'fs';
  3. import { mkdirp, mkdirpSync } from 'mkdirp';
  4. import path from 'node:path';
  5. import { CwdError } from './cwd-error.js';
  6. import { normalizeWindowsPath } from './normalize-windows-path.js';
  7. import { SymlinkError } from './symlink-error.js';
  8. const cGet = (cache, key) => cache.get(normalizeWindowsPath(key));
  9. const cSet = (cache, key, val) => cache.set(normalizeWindowsPath(key), val);
  10. const checkCwd = (dir, cb) => {
  11. fs.stat(dir, (er, st) => {
  12. if (er || !st.isDirectory()) {
  13. er = new CwdError(dir, er?.code || 'ENOTDIR');
  14. }
  15. cb(er);
  16. });
  17. };
  18. /**
  19. * Wrapper around mkdirp for tar's needs.
  20. *
  21. * The main purpose is to avoid creating directories if we know that
  22. * they already exist (and track which ones exist for this purpose),
  23. * and prevent entries from being extracted into symlinked folders,
  24. * if `preservePaths` is not set.
  25. */
  26. export const mkdir = (dir, opt, cb) => {
  27. dir = normalizeWindowsPath(dir);
  28. // if there's any overlap between mask and mode,
  29. // then we'll need an explicit chmod
  30. /* c8 ignore next */
  31. const umask = opt.umask ?? 0o22;
  32. const mode = opt.mode | 0o0700;
  33. const needChmod = (mode & umask) !== 0;
  34. const uid = opt.uid;
  35. const gid = opt.gid;
  36. const doChown = typeof uid === 'number' &&
  37. typeof gid === 'number' &&
  38. (uid !== opt.processUid || gid !== opt.processGid);
  39. const preserve = opt.preserve;
  40. const unlink = opt.unlink;
  41. const cache = opt.cache;
  42. const cwd = normalizeWindowsPath(opt.cwd);
  43. const done = (er, created) => {
  44. if (er) {
  45. cb(er);
  46. }
  47. else {
  48. cSet(cache, dir, true);
  49. if (created && doChown) {
  50. chownr(created, uid, gid, er => done(er));
  51. }
  52. else if (needChmod) {
  53. fs.chmod(dir, mode, cb);
  54. }
  55. else {
  56. cb();
  57. }
  58. }
  59. };
  60. if (cache && cGet(cache, dir) === true) {
  61. return done();
  62. }
  63. if (dir === cwd) {
  64. return checkCwd(dir, done);
  65. }
  66. if (preserve) {
  67. return mkdirp(dir, { mode }).then(made => done(null, made ?? undefined), // oh, ts
  68. done);
  69. }
  70. const sub = normalizeWindowsPath(path.relative(cwd, dir));
  71. const parts = sub.split('/');
  72. mkdir_(cwd, parts, mode, cache, unlink, cwd, undefined, done);
  73. };
  74. const mkdir_ = (base, parts, mode, cache, unlink, cwd, created, cb) => {
  75. if (!parts.length) {
  76. return cb(null, created);
  77. }
  78. const p = parts.shift();
  79. const part = normalizeWindowsPath(path.resolve(base + '/' + p));
  80. if (cGet(cache, part)) {
  81. return mkdir_(part, parts, mode, cache, unlink, cwd, created, cb);
  82. }
  83. fs.mkdir(part, mode, onmkdir(part, parts, mode, cache, unlink, cwd, created, cb));
  84. };
  85. const onmkdir = (part, parts, mode, cache, unlink, cwd, created, cb) => (er) => {
  86. if (er) {
  87. fs.lstat(part, (statEr, st) => {
  88. if (statEr) {
  89. statEr.path =
  90. statEr.path && normalizeWindowsPath(statEr.path);
  91. cb(statEr);
  92. }
  93. else if (st.isDirectory()) {
  94. mkdir_(part, parts, mode, cache, unlink, cwd, created, cb);
  95. }
  96. else if (unlink) {
  97. fs.unlink(part, er => {
  98. if (er) {
  99. return cb(er);
  100. }
  101. fs.mkdir(part, mode, onmkdir(part, parts, mode, cache, unlink, cwd, created, cb));
  102. });
  103. }
  104. else if (st.isSymbolicLink()) {
  105. return cb(new SymlinkError(part, part + '/' + parts.join('/')));
  106. }
  107. else {
  108. cb(er);
  109. }
  110. });
  111. }
  112. else {
  113. created = created || part;
  114. mkdir_(part, parts, mode, cache, unlink, cwd, created, cb);
  115. }
  116. };
  117. const checkCwdSync = (dir) => {
  118. let ok = false;
  119. let code = undefined;
  120. try {
  121. ok = fs.statSync(dir).isDirectory();
  122. }
  123. catch (er) {
  124. code = er?.code;
  125. }
  126. finally {
  127. if (!ok) {
  128. throw new CwdError(dir, code ?? 'ENOTDIR');
  129. }
  130. }
  131. };
  132. export const mkdirSync = (dir, opt) => {
  133. dir = normalizeWindowsPath(dir);
  134. // if there's any overlap between mask and mode,
  135. // then we'll need an explicit chmod
  136. /* c8 ignore next */
  137. const umask = opt.umask ?? 0o22;
  138. const mode = opt.mode | 0o700;
  139. const needChmod = (mode & umask) !== 0;
  140. const uid = opt.uid;
  141. const gid = opt.gid;
  142. const doChown = typeof uid === 'number' &&
  143. typeof gid === 'number' &&
  144. (uid !== opt.processUid || gid !== opt.processGid);
  145. const preserve = opt.preserve;
  146. const unlink = opt.unlink;
  147. const cache = opt.cache;
  148. const cwd = normalizeWindowsPath(opt.cwd);
  149. const done = (created) => {
  150. cSet(cache, dir, true);
  151. if (created && doChown) {
  152. chownrSync(created, uid, gid);
  153. }
  154. if (needChmod) {
  155. fs.chmodSync(dir, mode);
  156. }
  157. };
  158. if (cache && cGet(cache, dir) === true) {
  159. return done();
  160. }
  161. if (dir === cwd) {
  162. checkCwdSync(cwd);
  163. return done();
  164. }
  165. if (preserve) {
  166. return done(mkdirpSync(dir, mode) ?? undefined);
  167. }
  168. const sub = normalizeWindowsPath(path.relative(cwd, dir));
  169. const parts = sub.split('/');
  170. let created = undefined;
  171. for (let p = parts.shift(), part = cwd; p && (part += '/' + p); p = parts.shift()) {
  172. part = normalizeWindowsPath(path.resolve(part));
  173. if (cGet(cache, part)) {
  174. continue;
  175. }
  176. try {
  177. fs.mkdirSync(part, mode);
  178. created = created || part;
  179. cSet(cache, part, true);
  180. }
  181. catch (er) {
  182. const st = fs.lstatSync(part);
  183. if (st.isDirectory()) {
  184. cSet(cache, part, true);
  185. continue;
  186. }
  187. else if (unlink) {
  188. fs.unlinkSync(part);
  189. fs.mkdirSync(part, mode);
  190. created = created || part;
  191. cSet(cache, part, true);
  192. continue;
  193. }
  194. else if (st.isSymbolicLink()) {
  195. return new SymlinkError(part, part + '/' + parts.join('/'));
  196. }
  197. }
  198. }
  199. return done(created);
  200. };
  201. //# sourceMappingURL=mkdir.js.map