1f1190dfe7d7f9ae7585b94cab92c352ea618ad6b67294b4f035d0d8475b316f6f862da9e06ba345d13474f471584430a5b8836b69593676fd3f5d8388d5f4 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657
  1. import fs from 'fs';
  2. import { Minipass } from 'minipass';
  3. import path from 'path';
  4. import { Header } from './header.js';
  5. import { modeFix } from './mode-fix.js';
  6. import { normalizeWindowsPath } from './normalize-windows-path.js';
  7. import { dealias, } from './options.js';
  8. import { Pax } from './pax.js';
  9. import { stripAbsolutePath } from './strip-absolute-path.js';
  10. import { stripTrailingSlashes } from './strip-trailing-slashes.js';
  11. import { warnMethod, } from './warn-method.js';
  12. import * as winchars from './winchars.js';
  13. const prefixPath = (path, prefix) => {
  14. if (!prefix) {
  15. return normalizeWindowsPath(path);
  16. }
  17. path = normalizeWindowsPath(path).replace(/^\.(\/|$)/, '');
  18. return stripTrailingSlashes(prefix) + '/' + path;
  19. };
  20. const maxReadSize = 16 * 1024 * 1024;
  21. const PROCESS = Symbol('process');
  22. const FILE = Symbol('file');
  23. const DIRECTORY = Symbol('directory');
  24. const SYMLINK = Symbol('symlink');
  25. const HARDLINK = Symbol('hardlink');
  26. const HEADER = Symbol('header');
  27. const READ = Symbol('read');
  28. const LSTAT = Symbol('lstat');
  29. const ONLSTAT = Symbol('onlstat');
  30. const ONREAD = Symbol('onread');
  31. const ONREADLINK = Symbol('onreadlink');
  32. const OPENFILE = Symbol('openfile');
  33. const ONOPENFILE = Symbol('onopenfile');
  34. const CLOSE = Symbol('close');
  35. const MODE = Symbol('mode');
  36. const AWAITDRAIN = Symbol('awaitDrain');
  37. const ONDRAIN = Symbol('ondrain');
  38. const PREFIX = Symbol('prefix');
  39. export class WriteEntry extends Minipass {
  40. path;
  41. portable;
  42. myuid = (process.getuid && process.getuid()) || 0;
  43. // until node has builtin pwnam functions, this'll have to do
  44. myuser = process.env.USER || '';
  45. maxReadSize;
  46. linkCache;
  47. statCache;
  48. preservePaths;
  49. cwd;
  50. strict;
  51. mtime;
  52. noPax;
  53. noMtime;
  54. prefix;
  55. fd;
  56. blockLen = 0;
  57. blockRemain = 0;
  58. buf;
  59. pos = 0;
  60. remain = 0;
  61. length = 0;
  62. offset = 0;
  63. win32;
  64. absolute;
  65. header;
  66. type;
  67. linkpath;
  68. stat;
  69. onWriteEntry;
  70. #hadError = false;
  71. constructor(p, opt_ = {}) {
  72. const opt = dealias(opt_);
  73. super();
  74. this.path = normalizeWindowsPath(p);
  75. // suppress atime, ctime, uid, gid, uname, gname
  76. this.portable = !!opt.portable;
  77. this.maxReadSize = opt.maxReadSize || maxReadSize;
  78. this.linkCache = opt.linkCache || new Map();
  79. this.statCache = opt.statCache || new Map();
  80. this.preservePaths = !!opt.preservePaths;
  81. this.cwd = normalizeWindowsPath(opt.cwd || process.cwd());
  82. this.strict = !!opt.strict;
  83. this.noPax = !!opt.noPax;
  84. this.noMtime = !!opt.noMtime;
  85. this.mtime = opt.mtime;
  86. this.prefix =
  87. opt.prefix ? normalizeWindowsPath(opt.prefix) : undefined;
  88. this.onWriteEntry = opt.onWriteEntry;
  89. if (typeof opt.onwarn === 'function') {
  90. this.on('warn', opt.onwarn);
  91. }
  92. let pathWarn = false;
  93. if (!this.preservePaths) {
  94. const [root, stripped] = stripAbsolutePath(this.path);
  95. if (root && typeof stripped === 'string') {
  96. this.path = stripped;
  97. pathWarn = root;
  98. }
  99. }
  100. this.win32 = !!opt.win32 || process.platform === 'win32';
  101. if (this.win32) {
  102. // force the \ to / normalization, since we might not *actually*
  103. // be on windows, but want \ to be considered a path separator.
  104. this.path = winchars.decode(this.path.replace(/\\/g, '/'));
  105. p = p.replace(/\\/g, '/');
  106. }
  107. this.absolute = normalizeWindowsPath(opt.absolute || path.resolve(this.cwd, p));
  108. if (this.path === '') {
  109. this.path = './';
  110. }
  111. if (pathWarn) {
  112. this.warn('TAR_ENTRY_INFO', `stripping ${pathWarn} from absolute path`, {
  113. entry: this,
  114. path: pathWarn + this.path,
  115. });
  116. }
  117. const cs = this.statCache.get(this.absolute);
  118. if (cs) {
  119. this[ONLSTAT](cs);
  120. }
  121. else {
  122. this[LSTAT]();
  123. }
  124. }
  125. warn(code, message, data = {}) {
  126. return warnMethod(this, code, message, data);
  127. }
  128. emit(ev, ...data) {
  129. if (ev === 'error') {
  130. this.#hadError = true;
  131. }
  132. return super.emit(ev, ...data);
  133. }
  134. [LSTAT]() {
  135. fs.lstat(this.absolute, (er, stat) => {
  136. if (er) {
  137. return this.emit('error', er);
  138. }
  139. this[ONLSTAT](stat);
  140. });
  141. }
  142. [ONLSTAT](stat) {
  143. this.statCache.set(this.absolute, stat);
  144. this.stat = stat;
  145. if (!stat.isFile()) {
  146. stat.size = 0;
  147. }
  148. this.type = getType(stat);
  149. this.emit('stat', stat);
  150. this[PROCESS]();
  151. }
  152. [PROCESS]() {
  153. switch (this.type) {
  154. case 'File':
  155. return this[FILE]();
  156. case 'Directory':
  157. return this[DIRECTORY]();
  158. case 'SymbolicLink':
  159. return this[SYMLINK]();
  160. // unsupported types are ignored.
  161. default:
  162. return this.end();
  163. }
  164. }
  165. [MODE](mode) {
  166. return modeFix(mode, this.type === 'Directory', this.portable);
  167. }
  168. [PREFIX](path) {
  169. return prefixPath(path, this.prefix);
  170. }
  171. [HEADER]() {
  172. /* c8 ignore start */
  173. if (!this.stat) {
  174. throw new Error('cannot write header before stat');
  175. }
  176. /* c8 ignore stop */
  177. if (this.type === 'Directory' && this.portable) {
  178. this.noMtime = true;
  179. }
  180. this.onWriteEntry?.(this);
  181. this.header = new Header({
  182. path: this[PREFIX](this.path),
  183. // only apply the prefix to hard links.
  184. linkpath: this.type === 'Link' && this.linkpath !== undefined ?
  185. this[PREFIX](this.linkpath)
  186. : this.linkpath,
  187. // only the permissions and setuid/setgid/sticky bitflags
  188. // not the higher-order bits that specify file type
  189. mode: this[MODE](this.stat.mode),
  190. uid: this.portable ? undefined : this.stat.uid,
  191. gid: this.portable ? undefined : this.stat.gid,
  192. size: this.stat.size,
  193. mtime: this.noMtime ? undefined : this.mtime || this.stat.mtime,
  194. /* c8 ignore next */
  195. type: this.type === 'Unsupported' ? undefined : this.type,
  196. uname: this.portable ? undefined
  197. : this.stat.uid === this.myuid ? this.myuser
  198. : '',
  199. atime: this.portable ? undefined : this.stat.atime,
  200. ctime: this.portable ? undefined : this.stat.ctime,
  201. });
  202. if (this.header.encode() && !this.noPax) {
  203. super.write(new Pax({
  204. atime: this.portable ? undefined : this.header.atime,
  205. ctime: this.portable ? undefined : this.header.ctime,
  206. gid: this.portable ? undefined : this.header.gid,
  207. mtime: this.noMtime ? undefined : (this.mtime || this.header.mtime),
  208. path: this[PREFIX](this.path),
  209. linkpath: this.type === 'Link' && this.linkpath !== undefined ?
  210. this[PREFIX](this.linkpath)
  211. : this.linkpath,
  212. size: this.header.size,
  213. uid: this.portable ? undefined : this.header.uid,
  214. uname: this.portable ? undefined : this.header.uname,
  215. dev: this.portable ? undefined : this.stat.dev,
  216. ino: this.portable ? undefined : this.stat.ino,
  217. nlink: this.portable ? undefined : this.stat.nlink,
  218. }).encode());
  219. }
  220. const block = this.header?.block;
  221. /* c8 ignore start */
  222. if (!block) {
  223. throw new Error('failed to encode header');
  224. }
  225. /* c8 ignore stop */
  226. super.write(block);
  227. }
  228. [DIRECTORY]() {
  229. /* c8 ignore start */
  230. if (!this.stat) {
  231. throw new Error('cannot create directory entry without stat');
  232. }
  233. /* c8 ignore stop */
  234. if (this.path.slice(-1) !== '/') {
  235. this.path += '/';
  236. }
  237. this.stat.size = 0;
  238. this[HEADER]();
  239. this.end();
  240. }
  241. [SYMLINK]() {
  242. fs.readlink(this.absolute, (er, linkpath) => {
  243. if (er) {
  244. return this.emit('error', er);
  245. }
  246. this[ONREADLINK](linkpath);
  247. });
  248. }
  249. [ONREADLINK](linkpath) {
  250. this.linkpath = normalizeWindowsPath(linkpath);
  251. this[HEADER]();
  252. this.end();
  253. }
  254. [HARDLINK](linkpath) {
  255. /* c8 ignore start */
  256. if (!this.stat) {
  257. throw new Error('cannot create link entry without stat');
  258. }
  259. /* c8 ignore stop */
  260. this.type = 'Link';
  261. this.linkpath = normalizeWindowsPath(path.relative(this.cwd, linkpath));
  262. this.stat.size = 0;
  263. this[HEADER]();
  264. this.end();
  265. }
  266. [FILE]() {
  267. /* c8 ignore start */
  268. if (!this.stat) {
  269. throw new Error('cannot create file entry without stat');
  270. }
  271. /* c8 ignore stop */
  272. if (this.stat.nlink > 1) {
  273. const linkKey = `${this.stat.dev}:${this.stat.ino}`;
  274. const linkpath = this.linkCache.get(linkKey);
  275. if (linkpath?.indexOf(this.cwd) === 0) {
  276. return this[HARDLINK](linkpath);
  277. }
  278. this.linkCache.set(linkKey, this.absolute);
  279. }
  280. this[HEADER]();
  281. if (this.stat.size === 0) {
  282. return this.end();
  283. }
  284. this[OPENFILE]();
  285. }
  286. [OPENFILE]() {
  287. fs.open(this.absolute, 'r', (er, fd) => {
  288. if (er) {
  289. return this.emit('error', er);
  290. }
  291. this[ONOPENFILE](fd);
  292. });
  293. }
  294. [ONOPENFILE](fd) {
  295. this.fd = fd;
  296. if (this.#hadError) {
  297. return this[CLOSE]();
  298. }
  299. /* c8 ignore start */
  300. if (!this.stat) {
  301. throw new Error('should stat before calling onopenfile');
  302. }
  303. /* c8 ignore start */
  304. this.blockLen = 512 * Math.ceil(this.stat.size / 512);
  305. this.blockRemain = this.blockLen;
  306. const bufLen = Math.min(this.blockLen, this.maxReadSize);
  307. this.buf = Buffer.allocUnsafe(bufLen);
  308. this.offset = 0;
  309. this.pos = 0;
  310. this.remain = this.stat.size;
  311. this.length = this.buf.length;
  312. this[READ]();
  313. }
  314. [READ]() {
  315. const { fd, buf, offset, length, pos } = this;
  316. if (fd === undefined || buf === undefined) {
  317. throw new Error('cannot read file without first opening');
  318. }
  319. fs.read(fd, buf, offset, length, pos, (er, bytesRead) => {
  320. if (er) {
  321. // ignoring the error from close(2) is a bad practice, but at
  322. // this point we already have an error, don't need another one
  323. return this[CLOSE](() => this.emit('error', er));
  324. }
  325. this[ONREAD](bytesRead);
  326. });
  327. }
  328. /* c8 ignore start */
  329. [CLOSE](cb = () => { }) {
  330. /* c8 ignore stop */
  331. if (this.fd !== undefined)
  332. fs.close(this.fd, cb);
  333. }
  334. [ONREAD](bytesRead) {
  335. if (bytesRead <= 0 && this.remain > 0) {
  336. const er = Object.assign(new Error('encountered unexpected EOF'), {
  337. path: this.absolute,
  338. syscall: 'read',
  339. code: 'EOF',
  340. });
  341. return this[CLOSE](() => this.emit('error', er));
  342. }
  343. if (bytesRead > this.remain) {
  344. const er = Object.assign(new Error('did not encounter expected EOF'), {
  345. path: this.absolute,
  346. syscall: 'read',
  347. code: 'EOF',
  348. });
  349. return this[CLOSE](() => this.emit('error', er));
  350. }
  351. /* c8 ignore start */
  352. if (!this.buf) {
  353. throw new Error('should have created buffer prior to reading');
  354. }
  355. /* c8 ignore stop */
  356. // null out the rest of the buffer, if we could fit the block padding
  357. // at the end of this loop, we've incremented bytesRead and this.remain
  358. // to be incremented up to the blockRemain level, as if we had expected
  359. // to get a null-padded file, and read it until the end. then we will
  360. // decrement both remain and blockRemain by bytesRead, and know that we
  361. // reached the expected EOF, without any null buffer to append.
  362. if (bytesRead === this.remain) {
  363. for (let i = bytesRead; i < this.length && bytesRead < this.blockRemain; i++) {
  364. this.buf[i + this.offset] = 0;
  365. bytesRead++;
  366. this.remain++;
  367. }
  368. }
  369. const chunk = this.offset === 0 && bytesRead === this.buf.length ?
  370. this.buf
  371. : this.buf.subarray(this.offset, this.offset + bytesRead);
  372. const flushed = this.write(chunk);
  373. if (!flushed) {
  374. this[AWAITDRAIN](() => this[ONDRAIN]());
  375. }
  376. else {
  377. this[ONDRAIN]();
  378. }
  379. }
  380. [AWAITDRAIN](cb) {
  381. this.once('drain', cb);
  382. }
  383. write(chunk, encoding, cb) {
  384. /* c8 ignore start - just junk to comply with NodeJS.WritableStream */
  385. if (typeof encoding === 'function') {
  386. cb = encoding;
  387. encoding = undefined;
  388. }
  389. if (typeof chunk === 'string') {
  390. chunk = Buffer.from(chunk, typeof encoding === 'string' ? encoding : 'utf8');
  391. }
  392. /* c8 ignore stop */
  393. if (this.blockRemain < chunk.length) {
  394. const er = Object.assign(new Error('writing more data than expected'), {
  395. path: this.absolute,
  396. });
  397. return this.emit('error', er);
  398. }
  399. this.remain -= chunk.length;
  400. this.blockRemain -= chunk.length;
  401. this.pos += chunk.length;
  402. this.offset += chunk.length;
  403. return super.write(chunk, null, cb);
  404. }
  405. [ONDRAIN]() {
  406. if (!this.remain) {
  407. if (this.blockRemain) {
  408. super.write(Buffer.alloc(this.blockRemain));
  409. }
  410. return this[CLOSE](er => er ? this.emit('error', er) : this.end());
  411. }
  412. /* c8 ignore start */
  413. if (!this.buf) {
  414. throw new Error('buffer lost somehow in ONDRAIN');
  415. }
  416. /* c8 ignore stop */
  417. if (this.offset >= this.length) {
  418. // if we only have a smaller bit left to read, alloc a smaller buffer
  419. // otherwise, keep it the same length it was before.
  420. this.buf = Buffer.allocUnsafe(Math.min(this.blockRemain, this.buf.length));
  421. this.offset = 0;
  422. }
  423. this.length = this.buf.length - this.offset;
  424. this[READ]();
  425. }
  426. }
  427. export class WriteEntrySync extends WriteEntry {
  428. sync = true;
  429. [LSTAT]() {
  430. this[ONLSTAT](fs.lstatSync(this.absolute));
  431. }
  432. [SYMLINK]() {
  433. this[ONREADLINK](fs.readlinkSync(this.absolute));
  434. }
  435. [OPENFILE]() {
  436. this[ONOPENFILE](fs.openSync(this.absolute, 'r'));
  437. }
  438. [READ]() {
  439. let threw = true;
  440. try {
  441. const { fd, buf, offset, length, pos } = this;
  442. /* c8 ignore start */
  443. if (fd === undefined || buf === undefined) {
  444. throw new Error('fd and buf must be set in READ method');
  445. }
  446. /* c8 ignore stop */
  447. const bytesRead = fs.readSync(fd, buf, offset, length, pos);
  448. this[ONREAD](bytesRead);
  449. threw = false;
  450. }
  451. finally {
  452. // ignoring the error from close(2) is a bad practice, but at
  453. // this point we already have an error, don't need another one
  454. if (threw) {
  455. try {
  456. this[CLOSE](() => { });
  457. }
  458. catch (er) { }
  459. }
  460. }
  461. }
  462. [AWAITDRAIN](cb) {
  463. cb();
  464. }
  465. /* c8 ignore start */
  466. [CLOSE](cb = () => { }) {
  467. /* c8 ignore stop */
  468. if (this.fd !== undefined)
  469. fs.closeSync(this.fd);
  470. cb();
  471. }
  472. }
  473. export class WriteEntryTar extends Minipass {
  474. blockLen = 0;
  475. blockRemain = 0;
  476. buf = 0;
  477. pos = 0;
  478. remain = 0;
  479. length = 0;
  480. preservePaths;
  481. portable;
  482. strict;
  483. noPax;
  484. noMtime;
  485. readEntry;
  486. type;
  487. prefix;
  488. path;
  489. mode;
  490. uid;
  491. gid;
  492. uname;
  493. gname;
  494. header;
  495. mtime;
  496. atime;
  497. ctime;
  498. linkpath;
  499. size;
  500. onWriteEntry;
  501. warn(code, message, data = {}) {
  502. return warnMethod(this, code, message, data);
  503. }
  504. constructor(readEntry, opt_ = {}) {
  505. const opt = dealias(opt_);
  506. super();
  507. this.preservePaths = !!opt.preservePaths;
  508. this.portable = !!opt.portable;
  509. this.strict = !!opt.strict;
  510. this.noPax = !!opt.noPax;
  511. this.noMtime = !!opt.noMtime;
  512. this.onWriteEntry = opt.onWriteEntry;
  513. this.readEntry = readEntry;
  514. const { type } = readEntry;
  515. /* c8 ignore start */
  516. if (type === 'Unsupported') {
  517. throw new Error('writing entry that should be ignored');
  518. }
  519. /* c8 ignore stop */
  520. this.type = type;
  521. if (this.type === 'Directory' && this.portable) {
  522. this.noMtime = true;
  523. }
  524. this.prefix = opt.prefix;
  525. this.path = normalizeWindowsPath(readEntry.path);
  526. this.mode =
  527. readEntry.mode !== undefined ?
  528. this[MODE](readEntry.mode)
  529. : undefined;
  530. this.uid = this.portable ? undefined : readEntry.uid;
  531. this.gid = this.portable ? undefined : readEntry.gid;
  532. this.uname = this.portable ? undefined : readEntry.uname;
  533. this.gname = this.portable ? undefined : readEntry.gname;
  534. this.size = readEntry.size;
  535. this.mtime =
  536. this.noMtime ? undefined : opt.mtime || readEntry.mtime;
  537. this.atime = this.portable ? undefined : readEntry.atime;
  538. this.ctime = this.portable ? undefined : readEntry.ctime;
  539. this.linkpath =
  540. readEntry.linkpath !== undefined ?
  541. normalizeWindowsPath(readEntry.linkpath)
  542. : undefined;
  543. if (typeof opt.onwarn === 'function') {
  544. this.on('warn', opt.onwarn);
  545. }
  546. let pathWarn = false;
  547. if (!this.preservePaths) {
  548. const [root, stripped] = stripAbsolutePath(this.path);
  549. if (root && typeof stripped === 'string') {
  550. this.path = stripped;
  551. pathWarn = root;
  552. }
  553. }
  554. this.remain = readEntry.size;
  555. this.blockRemain = readEntry.startBlockSize;
  556. this.onWriteEntry?.(this);
  557. this.header = new Header({
  558. path: this[PREFIX](this.path),
  559. linkpath: this.type === 'Link' && this.linkpath !== undefined ?
  560. this[PREFIX](this.linkpath)
  561. : this.linkpath,
  562. // only the permissions and setuid/setgid/sticky bitflags
  563. // not the higher-order bits that specify file type
  564. mode: this.mode,
  565. uid: this.portable ? undefined : this.uid,
  566. gid: this.portable ? undefined : this.gid,
  567. size: this.size,
  568. mtime: this.noMtime ? undefined : this.mtime,
  569. type: this.type,
  570. uname: this.portable ? undefined : this.uname,
  571. atime: this.portable ? undefined : this.atime,
  572. ctime: this.portable ? undefined : this.ctime,
  573. });
  574. if (pathWarn) {
  575. this.warn('TAR_ENTRY_INFO', `stripping ${pathWarn} from absolute path`, {
  576. entry: this,
  577. path: pathWarn + this.path,
  578. });
  579. }
  580. if (this.header.encode() && !this.noPax) {
  581. super.write(new Pax({
  582. atime: this.portable ? undefined : this.atime,
  583. ctime: this.portable ? undefined : this.ctime,
  584. gid: this.portable ? undefined : this.gid,
  585. mtime: this.noMtime ? undefined : this.mtime,
  586. path: this[PREFIX](this.path),
  587. linkpath: this.type === 'Link' && this.linkpath !== undefined ?
  588. this[PREFIX](this.linkpath)
  589. : this.linkpath,
  590. size: this.size,
  591. uid: this.portable ? undefined : this.uid,
  592. uname: this.portable ? undefined : this.uname,
  593. dev: this.portable ? undefined : this.readEntry.dev,
  594. ino: this.portable ? undefined : this.readEntry.ino,
  595. nlink: this.portable ? undefined : this.readEntry.nlink,
  596. }).encode());
  597. }
  598. const b = this.header?.block;
  599. /* c8 ignore start */
  600. if (!b)
  601. throw new Error('failed to encode header');
  602. /* c8 ignore stop */
  603. super.write(b);
  604. readEntry.pipe(this);
  605. }
  606. [PREFIX](path) {
  607. return prefixPath(path, this.prefix);
  608. }
  609. [MODE](mode) {
  610. return modeFix(mode, this.type === 'Directory', this.portable);
  611. }
  612. write(chunk, encoding, cb) {
  613. /* c8 ignore start - just junk to comply with NodeJS.WritableStream */
  614. if (typeof encoding === 'function') {
  615. cb = encoding;
  616. encoding = undefined;
  617. }
  618. if (typeof chunk === 'string') {
  619. chunk = Buffer.from(chunk, typeof encoding === 'string' ? encoding : 'utf8');
  620. }
  621. /* c8 ignore stop */
  622. const writeLen = chunk.length;
  623. if (writeLen > this.blockRemain) {
  624. throw new Error('writing more to entry than is appropriate');
  625. }
  626. this.blockRemain -= writeLen;
  627. return super.write(chunk, cb);
  628. }
  629. end(chunk, encoding, cb) {
  630. if (this.blockRemain) {
  631. super.write(Buffer.alloc(this.blockRemain));
  632. }
  633. /* c8 ignore start - just junk to comply with NodeJS.WritableStream */
  634. if (typeof chunk === 'function') {
  635. cb = chunk;
  636. encoding = undefined;
  637. chunk = undefined;
  638. }
  639. if (typeof encoding === 'function') {
  640. cb = encoding;
  641. encoding = undefined;
  642. }
  643. if (typeof chunk === 'string') {
  644. chunk = Buffer.from(chunk, encoding ?? 'utf8');
  645. }
  646. if (cb)
  647. this.once('finish', cb);
  648. chunk ? super.end(chunk, cb) : super.end(cb);
  649. /* c8 ignore stop */
  650. return this;
  651. }
  652. }
  653. const getType = (stat) => stat.isFile() ? 'File'
  654. : stat.isDirectory() ? 'Directory'
  655. : stat.isSymbolicLink() ? 'SymbolicLink'
  656. : 'Unsupported';
  657. //# sourceMappingURL=write-entry.js.map