volume.js 80 KB

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