volume.js 74 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.FSWatcher = exports.StatWatcher = exports.Volume = void 0;
  4. exports.filenameToSteps = filenameToSteps;
  5. exports.pathToSteps = pathToSteps;
  6. exports.dataToStr = dataToStr;
  7. exports.toUnixTimestamp = toUnixTimestamp;
  8. const pathModule = require("path");
  9. const node_1 = require("./node");
  10. const Stats_1 = require("./Stats");
  11. const Dirent_1 = require("./Dirent");
  12. const buffer_1 = require("./internal/buffer");
  13. const setImmediate_1 = require("./setImmediate");
  14. const queueMicrotask_1 = require("./queueMicrotask");
  15. const process_1 = require("./process");
  16. const setTimeoutUnref_1 = require("./setTimeoutUnref");
  17. const stream_1 = require("stream");
  18. const constants_1 = require("./constants");
  19. const events_1 = require("events");
  20. const encoding_1 = require("./encoding");
  21. const FileHandle_1 = require("./node/FileHandle");
  22. const util = require("util");
  23. const FsPromises_1 = require("./node/FsPromises");
  24. const print_1 = require("./print");
  25. const constants_2 = require("./node/constants");
  26. const options_1 = require("./node/options");
  27. const util_1 = require("./node/util");
  28. const Dir_1 = require("./Dir");
  29. const resolveCrossPlatform = pathModule.resolve;
  30. const { O_RDONLY, O_WRONLY, O_RDWR, O_CREAT, O_EXCL, O_TRUNC, O_APPEND, O_DIRECTORY, O_SYMLINK, F_OK, COPYFILE_EXCL, COPYFILE_FICLONE_FORCE, } = constants_1.constants;
  31. const { sep, relative, join, dirname } = pathModule.posix ? pathModule.posix : pathModule;
  32. // ---------------------------------------- Constants
  33. const kMinPoolSpace = 128;
  34. // ---------------------------------------- Error messages
  35. const EPERM = 'EPERM';
  36. const ENOENT = 'ENOENT';
  37. const EBADF = 'EBADF';
  38. const EINVAL = 'EINVAL';
  39. const EEXIST = 'EEXIST';
  40. const ENOTDIR = 'ENOTDIR';
  41. const EMFILE = 'EMFILE';
  42. const EACCES = 'EACCES';
  43. const EISDIR = 'EISDIR';
  44. const ENOTEMPTY = 'ENOTEMPTY';
  45. const ENOSYS = 'ENOSYS';
  46. const ERR_FS_EISDIR = 'ERR_FS_EISDIR';
  47. const ERR_OUT_OF_RANGE = 'ERR_OUT_OF_RANGE';
  48. let resolve = (filename, base = process_1.default.cwd()) => resolveCrossPlatform(base, filename);
  49. if (util_1.isWin) {
  50. const _resolve = resolve;
  51. resolve = (filename, base) => (0, util_1.unixify)(_resolve(filename, base));
  52. }
  53. function filenameToSteps(filename, base) {
  54. const fullPath = resolve(filename, base);
  55. const fullPathSansSlash = fullPath.substring(1);
  56. if (!fullPathSansSlash)
  57. return [];
  58. return fullPathSansSlash.split(sep);
  59. }
  60. function pathToSteps(path) {
  61. return filenameToSteps((0, util_1.pathToFilename)(path));
  62. }
  63. function dataToStr(data, encoding = encoding_1.ENCODING_UTF8) {
  64. if (buffer_1.Buffer.isBuffer(data))
  65. return data.toString(encoding);
  66. else if (data instanceof Uint8Array)
  67. return (0, buffer_1.bufferFrom)(data).toString(encoding);
  68. else
  69. return String(data);
  70. }
  71. // converts Date or number to a fractional UNIX timestamp
  72. function toUnixTimestamp(time) {
  73. // tslint:disable-next-line triple-equals
  74. if (typeof time === 'string' && +time == time) {
  75. return +time;
  76. }
  77. if (time instanceof Date) {
  78. return time.getTime() / 1000;
  79. }
  80. if (isFinite(time)) {
  81. if (time < 0) {
  82. return Date.now() / 1000;
  83. }
  84. return time;
  85. }
  86. throw new Error('Cannot parse time: ' + time);
  87. }
  88. function validateUid(uid) {
  89. if (typeof uid !== 'number')
  90. throw TypeError(constants_2.ERRSTR.UID);
  91. }
  92. function validateGid(gid) {
  93. if (typeof gid !== 'number')
  94. throw TypeError(constants_2.ERRSTR.GID);
  95. }
  96. function flattenJSON(nestedJSON) {
  97. const flatJSON = {};
  98. function flatten(pathPrefix, node) {
  99. for (const path in node) {
  100. const contentOrNode = node[path];
  101. const joinedPath = join(pathPrefix, path);
  102. if (typeof contentOrNode === 'string' || contentOrNode instanceof buffer_1.Buffer) {
  103. flatJSON[joinedPath] = contentOrNode;
  104. }
  105. else if (typeof contentOrNode === 'object' && contentOrNode !== null && Object.keys(contentOrNode).length > 0) {
  106. // empty directories need an explicit entry and therefore get handled in `else`, non-empty ones are implicitly considered
  107. flatten(joinedPath, contentOrNode);
  108. }
  109. else {
  110. // without this branch null, empty-object or non-object entries would not be handled in the same way
  111. // by both fromJSON() and fromNestedJSON()
  112. flatJSON[joinedPath] = null;
  113. }
  114. }
  115. }
  116. flatten('', nestedJSON);
  117. return flatJSON;
  118. }
  119. const notImplemented = () => {
  120. throw new Error('Not implemented');
  121. };
  122. /**
  123. * `Volume` represents a file system.
  124. */
  125. class Volume {
  126. static fromJSON(json, cwd) {
  127. const vol = new Volume();
  128. vol.fromJSON(json, cwd);
  129. return vol;
  130. }
  131. static fromNestedJSON(json, cwd) {
  132. const vol = new Volume();
  133. vol.fromNestedJSON(json, cwd);
  134. return vol;
  135. }
  136. get promises() {
  137. if (this.promisesApi === null)
  138. throw new Error('Promise is not supported in this environment.');
  139. return this.promisesApi;
  140. }
  141. constructor(props = {}) {
  142. // I-node number counter.
  143. this.ino = 0;
  144. // A mapping for i-node numbers to i-nodes (`Node`);
  145. this.inodes = {};
  146. // List of released i-node numbers, for reuse.
  147. this.releasedInos = [];
  148. // A mapping for file descriptors to `File`s.
  149. this.fds = {};
  150. // A list of reusable (opened and closed) file descriptors, that should be
  151. // used first before creating a new file descriptor.
  152. this.releasedFds = [];
  153. // Max number of open files.
  154. this.maxFiles = 10000;
  155. // Current number of open files.
  156. this.openFiles = 0;
  157. this.promisesApi = new FsPromises_1.FsPromises(this, FileHandle_1.FileHandle);
  158. this.statWatchers = {};
  159. this.cpSync = notImplemented;
  160. this.lutimesSync = notImplemented;
  161. this.statfsSync = notImplemented;
  162. this.cp = notImplemented;
  163. this.lutimes = notImplemented;
  164. this.statfs = notImplemented;
  165. this.openAsBlob = notImplemented;
  166. this.props = Object.assign({ Node: node_1.Node, Link: node_1.Link, File: node_1.File }, props);
  167. const root = this.createLink();
  168. root.setNode(this.createNode(true));
  169. const self = this; // tslint:disable-line no-this-assignment
  170. this.StatWatcher = class extends StatWatcher {
  171. constructor() {
  172. super(self);
  173. }
  174. };
  175. const _ReadStream = FsReadStream;
  176. this.ReadStream = class extends _ReadStream {
  177. constructor(...args) {
  178. super(self, ...args);
  179. }
  180. };
  181. const _WriteStream = FsWriteStream;
  182. this.WriteStream = class extends _WriteStream {
  183. constructor(...args) {
  184. super(self, ...args);
  185. }
  186. };
  187. this.FSWatcher = class extends FSWatcher {
  188. constructor() {
  189. super(self);
  190. }
  191. };
  192. root.setChild('.', root);
  193. root.getNode().nlink++;
  194. root.setChild('..', root);
  195. root.getNode().nlink++;
  196. this.root = root;
  197. }
  198. createLink(parent, name, isDirectory = false, perm) {
  199. if (!parent) {
  200. return new this.props.Link(this, null, '');
  201. }
  202. if (!name) {
  203. throw new Error('createLink: name cannot be empty');
  204. }
  205. return parent.createChild(name, this.createNode(isDirectory, perm));
  206. }
  207. deleteLink(link) {
  208. const parent = link.parent;
  209. if (parent) {
  210. parent.deleteChild(link);
  211. return true;
  212. }
  213. return false;
  214. }
  215. newInoNumber() {
  216. const releasedFd = this.releasedInos.pop();
  217. if (releasedFd)
  218. return releasedFd;
  219. else {
  220. this.ino = (this.ino + 1) % 0xffffffff;
  221. return this.ino;
  222. }
  223. }
  224. newFdNumber() {
  225. const releasedFd = this.releasedFds.pop();
  226. return typeof releasedFd === 'number' ? releasedFd : Volume.fd--;
  227. }
  228. createNode(isDirectory = false, perm) {
  229. const node = new this.props.Node(this.newInoNumber(), perm);
  230. if (isDirectory)
  231. node.setIsDirectory();
  232. this.inodes[node.ino] = node;
  233. return node;
  234. }
  235. deleteNode(node) {
  236. node.del();
  237. delete this.inodes[node.ino];
  238. this.releasedInos.push(node.ino);
  239. }
  240. // Returns a `Link` (hard link) referenced by path "split" into steps.
  241. getLink(steps) {
  242. return this.root.walk(steps);
  243. }
  244. // Just link `getLink`, but throws a correct user error, if link to found.
  245. getLinkOrThrow(filename, funcName) {
  246. const steps = filenameToSteps(filename);
  247. const link = this.getLink(steps);
  248. if (!link)
  249. throw (0, util_1.createError)(ENOENT, funcName, filename);
  250. return link;
  251. }
  252. // Just like `getLink`, but also dereference/resolves symbolic links.
  253. getResolvedLink(filenameOrSteps) {
  254. let steps = typeof filenameOrSteps === 'string' ? filenameToSteps(filenameOrSteps) : filenameOrSteps;
  255. let link = this.root;
  256. let i = 0;
  257. while (i < steps.length) {
  258. const step = steps[i];
  259. link = link.getChild(step);
  260. if (!link)
  261. return null;
  262. const node = link.getNode();
  263. if (node.isSymlink()) {
  264. steps = node.symlink.concat(steps.slice(i + 1));
  265. link = this.root;
  266. i = 0;
  267. continue;
  268. }
  269. i++;
  270. }
  271. return link;
  272. }
  273. // Just like `getLinkOrThrow`, but also dereference/resolves symbolic links.
  274. getResolvedLinkOrThrow(filename, funcName) {
  275. const link = this.getResolvedLink(filename);
  276. if (!link)
  277. throw (0, util_1.createError)(ENOENT, funcName, filename);
  278. return link;
  279. }
  280. resolveSymlinks(link) {
  281. // let node: Node = link.getNode();
  282. // while(link && node.isSymlink()) {
  283. // link = this.getLink(node.symlink);
  284. // if(!link) return null;
  285. // node = link.getNode();
  286. // }
  287. // return link;
  288. return this.getResolvedLink(link.steps.slice(1));
  289. }
  290. // Just like `getLinkOrThrow`, but also verifies that the link is a directory.
  291. getLinkAsDirOrThrow(filename, funcName) {
  292. const link = this.getLinkOrThrow(filename, funcName);
  293. if (!link.getNode().isDirectory())
  294. throw (0, util_1.createError)(ENOTDIR, funcName, filename);
  295. return link;
  296. }
  297. // Get the immediate parent directory of the link.
  298. getLinkParent(steps) {
  299. return this.root.walk(steps, steps.length - 1);
  300. }
  301. getLinkParentAsDirOrThrow(filenameOrSteps, funcName) {
  302. const steps = filenameOrSteps instanceof Array ? filenameOrSteps : filenameToSteps(filenameOrSteps);
  303. const link = this.getLinkParent(steps);
  304. if (!link)
  305. throw (0, util_1.createError)(ENOENT, funcName, sep + steps.join(sep));
  306. if (!link.getNode().isDirectory())
  307. throw (0, util_1.createError)(ENOTDIR, funcName, sep + steps.join(sep));
  308. return link;
  309. }
  310. getFileByFd(fd) {
  311. return this.fds[String(fd)];
  312. }
  313. getFileByFdOrThrow(fd, funcName) {
  314. if (!(0, util_1.isFd)(fd))
  315. throw TypeError(constants_2.ERRSTR.FD);
  316. const file = this.getFileByFd(fd);
  317. if (!file)
  318. throw (0, util_1.createError)(EBADF, funcName);
  319. return file;
  320. }
  321. /**
  322. * @todo This is not used anymore. Remove.
  323. */
  324. /*
  325. private getNodeByIdOrCreate(id: TFileId, flags: number, perm: number): Node {
  326. if (typeof id === 'number') {
  327. const file = this.getFileByFd(id);
  328. if (!file) throw Error('File nto found');
  329. return file.node;
  330. } else {
  331. const steps = pathToSteps(id as PathLike);
  332. let link = this.getLink(steps);
  333. if (link) return link.getNode();
  334. // Try creating a node if not found.
  335. if (flags & O_CREAT) {
  336. const dirLink = this.getLinkParent(steps);
  337. if (dirLink) {
  338. const name = steps[steps.length - 1];
  339. link = this.createLink(dirLink, name, false, perm);
  340. return link.getNode();
  341. }
  342. }
  343. throw createError(ENOENT, 'getNodeByIdOrCreate', pathToFilename(id));
  344. }
  345. }
  346. */
  347. wrapAsync(method, args, callback) {
  348. (0, util_1.validateCallback)(callback);
  349. (0, setImmediate_1.default)(() => {
  350. let result;
  351. try {
  352. result = method.apply(this, args);
  353. }
  354. catch (err) {
  355. callback(err);
  356. return;
  357. }
  358. callback(null, result);
  359. });
  360. }
  361. _toJSON(link = this.root, json = {}, path, asBuffer) {
  362. let isEmpty = true;
  363. let children = link.children;
  364. if (link.getNode().isFile()) {
  365. children = new Map([[link.getName(), link.parent.getChild(link.getName())]]);
  366. link = link.parent;
  367. }
  368. for (const name of children.keys()) {
  369. if (name === '.' || name === '..') {
  370. continue;
  371. }
  372. isEmpty = false;
  373. const child = link.getChild(name);
  374. if (!child) {
  375. throw new Error('_toJSON: unexpected undefined');
  376. }
  377. const node = child.getNode();
  378. if (node.isFile()) {
  379. let filename = child.getPath();
  380. if (path)
  381. filename = relative(path, filename);
  382. json[filename] = asBuffer ? node.getBuffer() : node.getString();
  383. }
  384. else if (node.isDirectory()) {
  385. this._toJSON(child, json, path, asBuffer);
  386. }
  387. }
  388. let dirPath = link.getPath();
  389. if (path)
  390. dirPath = relative(path, dirPath);
  391. if (dirPath && isEmpty) {
  392. json[dirPath] = null;
  393. }
  394. return json;
  395. }
  396. toJSON(paths, json = {}, isRelative = false, asBuffer = false) {
  397. const links = [];
  398. if (paths) {
  399. if (!Array.isArray(paths))
  400. paths = [paths];
  401. for (const path of paths) {
  402. const filename = (0, util_1.pathToFilename)(path);
  403. const link = this.getResolvedLink(filename);
  404. if (!link)
  405. continue;
  406. links.push(link);
  407. }
  408. }
  409. else {
  410. links.push(this.root);
  411. }
  412. if (!links.length)
  413. return json;
  414. for (const link of links)
  415. this._toJSON(link, json, isRelative ? link.getPath() : '', asBuffer);
  416. return json;
  417. }
  418. // TODO: `cwd` should probably not invoke `process.cwd()`.
  419. fromJSON(json, cwd = process_1.default.cwd()) {
  420. for (let filename in json) {
  421. const data = json[filename];
  422. filename = resolve(filename, cwd);
  423. if (typeof data === 'string' || data instanceof buffer_1.Buffer) {
  424. const dir = dirname(filename);
  425. this.mkdirpBase(dir, 511 /* MODE.DIR */);
  426. this.writeFileSync(filename, data);
  427. }
  428. else {
  429. this.mkdirpBase(filename, 511 /* MODE.DIR */);
  430. }
  431. }
  432. }
  433. fromNestedJSON(json, cwd) {
  434. this.fromJSON(flattenJSON(json), cwd);
  435. }
  436. toTree(opts = { separator: sep }) {
  437. return (0, print_1.toTreeSync)(this, opts);
  438. }
  439. reset() {
  440. this.ino = 0;
  441. this.inodes = {};
  442. this.releasedInos = [];
  443. this.fds = {};
  444. this.releasedFds = [];
  445. this.openFiles = 0;
  446. this.root = this.createLink();
  447. this.root.setNode(this.createNode(true));
  448. }
  449. // Legacy interface
  450. mountSync(mountpoint, json) {
  451. this.fromJSON(json, mountpoint);
  452. }
  453. openLink(link, flagsNum, resolveSymlinks = true) {
  454. if (this.openFiles >= this.maxFiles) {
  455. // Too many open files.
  456. throw (0, util_1.createError)(EMFILE, 'open', link.getPath());
  457. }
  458. // Resolve symlinks.
  459. let realLink = link;
  460. if (resolveSymlinks)
  461. realLink = this.resolveSymlinks(link);
  462. if (!realLink)
  463. throw (0, util_1.createError)(ENOENT, 'open', link.getPath());
  464. const node = realLink.getNode();
  465. // Check whether node is a directory
  466. if (node.isDirectory()) {
  467. if ((flagsNum & (O_RDONLY | O_RDWR | O_WRONLY)) !== O_RDONLY)
  468. throw (0, util_1.createError)(EISDIR, 'open', link.getPath());
  469. }
  470. else {
  471. if (flagsNum & O_DIRECTORY)
  472. throw (0, util_1.createError)(ENOTDIR, 'open', link.getPath());
  473. }
  474. // Check node permissions
  475. if (!(flagsNum & O_WRONLY)) {
  476. if (!node.canRead()) {
  477. throw (0, util_1.createError)(EACCES, 'open', link.getPath());
  478. }
  479. }
  480. if (flagsNum & O_RDWR) {
  481. }
  482. const file = new this.props.File(link, node, flagsNum, this.newFdNumber());
  483. this.fds[file.fd] = file;
  484. this.openFiles++;
  485. if (flagsNum & O_TRUNC)
  486. file.truncate();
  487. return file;
  488. }
  489. openFile(filename, flagsNum, modeNum, resolveSymlinks = true) {
  490. const steps = filenameToSteps(filename);
  491. let link = resolveSymlinks ? this.getResolvedLink(steps) : this.getLink(steps);
  492. if (link && flagsNum & O_EXCL)
  493. throw (0, util_1.createError)(EEXIST, 'open', filename);
  494. // Try creating a new file, if it does not exist.
  495. if (!link && flagsNum & O_CREAT) {
  496. // const dirLink: Link = this.getLinkParent(steps);
  497. const dirLink = this.getResolvedLink(steps.slice(0, steps.length - 1));
  498. // if(!dirLink) throw createError(ENOENT, 'open', filename);
  499. if (!dirLink)
  500. throw (0, util_1.createError)(ENOENT, 'open', sep + steps.join(sep));
  501. if (flagsNum & O_CREAT && typeof modeNum === 'number') {
  502. link = this.createLink(dirLink, steps[steps.length - 1], false, modeNum);
  503. }
  504. }
  505. if (link)
  506. return this.openLink(link, flagsNum, resolveSymlinks);
  507. throw (0, util_1.createError)(ENOENT, 'open', filename);
  508. }
  509. openBase(filename, flagsNum, modeNum, resolveSymlinks = true) {
  510. const file = this.openFile(filename, flagsNum, modeNum, resolveSymlinks);
  511. if (!file)
  512. throw (0, util_1.createError)(ENOENT, 'open', filename);
  513. return file.fd;
  514. }
  515. openSync(path, flags, mode = 438 /* MODE.DEFAULT */) {
  516. // Validate (1) mode; (2) path; (3) flags - in that order.
  517. const modeNum = (0, util_1.modeToNumber)(mode);
  518. const fileName = (0, util_1.pathToFilename)(path);
  519. const flagsNum = (0, util_1.flagsToNumber)(flags);
  520. return this.openBase(fileName, flagsNum, modeNum, !(flagsNum & O_SYMLINK));
  521. }
  522. open(path, flags, a, b) {
  523. let mode = a;
  524. let callback = b;
  525. if (typeof a === 'function') {
  526. mode = 438 /* MODE.DEFAULT */;
  527. callback = a;
  528. }
  529. mode = mode || 438 /* MODE.DEFAULT */;
  530. const modeNum = (0, util_1.modeToNumber)(mode);
  531. const fileName = (0, util_1.pathToFilename)(path);
  532. const flagsNum = (0, util_1.flagsToNumber)(flags);
  533. this.wrapAsync(this.openBase, [fileName, flagsNum, modeNum, !(flagsNum & O_SYMLINK)], callback);
  534. }
  535. closeFile(file) {
  536. if (!this.fds[file.fd])
  537. return;
  538. this.openFiles--;
  539. delete this.fds[file.fd];
  540. this.releasedFds.push(file.fd);
  541. }
  542. closeSync(fd) {
  543. (0, util_1.validateFd)(fd);
  544. const file = this.getFileByFdOrThrow(fd, 'close');
  545. this.closeFile(file);
  546. }
  547. close(fd, callback) {
  548. (0, util_1.validateFd)(fd);
  549. this.wrapAsync(this.closeSync, [fd], callback);
  550. }
  551. openFileOrGetById(id, flagsNum, modeNum) {
  552. if (typeof id === 'number') {
  553. const file = this.fds[id];
  554. if (!file)
  555. throw (0, util_1.createError)(ENOENT);
  556. return file;
  557. }
  558. else {
  559. return this.openFile((0, util_1.pathToFilename)(id), flagsNum, modeNum);
  560. }
  561. }
  562. readBase(fd, buffer, offset, length, position) {
  563. if (buffer.byteLength < length) {
  564. throw (0, util_1.createError)(ERR_OUT_OF_RANGE, 'read', undefined, undefined, RangeError);
  565. }
  566. const file = this.getFileByFdOrThrow(fd);
  567. if (file.node.isSymlink()) {
  568. throw (0, util_1.createError)(EPERM, 'read', file.link.getPath());
  569. }
  570. return file.read(buffer, Number(offset), Number(length), position === -1 || typeof position !== 'number' ? undefined : position);
  571. }
  572. readSync(fd, buffer, offset, length, position) {
  573. (0, util_1.validateFd)(fd);
  574. return this.readBase(fd, buffer, offset, length, position);
  575. }
  576. read(fd, buffer, offset, length, position, callback) {
  577. (0, util_1.validateCallback)(callback);
  578. // This `if` branch is from Node.js
  579. if (length === 0) {
  580. return (0, queueMicrotask_1.default)(() => {
  581. if (callback)
  582. callback(null, 0, buffer);
  583. });
  584. }
  585. (0, setImmediate_1.default)(() => {
  586. try {
  587. const bytes = this.readBase(fd, buffer, offset, length, position);
  588. callback(null, bytes, buffer);
  589. }
  590. catch (err) {
  591. callback(err);
  592. }
  593. });
  594. }
  595. readvBase(fd, buffers, position) {
  596. const file = this.getFileByFdOrThrow(fd);
  597. let p = position !== null && position !== void 0 ? position : undefined;
  598. if (p === -1) {
  599. p = undefined;
  600. }
  601. let bytesRead = 0;
  602. for (const buffer of buffers) {
  603. const bytes = file.read(buffer, 0, buffer.byteLength, p);
  604. p = undefined;
  605. bytesRead += bytes;
  606. if (bytes < buffer.byteLength)
  607. break;
  608. }
  609. return bytesRead;
  610. }
  611. readv(fd, buffers, a, b) {
  612. let position = a;
  613. let callback = b;
  614. if (typeof a === 'function') {
  615. position = null;
  616. callback = a;
  617. }
  618. (0, util_1.validateCallback)(callback);
  619. (0, setImmediate_1.default)(() => {
  620. try {
  621. const bytes = this.readvBase(fd, buffers, position);
  622. callback(null, bytes, buffers);
  623. }
  624. catch (err) {
  625. callback(err);
  626. }
  627. });
  628. }
  629. readvSync(fd, buffers, position) {
  630. (0, util_1.validateFd)(fd);
  631. return this.readvBase(fd, buffers, position);
  632. }
  633. readFileBase(id, flagsNum, encoding) {
  634. let result;
  635. const isUserFd = typeof id === 'number';
  636. const userOwnsFd = isUserFd && (0, util_1.isFd)(id);
  637. let fd;
  638. if (userOwnsFd)
  639. fd = id;
  640. else {
  641. const filename = (0, util_1.pathToFilename)(id);
  642. const steps = filenameToSteps(filename);
  643. const link = this.getResolvedLink(steps);
  644. if (link) {
  645. const node = link.getNode();
  646. if (node.isDirectory())
  647. throw (0, util_1.createError)(EISDIR, 'open', link.getPath());
  648. }
  649. fd = this.openSync(id, flagsNum);
  650. }
  651. try {
  652. result = (0, util_1.bufferToEncoding)(this.getFileByFdOrThrow(fd).getBuffer(), encoding);
  653. }
  654. finally {
  655. if (!userOwnsFd) {
  656. this.closeSync(fd);
  657. }
  658. }
  659. return result;
  660. }
  661. readFileSync(file, options) {
  662. const opts = (0, options_1.getReadFileOptions)(options);
  663. const flagsNum = (0, util_1.flagsToNumber)(opts.flag);
  664. return this.readFileBase(file, flagsNum, opts.encoding);
  665. }
  666. readFile(id, a, b) {
  667. const [opts, callback] = (0, options_1.optsAndCbGenerator)(options_1.getReadFileOptions)(a, b);
  668. const flagsNum = (0, util_1.flagsToNumber)(opts.flag);
  669. this.wrapAsync(this.readFileBase, [id, flagsNum, opts.encoding], callback);
  670. }
  671. writeBase(fd, buf, offset, length, position) {
  672. const file = this.getFileByFdOrThrow(fd, 'write');
  673. if (file.node.isSymlink()) {
  674. throw (0, util_1.createError)(EBADF, 'write', file.link.getPath());
  675. }
  676. return file.write(buf, offset, length, position === -1 || typeof position !== 'number' ? undefined : position);
  677. }
  678. writeSync(fd, a, b, c, d) {
  679. const [, buf, offset, length, position] = (0, util_1.getWriteSyncArgs)(fd, a, b, c, d);
  680. return this.writeBase(fd, buf, offset, length, position);
  681. }
  682. write(fd, a, b, c, d, e) {
  683. const [, asStr, buf, offset, length, position, cb] = (0, util_1.getWriteArgs)(fd, a, b, c, d, e);
  684. (0, setImmediate_1.default)(() => {
  685. try {
  686. const bytes = this.writeBase(fd, buf, offset, length, position);
  687. if (!asStr) {
  688. cb(null, bytes, buf);
  689. }
  690. else {
  691. cb(null, bytes, a);
  692. }
  693. }
  694. catch (err) {
  695. cb(err);
  696. }
  697. });
  698. }
  699. writevBase(fd, buffers, position) {
  700. const file = this.getFileByFdOrThrow(fd);
  701. let p = position !== null && position !== void 0 ? position : undefined;
  702. if (p === -1) {
  703. p = undefined;
  704. }
  705. let bytesWritten = 0;
  706. for (const buffer of buffers) {
  707. const nodeBuf = buffer_1.Buffer.from(buffer.buffer, buffer.byteOffset, buffer.byteLength);
  708. const bytes = file.write(nodeBuf, 0, nodeBuf.byteLength, p);
  709. p = undefined;
  710. bytesWritten += bytes;
  711. if (bytes < nodeBuf.byteLength)
  712. break;
  713. }
  714. return bytesWritten;
  715. }
  716. writev(fd, buffers, a, b) {
  717. let position = a;
  718. let callback = b;
  719. if (typeof a === 'function') {
  720. position = null;
  721. callback = a;
  722. }
  723. (0, util_1.validateCallback)(callback);
  724. (0, setImmediate_1.default)(() => {
  725. try {
  726. const bytes = this.writevBase(fd, buffers, position);
  727. callback(null, bytes, buffers);
  728. }
  729. catch (err) {
  730. callback(err);
  731. }
  732. });
  733. }
  734. writevSync(fd, buffers, position) {
  735. (0, util_1.validateFd)(fd);
  736. return this.writevBase(fd, buffers, position);
  737. }
  738. writeFileBase(id, buf, flagsNum, modeNum) {
  739. // console.log('writeFileBase', id, buf, flagsNum, modeNum);
  740. // const node = this.getNodeByIdOrCreate(id, flagsNum, modeNum);
  741. // node.setBuffer(buf);
  742. const isUserFd = typeof id === 'number';
  743. let fd;
  744. if (isUserFd)
  745. fd = id;
  746. else {
  747. fd = this.openBase((0, util_1.pathToFilename)(id), flagsNum, modeNum);
  748. // fd = this.openSync(id as PathLike, flagsNum, modeNum);
  749. }
  750. let offset = 0;
  751. let length = buf.length;
  752. let position = flagsNum & O_APPEND ? undefined : 0;
  753. try {
  754. while (length > 0) {
  755. const written = this.writeSync(fd, buf, offset, length, position);
  756. offset += written;
  757. length -= written;
  758. if (position !== undefined)
  759. position += written;
  760. }
  761. }
  762. finally {
  763. if (!isUserFd)
  764. this.closeSync(fd);
  765. }
  766. }
  767. writeFileSync(id, data, options) {
  768. const opts = (0, options_1.getWriteFileOptions)(options);
  769. const flagsNum = (0, util_1.flagsToNumber)(opts.flag);
  770. const modeNum = (0, util_1.modeToNumber)(opts.mode);
  771. const buf = (0, util_1.dataToBuffer)(data, opts.encoding);
  772. this.writeFileBase(id, buf, flagsNum, modeNum);
  773. }
  774. writeFile(id, data, a, b) {
  775. let options = a;
  776. let callback = b;
  777. if (typeof a === 'function') {
  778. options = options_1.writeFileDefaults;
  779. callback = a;
  780. }
  781. const cb = (0, util_1.validateCallback)(callback);
  782. const opts = (0, options_1.getWriteFileOptions)(options);
  783. const flagsNum = (0, util_1.flagsToNumber)(opts.flag);
  784. const modeNum = (0, util_1.modeToNumber)(opts.mode);
  785. const buf = (0, util_1.dataToBuffer)(data, opts.encoding);
  786. this.wrapAsync(this.writeFileBase, [id, buf, flagsNum, modeNum], cb);
  787. }
  788. linkBase(filename1, filename2) {
  789. const steps1 = filenameToSteps(filename1);
  790. const link1 = this.getLink(steps1);
  791. if (!link1)
  792. throw (0, util_1.createError)(ENOENT, 'link', filename1, filename2);
  793. const steps2 = filenameToSteps(filename2);
  794. // Check new link directory exists.
  795. const dir2 = this.getLinkParent(steps2);
  796. if (!dir2)
  797. throw (0, util_1.createError)(ENOENT, 'link', filename1, filename2);
  798. const name = steps2[steps2.length - 1];
  799. // Check if new file already exists.
  800. if (dir2.getChild(name))
  801. throw (0, util_1.createError)(EEXIST, 'link', filename1, filename2);
  802. const node = link1.getNode();
  803. node.nlink++;
  804. dir2.createChild(name, node);
  805. }
  806. copyFileBase(src, dest, flags) {
  807. const buf = this.readFileSync(src);
  808. if (flags & COPYFILE_EXCL) {
  809. if (this.existsSync(dest)) {
  810. throw (0, util_1.createError)(EEXIST, 'copyFile', src, dest);
  811. }
  812. }
  813. if (flags & COPYFILE_FICLONE_FORCE) {
  814. throw (0, util_1.createError)(ENOSYS, 'copyFile', src, dest);
  815. }
  816. this.writeFileBase(dest, buf, constants_2.FLAGS.w, 438 /* MODE.DEFAULT */);
  817. }
  818. copyFileSync(src, dest, flags) {
  819. const srcFilename = (0, util_1.pathToFilename)(src);
  820. const destFilename = (0, util_1.pathToFilename)(dest);
  821. return this.copyFileBase(srcFilename, destFilename, (flags || 0) | 0);
  822. }
  823. copyFile(src, dest, a, b) {
  824. const srcFilename = (0, util_1.pathToFilename)(src);
  825. const destFilename = (0, util_1.pathToFilename)(dest);
  826. let flags;
  827. let callback;
  828. if (typeof a === 'function') {
  829. flags = 0;
  830. callback = a;
  831. }
  832. else {
  833. flags = a;
  834. callback = b;
  835. }
  836. (0, util_1.validateCallback)(callback);
  837. this.wrapAsync(this.copyFileBase, [srcFilename, destFilename, flags], callback);
  838. }
  839. linkSync(existingPath, newPath) {
  840. const existingPathFilename = (0, util_1.pathToFilename)(existingPath);
  841. const newPathFilename = (0, util_1.pathToFilename)(newPath);
  842. this.linkBase(existingPathFilename, newPathFilename);
  843. }
  844. link(existingPath, newPath, callback) {
  845. const existingPathFilename = (0, util_1.pathToFilename)(existingPath);
  846. const newPathFilename = (0, util_1.pathToFilename)(newPath);
  847. this.wrapAsync(this.linkBase, [existingPathFilename, newPathFilename], callback);
  848. }
  849. unlinkBase(filename) {
  850. const steps = filenameToSteps(filename);
  851. const link = this.getLink(steps);
  852. if (!link)
  853. throw (0, util_1.createError)(ENOENT, 'unlink', filename);
  854. // TODO: Check if it is file, dir, other...
  855. if (link.length)
  856. throw Error('Dir not empty...');
  857. this.deleteLink(link);
  858. const node = link.getNode();
  859. node.nlink--;
  860. // When all hard links to i-node are deleted, remove the i-node, too.
  861. if (node.nlink <= 0) {
  862. this.deleteNode(node);
  863. }
  864. }
  865. unlinkSync(path) {
  866. const filename = (0, util_1.pathToFilename)(path);
  867. this.unlinkBase(filename);
  868. }
  869. unlink(path, callback) {
  870. const filename = (0, util_1.pathToFilename)(path);
  871. this.wrapAsync(this.unlinkBase, [filename], callback);
  872. }
  873. symlinkBase(targetFilename, pathFilename) {
  874. const pathSteps = filenameToSteps(pathFilename);
  875. // Check if directory exists, where we about to create a symlink.
  876. const dirLink = this.getLinkParent(pathSteps);
  877. if (!dirLink)
  878. throw (0, util_1.createError)(ENOENT, 'symlink', targetFilename, pathFilename);
  879. const name = pathSteps[pathSteps.length - 1];
  880. // Check if new file already exists.
  881. if (dirLink.getChild(name))
  882. throw (0, util_1.createError)(EEXIST, 'symlink', targetFilename, pathFilename);
  883. // Create symlink.
  884. const symlink = dirLink.createChild(name);
  885. symlink.getNode().makeSymlink(filenameToSteps(targetFilename));
  886. return symlink;
  887. }
  888. // `type` argument works only on Windows.
  889. symlinkSync(target, path, type) {
  890. const targetFilename = (0, util_1.pathToFilename)(target);
  891. const pathFilename = (0, util_1.pathToFilename)(path);
  892. this.symlinkBase(targetFilename, pathFilename);
  893. }
  894. symlink(target, path, a, b) {
  895. const callback = (0, util_1.validateCallback)(typeof a === 'function' ? a : b);
  896. const targetFilename = (0, util_1.pathToFilename)(target);
  897. const pathFilename = (0, util_1.pathToFilename)(path);
  898. this.wrapAsync(this.symlinkBase, [targetFilename, pathFilename], callback);
  899. }
  900. realpathBase(filename, encoding) {
  901. const steps = filenameToSteps(filename);
  902. const realLink = this.getResolvedLink(steps);
  903. if (!realLink)
  904. throw (0, util_1.createError)(ENOENT, 'realpath', filename);
  905. return (0, encoding_1.strToEncoding)(realLink.getPath() || '/', encoding);
  906. }
  907. realpathSync(path, options) {
  908. return this.realpathBase((0, util_1.pathToFilename)(path), (0, options_1.getRealpathOptions)(options).encoding);
  909. }
  910. realpath(path, a, b) {
  911. const [opts, callback] = (0, options_1.getRealpathOptsAndCb)(a, b);
  912. const pathFilename = (0, util_1.pathToFilename)(path);
  913. this.wrapAsync(this.realpathBase, [pathFilename, opts.encoding], callback);
  914. }
  915. lstatBase(filename, bigint = false, throwIfNoEntry = false) {
  916. const link = this.getLink(filenameToSteps(filename));
  917. if (link) {
  918. return Stats_1.default.build(link.getNode(), bigint);
  919. }
  920. else if (!throwIfNoEntry) {
  921. return undefined;
  922. }
  923. else {
  924. throw (0, util_1.createError)(ENOENT, 'lstat', filename);
  925. }
  926. }
  927. lstatSync(path, options) {
  928. const { throwIfNoEntry = true, bigint = false } = (0, options_1.getStatOptions)(options);
  929. return this.lstatBase((0, util_1.pathToFilename)(path), bigint, throwIfNoEntry);
  930. }
  931. lstat(path, a, b) {
  932. const [{ throwIfNoEntry = true, bigint = false }, callback] = (0, options_1.getStatOptsAndCb)(a, b);
  933. this.wrapAsync(this.lstatBase, [(0, util_1.pathToFilename)(path), bigint, throwIfNoEntry], callback);
  934. }
  935. statBase(filename, bigint = false, throwIfNoEntry = true) {
  936. const link = this.getResolvedLink(filenameToSteps(filename));
  937. if (link) {
  938. return Stats_1.default.build(link.getNode(), bigint);
  939. }
  940. else if (!throwIfNoEntry) {
  941. return undefined;
  942. }
  943. else {
  944. throw (0, util_1.createError)(ENOENT, 'stat', filename);
  945. }
  946. }
  947. statSync(path, options) {
  948. const { bigint = true, throwIfNoEntry = true } = (0, options_1.getStatOptions)(options);
  949. return this.statBase((0, util_1.pathToFilename)(path), bigint, throwIfNoEntry);
  950. }
  951. stat(path, a, b) {
  952. const [{ bigint = false, throwIfNoEntry = true }, callback] = (0, options_1.getStatOptsAndCb)(a, b);
  953. this.wrapAsync(this.statBase, [(0, util_1.pathToFilename)(path), bigint, throwIfNoEntry], callback);
  954. }
  955. fstatBase(fd, bigint = false) {
  956. const file = this.getFileByFd(fd);
  957. if (!file)
  958. throw (0, util_1.createError)(EBADF, 'fstat');
  959. return Stats_1.default.build(file.node, bigint);
  960. }
  961. fstatSync(fd, options) {
  962. return this.fstatBase(fd, (0, options_1.getStatOptions)(options).bigint);
  963. }
  964. fstat(fd, a, b) {
  965. const [opts, callback] = (0, options_1.getStatOptsAndCb)(a, b);
  966. this.wrapAsync(this.fstatBase, [fd, opts.bigint], callback);
  967. }
  968. renameBase(oldPathFilename, newPathFilename) {
  969. const link = this.getLink(filenameToSteps(oldPathFilename));
  970. if (!link)
  971. throw (0, util_1.createError)(ENOENT, 'rename', oldPathFilename, newPathFilename);
  972. // TODO: Check if it is directory, if non-empty, we cannot move it, right?
  973. const newPathSteps = filenameToSteps(newPathFilename);
  974. // Check directory exists for the new location.
  975. const newPathDirLink = this.getLinkParent(newPathSteps);
  976. if (!newPathDirLink)
  977. throw (0, util_1.createError)(ENOENT, 'rename', oldPathFilename, newPathFilename);
  978. // TODO: Also treat cases with directories and symbolic links.
  979. // TODO: See: http://man7.org/linux/man-pages/man2/rename.2.html
  980. // Remove hard link from old folder.
  981. const oldLinkParent = link.parent;
  982. if (oldLinkParent) {
  983. oldLinkParent.deleteChild(link);
  984. }
  985. // Rename should overwrite the new path, if that exists.
  986. const name = newPathSteps[newPathSteps.length - 1];
  987. link.name = name;
  988. link.steps = [...newPathDirLink.steps, name];
  989. newPathDirLink.setChild(link.getName(), link);
  990. }
  991. renameSync(oldPath, newPath) {
  992. const oldPathFilename = (0, util_1.pathToFilename)(oldPath);
  993. const newPathFilename = (0, util_1.pathToFilename)(newPath);
  994. this.renameBase(oldPathFilename, newPathFilename);
  995. }
  996. rename(oldPath, newPath, callback) {
  997. const oldPathFilename = (0, util_1.pathToFilename)(oldPath);
  998. const newPathFilename = (0, util_1.pathToFilename)(newPath);
  999. this.wrapAsync(this.renameBase, [oldPathFilename, newPathFilename], callback);
  1000. }
  1001. existsBase(filename) {
  1002. return !!this.statBase(filename);
  1003. }
  1004. existsSync(path) {
  1005. try {
  1006. return this.existsBase((0, util_1.pathToFilename)(path));
  1007. }
  1008. catch (err) {
  1009. return false;
  1010. }
  1011. }
  1012. exists(path, callback) {
  1013. const filename = (0, util_1.pathToFilename)(path);
  1014. if (typeof callback !== 'function')
  1015. throw Error(constants_2.ERRSTR.CB);
  1016. (0, setImmediate_1.default)(() => {
  1017. try {
  1018. callback(this.existsBase(filename));
  1019. }
  1020. catch (err) {
  1021. callback(false);
  1022. }
  1023. });
  1024. }
  1025. accessBase(filename, mode) {
  1026. const link = this.getLinkOrThrow(filename, 'access');
  1027. // TODO: Verify permissions
  1028. }
  1029. accessSync(path, mode = F_OK) {
  1030. const filename = (0, util_1.pathToFilename)(path);
  1031. mode = mode | 0;
  1032. this.accessBase(filename, mode);
  1033. }
  1034. access(path, a, b) {
  1035. let mode = F_OK;
  1036. let callback;
  1037. if (typeof a !== 'function') {
  1038. mode = a | 0; // cast to number
  1039. callback = (0, util_1.validateCallback)(b);
  1040. }
  1041. else {
  1042. callback = a;
  1043. }
  1044. const filename = (0, util_1.pathToFilename)(path);
  1045. this.wrapAsync(this.accessBase, [filename, mode], callback);
  1046. }
  1047. appendFileSync(id, data, options) {
  1048. const opts = (0, options_1.getAppendFileOpts)(options);
  1049. // force append behavior when using a supplied file descriptor
  1050. if (!opts.flag || (0, util_1.isFd)(id))
  1051. opts.flag = 'a';
  1052. this.writeFileSync(id, data, opts);
  1053. }
  1054. appendFile(id, data, a, b) {
  1055. const [opts, callback] = (0, options_1.getAppendFileOptsAndCb)(a, b);
  1056. // force append behavior when using a supplied file descriptor
  1057. if (!opts.flag || (0, util_1.isFd)(id))
  1058. opts.flag = 'a';
  1059. this.writeFile(id, data, opts, callback);
  1060. }
  1061. readdirBase(filename, options) {
  1062. const steps = filenameToSteps(filename);
  1063. const link = this.getResolvedLink(steps);
  1064. if (!link)
  1065. throw (0, util_1.createError)(ENOENT, 'readdir', filename);
  1066. const node = link.getNode();
  1067. if (!node.isDirectory())
  1068. throw (0, util_1.createError)(ENOTDIR, 'scandir', filename);
  1069. const list = []; // output list
  1070. for (const name of link.children.keys()) {
  1071. const child = link.getChild(name);
  1072. if (!child || name === '.' || name === '..')
  1073. continue;
  1074. list.push(Dirent_1.default.build(child, options.encoding));
  1075. // recursion
  1076. if (options.recursive && child.children.size) {
  1077. const recurseOptions = Object.assign(Object.assign({}, options), { recursive: true, withFileTypes: true });
  1078. const childList = this.readdirBase(child.getPath(), recurseOptions);
  1079. list.push(...childList);
  1080. }
  1081. }
  1082. if (!util_1.isWin && options.encoding !== 'buffer')
  1083. list.sort((a, b) => {
  1084. if (a.name < b.name)
  1085. return -1;
  1086. if (a.name > b.name)
  1087. return 1;
  1088. return 0;
  1089. });
  1090. if (options.withFileTypes)
  1091. return list;
  1092. let filename2 = filename;
  1093. if (util_1.isWin) {
  1094. filename2 = filename2.replace(/\\/g, '/');
  1095. }
  1096. return list.map(dirent => {
  1097. if (options.recursive) {
  1098. let fullPath = pathModule.join(dirent.path, dirent.name.toString());
  1099. if (util_1.isWin) {
  1100. fullPath = fullPath.replace(/\\/g, '/');
  1101. }
  1102. return fullPath.replace(filename2 + pathModule.posix.sep, '');
  1103. }
  1104. return dirent.name;
  1105. });
  1106. }
  1107. readdirSync(path, options) {
  1108. const opts = (0, options_1.getReaddirOptions)(options);
  1109. const filename = (0, util_1.pathToFilename)(path);
  1110. return this.readdirBase(filename, opts);
  1111. }
  1112. readdir(path, a, b) {
  1113. const [options, callback] = (0, options_1.getReaddirOptsAndCb)(a, b);
  1114. const filename = (0, util_1.pathToFilename)(path);
  1115. this.wrapAsync(this.readdirBase, [filename, options], callback);
  1116. }
  1117. readlinkBase(filename, encoding) {
  1118. const link = this.getLinkOrThrow(filename, 'readlink');
  1119. const node = link.getNode();
  1120. if (!node.isSymlink())
  1121. throw (0, util_1.createError)(EINVAL, 'readlink', filename);
  1122. const str = sep + node.symlink.join(sep);
  1123. return (0, encoding_1.strToEncoding)(str, encoding);
  1124. }
  1125. readlinkSync(path, options) {
  1126. const opts = (0, options_1.getDefaultOpts)(options);
  1127. const filename = (0, util_1.pathToFilename)(path);
  1128. return this.readlinkBase(filename, opts.encoding);
  1129. }
  1130. readlink(path, a, b) {
  1131. const [opts, callback] = (0, options_1.getDefaultOptsAndCb)(a, b);
  1132. const filename = (0, util_1.pathToFilename)(path);
  1133. this.wrapAsync(this.readlinkBase, [filename, opts.encoding], callback);
  1134. }
  1135. fsyncBase(fd) {
  1136. this.getFileByFdOrThrow(fd, 'fsync');
  1137. }
  1138. fsyncSync(fd) {
  1139. this.fsyncBase(fd);
  1140. }
  1141. fsync(fd, callback) {
  1142. this.wrapAsync(this.fsyncBase, [fd], callback);
  1143. }
  1144. fdatasyncBase(fd) {
  1145. this.getFileByFdOrThrow(fd, 'fdatasync');
  1146. }
  1147. fdatasyncSync(fd) {
  1148. this.fdatasyncBase(fd);
  1149. }
  1150. fdatasync(fd, callback) {
  1151. this.wrapAsync(this.fdatasyncBase, [fd], callback);
  1152. }
  1153. ftruncateBase(fd, len) {
  1154. const file = this.getFileByFdOrThrow(fd, 'ftruncate');
  1155. file.truncate(len);
  1156. }
  1157. ftruncateSync(fd, len) {
  1158. this.ftruncateBase(fd, len);
  1159. }
  1160. ftruncate(fd, a, b) {
  1161. const len = typeof a === 'number' ? a : 0;
  1162. const callback = (0, util_1.validateCallback)(typeof a === 'number' ? b : a);
  1163. this.wrapAsync(this.ftruncateBase, [fd, len], callback);
  1164. }
  1165. truncateBase(path, len) {
  1166. const fd = this.openSync(path, 'r+');
  1167. try {
  1168. this.ftruncateSync(fd, len);
  1169. }
  1170. finally {
  1171. this.closeSync(fd);
  1172. }
  1173. }
  1174. /**
  1175. * `id` should be a file descriptor or a path. `id` as file descriptor will
  1176. * not be supported soon.
  1177. */
  1178. truncateSync(id, len) {
  1179. if ((0, util_1.isFd)(id))
  1180. return this.ftruncateSync(id, len);
  1181. this.truncateBase(id, len);
  1182. }
  1183. truncate(id, a, b) {
  1184. const len = typeof a === 'number' ? a : 0;
  1185. const callback = (0, util_1.validateCallback)(typeof a === 'number' ? b : a);
  1186. if ((0, util_1.isFd)(id))
  1187. return this.ftruncate(id, len, callback);
  1188. this.wrapAsync(this.truncateBase, [id, len], callback);
  1189. }
  1190. futimesBase(fd, atime, mtime) {
  1191. const file = this.getFileByFdOrThrow(fd, 'futimes');
  1192. const node = file.node;
  1193. node.atime = new Date(atime * 1000);
  1194. node.mtime = new Date(mtime * 1000);
  1195. }
  1196. futimesSync(fd, atime, mtime) {
  1197. this.futimesBase(fd, toUnixTimestamp(atime), toUnixTimestamp(mtime));
  1198. }
  1199. futimes(fd, atime, mtime, callback) {
  1200. this.wrapAsync(this.futimesBase, [fd, toUnixTimestamp(atime), toUnixTimestamp(mtime)], callback);
  1201. }
  1202. utimesBase(filename, atime, mtime) {
  1203. const fd = this.openSync(filename, 'r');
  1204. try {
  1205. this.futimesBase(fd, atime, mtime);
  1206. }
  1207. finally {
  1208. this.closeSync(fd);
  1209. }
  1210. }
  1211. utimesSync(path, atime, mtime) {
  1212. this.utimesBase((0, util_1.pathToFilename)(path), toUnixTimestamp(atime), toUnixTimestamp(mtime));
  1213. }
  1214. utimes(path, atime, mtime, callback) {
  1215. this.wrapAsync(this.utimesBase, [(0, util_1.pathToFilename)(path), toUnixTimestamp(atime), toUnixTimestamp(mtime)], callback);
  1216. }
  1217. mkdirBase(filename, modeNum) {
  1218. const steps = filenameToSteps(filename);
  1219. // This will throw if user tries to create root dir `fs.mkdirSync('/')`.
  1220. if (!steps.length) {
  1221. throw (0, util_1.createError)(EEXIST, 'mkdir', filename);
  1222. }
  1223. const dir = this.getLinkParentAsDirOrThrow(filename, 'mkdir');
  1224. // Check path already exists.
  1225. const name = steps[steps.length - 1];
  1226. if (dir.getChild(name))
  1227. throw (0, util_1.createError)(EEXIST, 'mkdir', filename);
  1228. dir.createChild(name, this.createNode(true, modeNum));
  1229. }
  1230. /**
  1231. * Creates directory tree recursively.
  1232. * @param filename
  1233. * @param modeNum
  1234. */
  1235. mkdirpBase(filename, modeNum) {
  1236. const fullPath = resolve(filename);
  1237. const fullPathSansSlash = fullPath.substring(1);
  1238. const steps = !fullPathSansSlash ? [] : fullPathSansSlash.split(sep);
  1239. let link = this.root;
  1240. let created = false;
  1241. for (let i = 0; i < steps.length; i++) {
  1242. const step = steps[i];
  1243. if (!link.getNode().isDirectory())
  1244. throw (0, util_1.createError)(ENOTDIR, 'mkdir', link.getPath());
  1245. const child = link.getChild(step);
  1246. if (child) {
  1247. if (child.getNode().isDirectory())
  1248. link = child;
  1249. else
  1250. throw (0, util_1.createError)(ENOTDIR, 'mkdir', child.getPath());
  1251. }
  1252. else {
  1253. link = link.createChild(step, this.createNode(true, modeNum));
  1254. created = true;
  1255. }
  1256. }
  1257. return created ? fullPath : undefined;
  1258. }
  1259. mkdirSync(path, options) {
  1260. const opts = (0, options_1.getMkdirOptions)(options);
  1261. const modeNum = (0, util_1.modeToNumber)(opts.mode, 0o777);
  1262. const filename = (0, util_1.pathToFilename)(path);
  1263. if (opts.recursive)
  1264. return this.mkdirpBase(filename, modeNum);
  1265. this.mkdirBase(filename, modeNum);
  1266. }
  1267. mkdir(path, a, b) {
  1268. const opts = (0, options_1.getMkdirOptions)(a);
  1269. const callback = (0, util_1.validateCallback)(typeof a === 'function' ? a : b);
  1270. const modeNum = (0, util_1.modeToNumber)(opts.mode, 0o777);
  1271. const filename = (0, util_1.pathToFilename)(path);
  1272. if (opts.recursive)
  1273. this.wrapAsync(this.mkdirpBase, [filename, modeNum], callback);
  1274. else
  1275. this.wrapAsync(this.mkdirBase, [filename, modeNum], callback);
  1276. }
  1277. mkdtempBase(prefix, encoding, retry = 5) {
  1278. const filename = prefix + (0, util_1.genRndStr6)();
  1279. try {
  1280. this.mkdirBase(filename, 511 /* MODE.DIR */);
  1281. return (0, encoding_1.strToEncoding)(filename, encoding);
  1282. }
  1283. catch (err) {
  1284. if (err.code === EEXIST) {
  1285. if (retry > 1)
  1286. return this.mkdtempBase(prefix, encoding, retry - 1);
  1287. else
  1288. throw Error('Could not create temp dir.');
  1289. }
  1290. else
  1291. throw err;
  1292. }
  1293. }
  1294. mkdtempSync(prefix, options) {
  1295. const { encoding } = (0, options_1.getDefaultOpts)(options);
  1296. if (!prefix || typeof prefix !== 'string')
  1297. throw new TypeError('filename prefix is required');
  1298. (0, util_1.nullCheck)(prefix);
  1299. return this.mkdtempBase(prefix, encoding);
  1300. }
  1301. mkdtemp(prefix, a, b) {
  1302. const [{ encoding }, callback] = (0, options_1.getDefaultOptsAndCb)(a, b);
  1303. if (!prefix || typeof prefix !== 'string')
  1304. throw new TypeError('filename prefix is required');
  1305. if (!(0, util_1.nullCheck)(prefix))
  1306. return;
  1307. this.wrapAsync(this.mkdtempBase, [prefix, encoding], callback);
  1308. }
  1309. rmdirBase(filename, options) {
  1310. const opts = (0, options_1.getRmdirOptions)(options);
  1311. const link = this.getLinkAsDirOrThrow(filename, 'rmdir');
  1312. // Check directory is empty.
  1313. if (link.length && !opts.recursive)
  1314. throw (0, util_1.createError)(ENOTEMPTY, 'rmdir', filename);
  1315. this.deleteLink(link);
  1316. }
  1317. rmdirSync(path, options) {
  1318. this.rmdirBase((0, util_1.pathToFilename)(path), options);
  1319. }
  1320. rmdir(path, a, b) {
  1321. const opts = (0, options_1.getRmdirOptions)(a);
  1322. const callback = (0, util_1.validateCallback)(typeof a === 'function' ? a : b);
  1323. this.wrapAsync(this.rmdirBase, [(0, util_1.pathToFilename)(path), opts], callback);
  1324. }
  1325. rmBase(filename, options = {}) {
  1326. const link = this.getResolvedLink(filename);
  1327. if (!link) {
  1328. // "stat" is used to match Node's native error message.
  1329. if (!options.force)
  1330. throw (0, util_1.createError)(ENOENT, 'stat', filename);
  1331. return;
  1332. }
  1333. if (link.getNode().isDirectory()) {
  1334. if (!options.recursive) {
  1335. throw (0, util_1.createError)(ERR_FS_EISDIR, 'rm', filename);
  1336. }
  1337. }
  1338. this.deleteLink(link);
  1339. }
  1340. rmSync(path, options) {
  1341. this.rmBase((0, util_1.pathToFilename)(path), options);
  1342. }
  1343. rm(path, a, b) {
  1344. const [opts, callback] = (0, options_1.getRmOptsAndCb)(a, b);
  1345. this.wrapAsync(this.rmBase, [(0, util_1.pathToFilename)(path), opts], callback);
  1346. }
  1347. fchmodBase(fd, modeNum) {
  1348. const file = this.getFileByFdOrThrow(fd, 'fchmod');
  1349. file.chmod(modeNum);
  1350. }
  1351. fchmodSync(fd, mode) {
  1352. this.fchmodBase(fd, (0, util_1.modeToNumber)(mode));
  1353. }
  1354. fchmod(fd, mode, callback) {
  1355. this.wrapAsync(this.fchmodBase, [fd, (0, util_1.modeToNumber)(mode)], callback);
  1356. }
  1357. chmodBase(filename, modeNum) {
  1358. const fd = this.openSync(filename, 'r');
  1359. try {
  1360. this.fchmodBase(fd, modeNum);
  1361. }
  1362. finally {
  1363. this.closeSync(fd);
  1364. }
  1365. }
  1366. chmodSync(path, mode) {
  1367. const modeNum = (0, util_1.modeToNumber)(mode);
  1368. const filename = (0, util_1.pathToFilename)(path);
  1369. this.chmodBase(filename, modeNum);
  1370. }
  1371. chmod(path, mode, callback) {
  1372. const modeNum = (0, util_1.modeToNumber)(mode);
  1373. const filename = (0, util_1.pathToFilename)(path);
  1374. this.wrapAsync(this.chmodBase, [filename, modeNum], callback);
  1375. }
  1376. lchmodBase(filename, modeNum) {
  1377. const fd = this.openBase(filename, O_RDWR, 0, false);
  1378. try {
  1379. this.fchmodBase(fd, modeNum);
  1380. }
  1381. finally {
  1382. this.closeSync(fd);
  1383. }
  1384. }
  1385. lchmodSync(path, mode) {
  1386. const modeNum = (0, util_1.modeToNumber)(mode);
  1387. const filename = (0, util_1.pathToFilename)(path);
  1388. this.lchmodBase(filename, modeNum);
  1389. }
  1390. lchmod(path, mode, callback) {
  1391. const modeNum = (0, util_1.modeToNumber)(mode);
  1392. const filename = (0, util_1.pathToFilename)(path);
  1393. this.wrapAsync(this.lchmodBase, [filename, modeNum], callback);
  1394. }
  1395. fchownBase(fd, uid, gid) {
  1396. this.getFileByFdOrThrow(fd, 'fchown').chown(uid, gid);
  1397. }
  1398. fchownSync(fd, uid, gid) {
  1399. validateUid(uid);
  1400. validateGid(gid);
  1401. this.fchownBase(fd, uid, gid);
  1402. }
  1403. fchown(fd, uid, gid, callback) {
  1404. validateUid(uid);
  1405. validateGid(gid);
  1406. this.wrapAsync(this.fchownBase, [fd, uid, gid], callback);
  1407. }
  1408. chownBase(filename, uid, gid) {
  1409. const link = this.getResolvedLinkOrThrow(filename, 'chown');
  1410. const node = link.getNode();
  1411. node.chown(uid, gid);
  1412. // if(node.isFile() || node.isSymlink()) {
  1413. //
  1414. // } else if(node.isDirectory()) {
  1415. //
  1416. // } else {
  1417. // TODO: What do we do here?
  1418. // }
  1419. }
  1420. chownSync(path, uid, gid) {
  1421. validateUid(uid);
  1422. validateGid(gid);
  1423. this.chownBase((0, util_1.pathToFilename)(path), uid, gid);
  1424. }
  1425. chown(path, uid, gid, callback) {
  1426. validateUid(uid);
  1427. validateGid(gid);
  1428. this.wrapAsync(this.chownBase, [(0, util_1.pathToFilename)(path), uid, gid], callback);
  1429. }
  1430. lchownBase(filename, uid, gid) {
  1431. this.getLinkOrThrow(filename, 'lchown').getNode().chown(uid, gid);
  1432. }
  1433. lchownSync(path, uid, gid) {
  1434. validateUid(uid);
  1435. validateGid(gid);
  1436. this.lchownBase((0, util_1.pathToFilename)(path), uid, gid);
  1437. }
  1438. lchown(path, uid, gid, callback) {
  1439. validateUid(uid);
  1440. validateGid(gid);
  1441. this.wrapAsync(this.lchownBase, [(0, util_1.pathToFilename)(path), uid, gid], callback);
  1442. }
  1443. watchFile(path, a, b) {
  1444. const filename = (0, util_1.pathToFilename)(path);
  1445. let options = a;
  1446. let listener = b;
  1447. if (typeof options === 'function') {
  1448. listener = a;
  1449. options = null;
  1450. }
  1451. if (typeof listener !== 'function') {
  1452. throw Error('"watchFile()" requires a listener function');
  1453. }
  1454. let interval = 5007;
  1455. let persistent = true;
  1456. if (options && typeof options === 'object') {
  1457. if (typeof options.interval === 'number')
  1458. interval = options.interval;
  1459. if (typeof options.persistent === 'boolean')
  1460. persistent = options.persistent;
  1461. }
  1462. let watcher = this.statWatchers[filename];
  1463. if (!watcher) {
  1464. watcher = new this.StatWatcher();
  1465. watcher.start(filename, persistent, interval);
  1466. this.statWatchers[filename] = watcher;
  1467. }
  1468. watcher.addListener('change', listener);
  1469. return watcher;
  1470. }
  1471. unwatchFile(path, listener) {
  1472. const filename = (0, util_1.pathToFilename)(path);
  1473. const watcher = this.statWatchers[filename];
  1474. if (!watcher)
  1475. return;
  1476. if (typeof listener === 'function') {
  1477. watcher.removeListener('change', listener);
  1478. }
  1479. else {
  1480. watcher.removeAllListeners('change');
  1481. }
  1482. if (watcher.listenerCount('change') === 0) {
  1483. watcher.stop();
  1484. delete this.statWatchers[filename];
  1485. }
  1486. }
  1487. createReadStream(path, options) {
  1488. return new this.ReadStream(path, options);
  1489. }
  1490. createWriteStream(path, options) {
  1491. return new this.WriteStream(path, options);
  1492. }
  1493. // watch(path: PathLike): FSWatcher;
  1494. // watch(path: PathLike, options?: IWatchOptions | string): FSWatcher;
  1495. watch(path, options, listener) {
  1496. const filename = (0, util_1.pathToFilename)(path);
  1497. let givenOptions = options;
  1498. if (typeof options === 'function') {
  1499. listener = options;
  1500. givenOptions = null;
  1501. }
  1502. // tslint:disable-next-line prefer-const
  1503. let { persistent, recursive, encoding } = (0, options_1.getDefaultOpts)(givenOptions);
  1504. if (persistent === undefined)
  1505. persistent = true;
  1506. if (recursive === undefined)
  1507. recursive = false;
  1508. const watcher = new this.FSWatcher();
  1509. watcher.start(filename, persistent, recursive, encoding);
  1510. if (listener) {
  1511. watcher.addListener('change', listener);
  1512. }
  1513. return watcher;
  1514. }
  1515. opendirBase(filename, options) {
  1516. const steps = filenameToSteps(filename);
  1517. const link = this.getResolvedLink(steps);
  1518. if (!link)
  1519. throw (0, util_1.createError)(ENOENT, 'opendir', filename);
  1520. const node = link.getNode();
  1521. if (!node.isDirectory())
  1522. throw (0, util_1.createError)(ENOTDIR, 'scandir', filename);
  1523. return new Dir_1.Dir(link, options);
  1524. }
  1525. opendirSync(path, options) {
  1526. const opts = (0, options_1.getOpendirOptions)(options);
  1527. const filename = (0, util_1.pathToFilename)(path);
  1528. return this.opendirBase(filename, opts);
  1529. }
  1530. opendir(path, a, b) {
  1531. const [options, callback] = (0, options_1.getOpendirOptsAndCb)(a, b);
  1532. const filename = (0, util_1.pathToFilename)(path);
  1533. this.wrapAsync(this.opendirBase, [filename, options], callback);
  1534. }
  1535. }
  1536. exports.Volume = Volume;
  1537. /**
  1538. * Global file descriptor counter. UNIX file descriptors start from 0 and go sequentially
  1539. * up, so here, in order not to conflict with them, we choose some big number and descrease
  1540. * the file descriptor of every new opened file.
  1541. * @type {number}
  1542. * @todo This should not be static, right?
  1543. */
  1544. Volume.fd = 0x7fffffff;
  1545. function emitStop(self) {
  1546. self.emit('stop');
  1547. }
  1548. class StatWatcher extends events_1.EventEmitter {
  1549. constructor(vol) {
  1550. super();
  1551. this.onInterval = () => {
  1552. try {
  1553. const stats = this.vol.statSync(this.filename);
  1554. if (this.hasChanged(stats)) {
  1555. this.emit('change', stats, this.prev);
  1556. this.prev = stats;
  1557. }
  1558. }
  1559. finally {
  1560. this.loop();
  1561. }
  1562. };
  1563. this.vol = vol;
  1564. }
  1565. loop() {
  1566. this.timeoutRef = this.setTimeout(this.onInterval, this.interval);
  1567. }
  1568. hasChanged(stats) {
  1569. // if(!this.prev) return false;
  1570. if (stats.mtimeMs > this.prev.mtimeMs)
  1571. return true;
  1572. if (stats.nlink !== this.prev.nlink)
  1573. return true;
  1574. return false;
  1575. }
  1576. start(path, persistent = true, interval = 5007) {
  1577. this.filename = (0, util_1.pathToFilename)(path);
  1578. this.setTimeout = persistent
  1579. ? setTimeout.bind(typeof globalThis !== 'undefined' ? globalThis : global)
  1580. : setTimeoutUnref_1.default;
  1581. this.interval = interval;
  1582. this.prev = this.vol.statSync(this.filename);
  1583. this.loop();
  1584. }
  1585. stop() {
  1586. clearTimeout(this.timeoutRef);
  1587. (0, queueMicrotask_1.default)(() => {
  1588. emitStop.call(this, this);
  1589. });
  1590. }
  1591. }
  1592. exports.StatWatcher = StatWatcher;
  1593. /* tslint:disable no-var-keyword prefer-const */
  1594. // ---------------------------------------- ReadStream
  1595. var pool;
  1596. function allocNewPool(poolSize) {
  1597. pool = (0, buffer_1.bufferAllocUnsafe)(poolSize);
  1598. pool.used = 0;
  1599. }
  1600. util.inherits(FsReadStream, stream_1.Readable);
  1601. exports.ReadStream = FsReadStream;
  1602. function FsReadStream(vol, path, options) {
  1603. if (!(this instanceof FsReadStream))
  1604. return new FsReadStream(vol, path, options);
  1605. this._vol = vol;
  1606. // a little bit bigger buffer and water marks by default
  1607. options = Object.assign({}, (0, options_1.getOptions)(options, {}));
  1608. if (options.highWaterMark === undefined)
  1609. options.highWaterMark = 64 * 1024;
  1610. stream_1.Readable.call(this, options);
  1611. this.path = (0, util_1.pathToFilename)(path);
  1612. this.fd = options.fd === undefined ? null : options.fd;
  1613. this.flags = options.flags === undefined ? 'r' : options.flags;
  1614. this.mode = options.mode === undefined ? 0o666 : options.mode;
  1615. this.start = options.start;
  1616. this.end = options.end;
  1617. this.autoClose = options.autoClose === undefined ? true : options.autoClose;
  1618. this.pos = undefined;
  1619. this.bytesRead = 0;
  1620. if (this.start !== undefined) {
  1621. if (typeof this.start !== 'number') {
  1622. throw new TypeError('"start" option must be a Number');
  1623. }
  1624. if (this.end === undefined) {
  1625. this.end = Infinity;
  1626. }
  1627. else if (typeof this.end !== 'number') {
  1628. throw new TypeError('"end" option must be a Number');
  1629. }
  1630. if (this.start > this.end) {
  1631. throw new Error('"start" option must be <= "end" option');
  1632. }
  1633. this.pos = this.start;
  1634. }
  1635. if (typeof this.fd !== 'number')
  1636. this.open();
  1637. this.on('end', function () {
  1638. if (this.autoClose) {
  1639. if (this.destroy)
  1640. this.destroy();
  1641. }
  1642. });
  1643. }
  1644. FsReadStream.prototype.open = function () {
  1645. var self = this; // tslint:disable-line no-this-assignment
  1646. this._vol.open(this.path, this.flags, this.mode, (er, fd) => {
  1647. if (er) {
  1648. if (self.autoClose) {
  1649. if (self.destroy)
  1650. self.destroy();
  1651. }
  1652. self.emit('error', er);
  1653. return;
  1654. }
  1655. self.fd = fd;
  1656. self.emit('open', fd);
  1657. // start the flow of data.
  1658. self.read();
  1659. });
  1660. };
  1661. FsReadStream.prototype._read = function (n) {
  1662. if (typeof this.fd !== 'number') {
  1663. return this.once('open', function () {
  1664. this._read(n);
  1665. });
  1666. }
  1667. if (this.destroyed)
  1668. return;
  1669. if (!pool || pool.length - pool.used < kMinPoolSpace) {
  1670. // discard the old pool.
  1671. allocNewPool(this._readableState.highWaterMark);
  1672. }
  1673. // Grab another reference to the pool in the case that while we're
  1674. // in the thread pool another read() finishes up the pool, and
  1675. // allocates a new one.
  1676. var thisPool = pool;
  1677. var toRead = Math.min(pool.length - pool.used, n);
  1678. var start = pool.used;
  1679. if (this.pos !== undefined)
  1680. toRead = Math.min(this.end - this.pos + 1, toRead);
  1681. // already read everything we were supposed to read!
  1682. // treat as EOF.
  1683. if (toRead <= 0)
  1684. return this.push(null);
  1685. // the actual read.
  1686. var self = this; // tslint:disable-line no-this-assignment
  1687. this._vol.read(this.fd, pool, pool.used, toRead, this.pos, onread);
  1688. // move the pool positions, and internal position for reading.
  1689. if (this.pos !== undefined)
  1690. this.pos += toRead;
  1691. pool.used += toRead;
  1692. function onread(er, bytesRead) {
  1693. if (er) {
  1694. if (self.autoClose && self.destroy) {
  1695. self.destroy();
  1696. }
  1697. self.emit('error', er);
  1698. }
  1699. else {
  1700. var b = null;
  1701. if (bytesRead > 0) {
  1702. self.bytesRead += bytesRead;
  1703. b = thisPool.slice(start, start + bytesRead);
  1704. }
  1705. self.push(b);
  1706. }
  1707. }
  1708. };
  1709. FsReadStream.prototype._destroy = function (err, cb) {
  1710. this.close(err2 => {
  1711. cb(err || err2);
  1712. });
  1713. };
  1714. FsReadStream.prototype.close = function (cb) {
  1715. var _a;
  1716. if (cb)
  1717. this.once('close', cb);
  1718. if (this.closed || typeof this.fd !== 'number') {
  1719. if (typeof this.fd !== 'number') {
  1720. this.once('open', closeOnOpen);
  1721. return;
  1722. }
  1723. return (0, queueMicrotask_1.default)(() => this.emit('close'));
  1724. }
  1725. // Since Node 18, there is only a getter for '.closed'.
  1726. // The first branch mimics other setters from Readable.
  1727. // See https://github.com/nodejs/node/blob/v18.0.0/lib/internal/streams/readable.js#L1243
  1728. if (typeof ((_a = this._readableState) === null || _a === void 0 ? void 0 : _a.closed) === 'boolean') {
  1729. this._readableState.closed = true;
  1730. }
  1731. else {
  1732. this.closed = true;
  1733. }
  1734. this._vol.close(this.fd, er => {
  1735. if (er)
  1736. this.emit('error', er);
  1737. else
  1738. this.emit('close');
  1739. });
  1740. this.fd = null;
  1741. };
  1742. // needed because as it will be called with arguments
  1743. // that does not match this.close() signature
  1744. function closeOnOpen(fd) {
  1745. this.close();
  1746. }
  1747. util.inherits(FsWriteStream, stream_1.Writable);
  1748. exports.WriteStream = FsWriteStream;
  1749. function FsWriteStream(vol, path, options) {
  1750. if (!(this instanceof FsWriteStream))
  1751. return new FsWriteStream(vol, path, options);
  1752. this._vol = vol;
  1753. options = Object.assign({}, (0, options_1.getOptions)(options, {}));
  1754. stream_1.Writable.call(this, options);
  1755. this.path = (0, util_1.pathToFilename)(path);
  1756. this.fd = options.fd === undefined ? null : options.fd;
  1757. this.flags = options.flags === undefined ? 'w' : options.flags;
  1758. this.mode = options.mode === undefined ? 0o666 : options.mode;
  1759. this.start = options.start;
  1760. this.autoClose = options.autoClose === undefined ? true : !!options.autoClose;
  1761. this.pos = undefined;
  1762. this.bytesWritten = 0;
  1763. this.pending = true;
  1764. if (this.start !== undefined) {
  1765. if (typeof this.start !== 'number') {
  1766. throw new TypeError('"start" option must be a Number');
  1767. }
  1768. if (this.start < 0) {
  1769. throw new Error('"start" must be >= zero');
  1770. }
  1771. this.pos = this.start;
  1772. }
  1773. if (options.encoding)
  1774. this.setDefaultEncoding(options.encoding);
  1775. if (typeof this.fd !== 'number')
  1776. this.open();
  1777. // dispose on finish.
  1778. this.once('finish', function () {
  1779. if (this.autoClose) {
  1780. this.close();
  1781. }
  1782. });
  1783. }
  1784. FsWriteStream.prototype.open = function () {
  1785. this._vol.open(this.path, this.flags, this.mode, function (er, fd) {
  1786. if (er) {
  1787. if (this.autoClose && this.destroy) {
  1788. this.destroy();
  1789. }
  1790. this.emit('error', er);
  1791. return;
  1792. }
  1793. this.fd = fd;
  1794. this.pending = false;
  1795. this.emit('open', fd);
  1796. }.bind(this));
  1797. };
  1798. FsWriteStream.prototype._write = function (data, encoding, cb) {
  1799. if (!(data instanceof buffer_1.Buffer || data instanceof Uint8Array))
  1800. return this.emit('error', new Error('Invalid data'));
  1801. if (typeof this.fd !== 'number') {
  1802. return this.once('open', function () {
  1803. this._write(data, encoding, cb);
  1804. });
  1805. }
  1806. var self = this; // tslint:disable-line no-this-assignment
  1807. this._vol.write(this.fd, data, 0, data.length, this.pos, (er, bytes) => {
  1808. if (er) {
  1809. if (self.autoClose && self.destroy) {
  1810. self.destroy();
  1811. }
  1812. return cb(er);
  1813. }
  1814. self.bytesWritten += bytes;
  1815. cb();
  1816. });
  1817. if (this.pos !== undefined)
  1818. this.pos += data.length;
  1819. };
  1820. FsWriteStream.prototype._writev = function (data, cb) {
  1821. if (typeof this.fd !== 'number') {
  1822. return this.once('open', function () {
  1823. this._writev(data, cb);
  1824. });
  1825. }
  1826. const self = this; // tslint:disable-line no-this-assignment
  1827. const len = data.length;
  1828. const chunks = new Array(len);
  1829. var size = 0;
  1830. for (var i = 0; i < len; i++) {
  1831. var chunk = data[i].chunk;
  1832. chunks[i] = chunk;
  1833. size += chunk.length;
  1834. }
  1835. const buf = buffer_1.Buffer.concat(chunks);
  1836. this._vol.write(this.fd, buf, 0, buf.length, this.pos, (er, bytes) => {
  1837. if (er) {
  1838. if (self.destroy)
  1839. self.destroy();
  1840. return cb(er);
  1841. }
  1842. self.bytesWritten += bytes;
  1843. cb();
  1844. });
  1845. if (this.pos !== undefined)
  1846. this.pos += size;
  1847. };
  1848. FsWriteStream.prototype.close = function (cb) {
  1849. var _a;
  1850. if (cb)
  1851. this.once('close', cb);
  1852. if (this.closed || typeof this.fd !== 'number') {
  1853. if (typeof this.fd !== 'number') {
  1854. this.once('open', closeOnOpen);
  1855. return;
  1856. }
  1857. return (0, queueMicrotask_1.default)(() => this.emit('close'));
  1858. }
  1859. // Since Node 18, there is only a getter for '.closed'.
  1860. // The first branch mimics other setters from Writable.
  1861. // See https://github.com/nodejs/node/blob/v18.0.0/lib/internal/streams/writable.js#L766
  1862. if (typeof ((_a = this._writableState) === null || _a === void 0 ? void 0 : _a.closed) === 'boolean') {
  1863. this._writableState.closed = true;
  1864. }
  1865. else {
  1866. this.closed = true;
  1867. }
  1868. this._vol.close(this.fd, er => {
  1869. if (er)
  1870. this.emit('error', er);
  1871. else
  1872. this.emit('close');
  1873. });
  1874. this.fd = null;
  1875. };
  1876. FsWriteStream.prototype._destroy = FsReadStream.prototype._destroy;
  1877. // There is no shutdown() for files.
  1878. FsWriteStream.prototype.destroySoon = FsWriteStream.prototype.end;
  1879. // ---------------------------------------- FSWatcher
  1880. class FSWatcher extends events_1.EventEmitter {
  1881. constructor(vol) {
  1882. super();
  1883. this._filename = '';
  1884. this._filenameEncoded = '';
  1885. // _persistent: boolean = true;
  1886. this._recursive = false;
  1887. this._encoding = encoding_1.ENCODING_UTF8;
  1888. // inode -> removers
  1889. this._listenerRemovers = new Map();
  1890. this._onParentChild = (link) => {
  1891. if (link.getName() === this._getName()) {
  1892. this._emit('rename');
  1893. }
  1894. };
  1895. this._emit = (type) => {
  1896. this.emit('change', type, this._filenameEncoded);
  1897. };
  1898. this._persist = () => {
  1899. this._timer = setTimeout(this._persist, 1e6);
  1900. };
  1901. this._vol = vol;
  1902. // TODO: Emit "error" messages when watching.
  1903. // this._handle.onchange = function(status, eventType, filename) {
  1904. // if (status < 0) {
  1905. // self._handle.close();
  1906. // const error = !filename ?
  1907. // errnoException(status, 'Error watching file for changes:') :
  1908. // errnoException(status, `Error watching file ${filename} for changes:`);
  1909. // error.filename = filename;
  1910. // self.emit('error', error);
  1911. // } else {
  1912. // self.emit('change', eventType, filename);
  1913. // }
  1914. // };
  1915. }
  1916. _getName() {
  1917. return this._steps[this._steps.length - 1];
  1918. }
  1919. start(path, persistent = true, recursive = false, encoding = encoding_1.ENCODING_UTF8) {
  1920. this._filename = (0, util_1.pathToFilename)(path);
  1921. this._steps = filenameToSteps(this._filename);
  1922. this._filenameEncoded = (0, encoding_1.strToEncoding)(this._filename);
  1923. // this._persistent = persistent;
  1924. this._recursive = recursive;
  1925. this._encoding = encoding;
  1926. try {
  1927. this._link = this._vol.getLinkOrThrow(this._filename, 'FSWatcher');
  1928. }
  1929. catch (err) {
  1930. const error = new Error(`watch ${this._filename} ${err.code}`);
  1931. error.code = err.code;
  1932. error.errno = err.code;
  1933. throw error;
  1934. }
  1935. const watchLinkNodeChanged = (link) => {
  1936. var _a;
  1937. const filepath = link.getPath();
  1938. const node = link.getNode();
  1939. const onNodeChange = () => {
  1940. let filename = relative(this._filename, filepath);
  1941. if (!filename) {
  1942. filename = this._getName();
  1943. }
  1944. return this.emit('change', 'change', filename);
  1945. };
  1946. node.on('change', onNodeChange);
  1947. const removers = (_a = this._listenerRemovers.get(node.ino)) !== null && _a !== void 0 ? _a : [];
  1948. removers.push(() => node.removeListener('change', onNodeChange));
  1949. this._listenerRemovers.set(node.ino, removers);
  1950. };
  1951. const watchLinkChildrenChanged = (link) => {
  1952. var _a;
  1953. const node = link.getNode();
  1954. // when a new link added
  1955. const onLinkChildAdd = (l) => {
  1956. this.emit('change', 'rename', relative(this._filename, l.getPath()));
  1957. setTimeout(() => {
  1958. // 1. watch changes of the new link-node
  1959. watchLinkNodeChanged(l);
  1960. // 2. watch changes of the new link-node's children
  1961. watchLinkChildrenChanged(l);
  1962. });
  1963. };
  1964. // when a new link deleted
  1965. const onLinkChildDelete = (l) => {
  1966. // remove the listeners of the children nodes
  1967. const removeLinkNodeListeners = (curLink) => {
  1968. const ino = curLink.getNode().ino;
  1969. const removers = this._listenerRemovers.get(ino);
  1970. if (removers) {
  1971. removers.forEach(r => r());
  1972. this._listenerRemovers.delete(ino);
  1973. }
  1974. for (const [name, childLink] of curLink.children.entries()) {
  1975. if (childLink && name !== '.' && name !== '..') {
  1976. removeLinkNodeListeners(childLink);
  1977. }
  1978. }
  1979. };
  1980. removeLinkNodeListeners(l);
  1981. this.emit('change', 'rename', relative(this._filename, l.getPath()));
  1982. };
  1983. // children nodes changed
  1984. for (const [name, childLink] of link.children.entries()) {
  1985. if (childLink && name !== '.' && name !== '..') {
  1986. watchLinkNodeChanged(childLink);
  1987. }
  1988. }
  1989. // link children add/remove
  1990. link.on('child:add', onLinkChildAdd);
  1991. link.on('child:delete', onLinkChildDelete);
  1992. const removers = (_a = this._listenerRemovers.get(node.ino)) !== null && _a !== void 0 ? _a : [];
  1993. removers.push(() => {
  1994. link.removeListener('child:add', onLinkChildAdd);
  1995. link.removeListener('child:delete', onLinkChildDelete);
  1996. });
  1997. if (recursive) {
  1998. for (const [name, childLink] of link.children.entries()) {
  1999. if (childLink && name !== '.' && name !== '..') {
  2000. watchLinkChildrenChanged(childLink);
  2001. }
  2002. }
  2003. }
  2004. };
  2005. watchLinkNodeChanged(this._link);
  2006. watchLinkChildrenChanged(this._link);
  2007. const parent = this._link.parent;
  2008. if (parent) {
  2009. // parent.on('child:add', this._onParentChild);
  2010. parent.setMaxListeners(parent.getMaxListeners() + 1);
  2011. parent.on('child:delete', this._onParentChild);
  2012. }
  2013. if (persistent)
  2014. this._persist();
  2015. }
  2016. close() {
  2017. clearTimeout(this._timer);
  2018. this._listenerRemovers.forEach(removers => {
  2019. removers.forEach(r => r());
  2020. });
  2021. this._listenerRemovers.clear();
  2022. const parent = this._link.parent;
  2023. if (parent) {
  2024. // parent.removeListener('child:add', this._onParentChild);
  2025. parent.removeListener('child:delete', this._onParentChild);
  2026. }
  2027. }
  2028. }
  2029. exports.FSWatcher = FSWatcher;
  2030. //# sourceMappingURL=volume.js.map