volume.js 80 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125
  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. const realLink = this.getResolvedLinkOrThrow(filename, 'realpath');
  966. return (0, encoding_1.strToEncoding)(realLink.getPath() || '/', encoding);
  967. }
  968. realpathSync(path, options) {
  969. return this.realpathBase((0, util_1.pathToFilename)(path), (0, options_1.getRealpathOptions)(options).encoding);
  970. }
  971. realpath(path, a, b) {
  972. const [opts, callback] = (0, options_1.getRealpathOptsAndCb)(a, b);
  973. const pathFilename = (0, util_1.pathToFilename)(path);
  974. this.wrapAsync(this.realpathBase, [pathFilename, opts.encoding], callback);
  975. }
  976. lstatBase(filename, bigint = false, throwIfNoEntry = false) {
  977. let link;
  978. try {
  979. link = this.getLinkOrThrow(filename, 'lstat');
  980. }
  981. catch (err) {
  982. if (err.code === ENOENT && !throwIfNoEntry)
  983. return undefined;
  984. else
  985. throw err;
  986. }
  987. return Stats_1.default.build(link.getNode(), bigint);
  988. }
  989. lstatSync(path, options) {
  990. const { throwIfNoEntry = true, bigint = false } = (0, options_1.getStatOptions)(options);
  991. return this.lstatBase((0, util_1.pathToFilename)(path), bigint, throwIfNoEntry);
  992. }
  993. lstat(path, a, b) {
  994. const [{ throwIfNoEntry = true, bigint = false }, callback] = (0, options_1.getStatOptsAndCb)(a, b);
  995. this.wrapAsync(this.lstatBase, [(0, util_1.pathToFilename)(path), bigint, throwIfNoEntry], callback);
  996. }
  997. statBase(filename, bigint = false, throwIfNoEntry = true) {
  998. let link;
  999. try {
  1000. link = this.getResolvedLinkOrThrow(filename, 'stat');
  1001. }
  1002. catch (err) {
  1003. if (err.code === ENOENT && !throwIfNoEntry)
  1004. return undefined;
  1005. else
  1006. throw err;
  1007. }
  1008. return Stats_1.default.build(link.getNode(), bigint);
  1009. }
  1010. statSync(path, options) {
  1011. const { bigint = true, throwIfNoEntry = true } = (0, options_1.getStatOptions)(options);
  1012. return this.statBase((0, util_1.pathToFilename)(path), bigint, throwIfNoEntry);
  1013. }
  1014. stat(path, a, b) {
  1015. const [{ bigint = false, throwIfNoEntry = true }, callback] = (0, options_1.getStatOptsAndCb)(a, b);
  1016. this.wrapAsync(this.statBase, [(0, util_1.pathToFilename)(path), bigint, throwIfNoEntry], callback);
  1017. }
  1018. fstatBase(fd, bigint = false) {
  1019. const file = this.getFileByFd(fd);
  1020. if (!file)
  1021. throw (0, util_1.createError)(EBADF, 'fstat');
  1022. return Stats_1.default.build(file.node, bigint);
  1023. }
  1024. fstatSync(fd, options) {
  1025. return this.fstatBase(fd, (0, options_1.getStatOptions)(options).bigint);
  1026. }
  1027. fstat(fd, a, b) {
  1028. const [opts, callback] = (0, options_1.getStatOptsAndCb)(a, b);
  1029. this.wrapAsync(this.fstatBase, [fd, opts.bigint], callback);
  1030. }
  1031. renameBase(oldPathFilename, newPathFilename) {
  1032. let link;
  1033. try {
  1034. link = this.getResolvedLinkOrThrow(oldPathFilename);
  1035. }
  1036. catch (err) {
  1037. // Augment err with newPathFilename
  1038. if (err.code)
  1039. err = (0, util_1.createError)(err.code, 'rename', oldPathFilename, newPathFilename);
  1040. throw err;
  1041. }
  1042. // TODO: Check if it is directory, if non-empty, we cannot move it, right?
  1043. // Check directory exists for the new location.
  1044. let newPathDirLink;
  1045. try {
  1046. newPathDirLink = this.getLinkParentAsDirOrThrow(newPathFilename);
  1047. }
  1048. catch (err) {
  1049. // Augment error with oldPathFilename
  1050. if (err.code)
  1051. err = (0, util_1.createError)(err.code, 'rename', oldPathFilename, newPathFilename);
  1052. throw err;
  1053. }
  1054. // TODO: Also treat cases with directories and symbolic links.
  1055. // TODO: See: http://man7.org/linux/man-pages/man2/rename.2.html
  1056. // Remove hard link from old folder.
  1057. const oldLinkParent = link.parent;
  1058. // Check we have access and write permissions in both places
  1059. const oldParentNode = oldLinkParent.getNode();
  1060. const newPathDirNode = newPathDirLink.getNode();
  1061. if (!oldParentNode.canExecute() ||
  1062. !oldParentNode.canWrite() ||
  1063. !newPathDirNode.canExecute() ||
  1064. !newPathDirNode.canWrite()) {
  1065. throw (0, util_1.createError)(EACCES, 'rename', oldPathFilename, newPathFilename);
  1066. }
  1067. oldLinkParent.deleteChild(link);
  1068. // Rename should overwrite the new path, if that exists.
  1069. const name = pathModule.basename(newPathFilename);
  1070. link.name = name;
  1071. link.steps = [...newPathDirLink.steps, name];
  1072. newPathDirLink.setChild(link.getName(), link);
  1073. }
  1074. renameSync(oldPath, newPath) {
  1075. const oldPathFilename = (0, util_1.pathToFilename)(oldPath);
  1076. const newPathFilename = (0, util_1.pathToFilename)(newPath);
  1077. this.renameBase(oldPathFilename, newPathFilename);
  1078. }
  1079. rename(oldPath, newPath, callback) {
  1080. const oldPathFilename = (0, util_1.pathToFilename)(oldPath);
  1081. const newPathFilename = (0, util_1.pathToFilename)(newPath);
  1082. this.wrapAsync(this.renameBase, [oldPathFilename, newPathFilename], callback);
  1083. }
  1084. existsBase(filename) {
  1085. return !!this.statBase(filename);
  1086. }
  1087. existsSync(path) {
  1088. try {
  1089. return this.existsBase((0, util_1.pathToFilename)(path));
  1090. }
  1091. catch (err) {
  1092. return false;
  1093. }
  1094. }
  1095. exists(path, callback) {
  1096. const filename = (0, util_1.pathToFilename)(path);
  1097. if (typeof callback !== 'function')
  1098. throw Error(constants_2.ERRSTR.CB);
  1099. (0, setImmediate_1.default)(() => {
  1100. try {
  1101. callback(this.existsBase(filename));
  1102. }
  1103. catch (err) {
  1104. callback(false);
  1105. }
  1106. });
  1107. }
  1108. accessBase(filename, mode) {
  1109. const link = this.getLinkOrThrow(filename, 'access');
  1110. }
  1111. accessSync(path, mode = F_OK) {
  1112. const filename = (0, util_1.pathToFilename)(path);
  1113. mode = mode | 0;
  1114. this.accessBase(filename, mode);
  1115. }
  1116. access(path, a, b) {
  1117. let mode = F_OK;
  1118. let callback;
  1119. if (typeof a !== 'function') {
  1120. mode = a | 0; // cast to number
  1121. callback = (0, util_1.validateCallback)(b);
  1122. }
  1123. else {
  1124. callback = a;
  1125. }
  1126. const filename = (0, util_1.pathToFilename)(path);
  1127. this.wrapAsync(this.accessBase, [filename, mode], callback);
  1128. }
  1129. appendFileSync(id, data, options) {
  1130. const opts = (0, options_1.getAppendFileOpts)(options);
  1131. // force append behavior when using a supplied file descriptor
  1132. if (!opts.flag || (0, util_1.isFd)(id))
  1133. opts.flag = 'a';
  1134. this.writeFileSync(id, data, opts);
  1135. }
  1136. appendFile(id, data, a, b) {
  1137. const [opts, callback] = (0, options_1.getAppendFileOptsAndCb)(a, b);
  1138. // force append behavior when using a supplied file descriptor
  1139. if (!opts.flag || (0, util_1.isFd)(id))
  1140. opts.flag = 'a';
  1141. this.writeFile(id, data, opts, callback);
  1142. }
  1143. readdirBase(filename, options) {
  1144. const steps = filenameToSteps(filename);
  1145. const link = this.getResolvedLinkOrThrow(filename, 'scandir');
  1146. const node = link.getNode();
  1147. if (!node.isDirectory())
  1148. throw (0, util_1.createError)(ENOTDIR, 'scandir', filename);
  1149. // Check we have permissions
  1150. if (!node.canRead())
  1151. throw (0, util_1.createError)(EACCES, 'scandir', filename);
  1152. const list = []; // output list
  1153. for (const name of link.children.keys()) {
  1154. const child = link.getChild(name);
  1155. if (!child || name === '.' || name === '..')
  1156. continue;
  1157. list.push(Dirent_1.default.build(child, options.encoding));
  1158. // recursion
  1159. if (options.recursive && child.children.size) {
  1160. const recurseOptions = Object.assign(Object.assign({}, options), { recursive: true, withFileTypes: true });
  1161. const childList = this.readdirBase(child.getPath(), recurseOptions);
  1162. list.push(...childList);
  1163. }
  1164. }
  1165. if (!util_1.isWin && options.encoding !== 'buffer')
  1166. list.sort((a, b) => {
  1167. if (a.name < b.name)
  1168. return -1;
  1169. if (a.name > b.name)
  1170. return 1;
  1171. return 0;
  1172. });
  1173. if (options.withFileTypes)
  1174. return list;
  1175. let filename2 = filename;
  1176. if (util_1.isWin) {
  1177. filename2 = filename2.replace(/\\/g, '/');
  1178. }
  1179. return list.map(dirent => {
  1180. if (options.recursive) {
  1181. let fullPath = pathModule.join(dirent.parentPath, dirent.name.toString());
  1182. if (util_1.isWin) {
  1183. fullPath = fullPath.replace(/\\/g, '/');
  1184. }
  1185. return fullPath.replace(filename2 + pathModule.posix.sep, '');
  1186. }
  1187. return dirent.name;
  1188. });
  1189. }
  1190. readdirSync(path, options) {
  1191. const opts = (0, options_1.getReaddirOptions)(options);
  1192. const filename = (0, util_1.pathToFilename)(path);
  1193. return this.readdirBase(filename, opts);
  1194. }
  1195. readdir(path, a, b) {
  1196. const [options, callback] = (0, options_1.getReaddirOptsAndCb)(a, b);
  1197. const filename = (0, util_1.pathToFilename)(path);
  1198. this.wrapAsync(this.readdirBase, [filename, options], callback);
  1199. }
  1200. readlinkBase(filename, encoding) {
  1201. const link = this.getLinkOrThrow(filename, 'readlink');
  1202. const node = link.getNode();
  1203. if (!node.isSymlink())
  1204. throw (0, util_1.createError)(EINVAL, 'readlink', filename);
  1205. return (0, encoding_1.strToEncoding)(node.symlink, encoding);
  1206. }
  1207. readlinkSync(path, options) {
  1208. const opts = (0, options_1.getDefaultOpts)(options);
  1209. const filename = (0, util_1.pathToFilename)(path);
  1210. return this.readlinkBase(filename, opts.encoding);
  1211. }
  1212. readlink(path, a, b) {
  1213. const [opts, callback] = (0, options_1.getDefaultOptsAndCb)(a, b);
  1214. const filename = (0, util_1.pathToFilename)(path);
  1215. this.wrapAsync(this.readlinkBase, [filename, opts.encoding], callback);
  1216. }
  1217. fsyncBase(fd) {
  1218. this.getFileByFdOrThrow(fd, 'fsync');
  1219. }
  1220. fsyncSync(fd) {
  1221. this.fsyncBase(fd);
  1222. }
  1223. fsync(fd, callback) {
  1224. this.wrapAsync(this.fsyncBase, [fd], callback);
  1225. }
  1226. fdatasyncBase(fd) {
  1227. this.getFileByFdOrThrow(fd, 'fdatasync');
  1228. }
  1229. fdatasyncSync(fd) {
  1230. this.fdatasyncBase(fd);
  1231. }
  1232. fdatasync(fd, callback) {
  1233. this.wrapAsync(this.fdatasyncBase, [fd], callback);
  1234. }
  1235. ftruncateBase(fd, len) {
  1236. const file = this.getFileByFdOrThrow(fd, 'ftruncate');
  1237. file.truncate(len);
  1238. }
  1239. ftruncateSync(fd, len) {
  1240. this.ftruncateBase(fd, len);
  1241. }
  1242. ftruncate(fd, a, b) {
  1243. const len = typeof a === 'number' ? a : 0;
  1244. const callback = (0, util_1.validateCallback)(typeof a === 'number' ? b : a);
  1245. this.wrapAsync(this.ftruncateBase, [fd, len], callback);
  1246. }
  1247. truncateBase(path, len) {
  1248. const fd = this.openSync(path, 'r+');
  1249. try {
  1250. this.ftruncateSync(fd, len);
  1251. }
  1252. finally {
  1253. this.closeSync(fd);
  1254. }
  1255. }
  1256. /**
  1257. * `id` should be a file descriptor or a path. `id` as file descriptor will
  1258. * not be supported soon.
  1259. */
  1260. truncateSync(id, len) {
  1261. if ((0, util_1.isFd)(id))
  1262. return this.ftruncateSync(id, len);
  1263. this.truncateBase(id, len);
  1264. }
  1265. truncate(id, a, b) {
  1266. const len = typeof a === 'number' ? a : 0;
  1267. const callback = (0, util_1.validateCallback)(typeof a === 'number' ? b : a);
  1268. if ((0, util_1.isFd)(id))
  1269. return this.ftruncate(id, len, callback);
  1270. this.wrapAsync(this.truncateBase, [id, len], callback);
  1271. }
  1272. futimesBase(fd, atime, mtime) {
  1273. const file = this.getFileByFdOrThrow(fd, 'futimes');
  1274. const node = file.node;
  1275. node.atime = new Date(atime * 1000);
  1276. node.mtime = new Date(mtime * 1000);
  1277. }
  1278. futimesSync(fd, atime, mtime) {
  1279. this.futimesBase(fd, toUnixTimestamp(atime), toUnixTimestamp(mtime));
  1280. }
  1281. futimes(fd, atime, mtime, callback) {
  1282. this.wrapAsync(this.futimesBase, [fd, toUnixTimestamp(atime), toUnixTimestamp(mtime)], callback);
  1283. }
  1284. utimesBase(filename, atime, mtime, followSymlinks = true) {
  1285. const link = followSymlinks
  1286. ? this.getResolvedLinkOrThrow(filename, 'utimes')
  1287. : this.getLinkOrThrow(filename, 'lutimes');
  1288. const node = link.getNode();
  1289. node.atime = new Date(atime * 1000);
  1290. node.mtime = new Date(mtime * 1000);
  1291. }
  1292. utimesSync(path, atime, mtime) {
  1293. this.utimesBase((0, util_1.pathToFilename)(path), toUnixTimestamp(atime), toUnixTimestamp(mtime), true);
  1294. }
  1295. utimes(path, atime, mtime, callback) {
  1296. this.wrapAsync(this.utimesBase, [(0, util_1.pathToFilename)(path), toUnixTimestamp(atime), toUnixTimestamp(mtime), true], callback);
  1297. }
  1298. lutimesSync(path, atime, mtime) {
  1299. this.utimesBase((0, util_1.pathToFilename)(path), toUnixTimestamp(atime), toUnixTimestamp(mtime), false);
  1300. }
  1301. lutimes(path, atime, mtime, callback) {
  1302. this.wrapAsync(this.utimesBase, [(0, util_1.pathToFilename)(path), toUnixTimestamp(atime), toUnixTimestamp(mtime), false], callback);
  1303. }
  1304. mkdirBase(filename, modeNum) {
  1305. const steps = filenameToSteps(filename);
  1306. // This will throw if user tries to create root dir `fs.mkdirSync('/')`.
  1307. if (!steps.length) {
  1308. throw (0, util_1.createError)(EEXIST, 'mkdir', filename);
  1309. }
  1310. const dir = this.getLinkParentAsDirOrThrow(filename, 'mkdir');
  1311. // Check path already exists.
  1312. const name = steps[steps.length - 1];
  1313. if (dir.getChild(name))
  1314. throw (0, util_1.createError)(EEXIST, 'mkdir', filename);
  1315. const node = dir.getNode();
  1316. if (!node.canWrite() || !node.canExecute())
  1317. throw (0, util_1.createError)(EACCES, 'mkdir', filename);
  1318. dir.createChild(name, this.createNode(constants_1.constants.S_IFDIR | modeNum));
  1319. }
  1320. /**
  1321. * Creates directory tree recursively.
  1322. */
  1323. mkdirpBase(filename, modeNum) {
  1324. let created = false;
  1325. const steps = filenameToSteps(filename);
  1326. let curr = null;
  1327. let i = steps.length;
  1328. // Find the longest subpath of filename that still exists:
  1329. for (i = steps.length; i >= 0; i--) {
  1330. curr = this.getResolvedLink(steps.slice(0, i));
  1331. if (curr)
  1332. break;
  1333. }
  1334. if (!curr) {
  1335. curr = this.root;
  1336. i = 0;
  1337. }
  1338. // curr is now the last directory that still exists.
  1339. // (If none of them existed, curr is the root.)
  1340. // Check access the lazy way:
  1341. curr = this.getResolvedLinkOrThrow(sep + steps.slice(0, i).join(sep), 'mkdir');
  1342. // Start creating directories:
  1343. for (i; i < steps.length; i++) {
  1344. const node = curr.getNode();
  1345. if (node.isDirectory()) {
  1346. // Check we have permissions
  1347. if (!node.canExecute() || !node.canWrite())
  1348. throw (0, util_1.createError)(EACCES, 'mkdir', filename);
  1349. }
  1350. else {
  1351. throw (0, util_1.createError)(ENOTDIR, 'mkdir', filename);
  1352. }
  1353. created = true;
  1354. curr = curr.createChild(steps[i], this.createNode(constants_1.constants.S_IFDIR | modeNum));
  1355. }
  1356. return created ? filename : undefined;
  1357. }
  1358. mkdirSync(path, options) {
  1359. const opts = (0, options_1.getMkdirOptions)(options);
  1360. const modeNum = (0, util_1.modeToNumber)(opts.mode, 0o777);
  1361. const filename = (0, util_1.pathToFilename)(path);
  1362. if (opts.recursive)
  1363. return this.mkdirpBase(filename, modeNum);
  1364. this.mkdirBase(filename, modeNum);
  1365. }
  1366. mkdir(path, a, b) {
  1367. const opts = (0, options_1.getMkdirOptions)(a);
  1368. const callback = (0, util_1.validateCallback)(typeof a === 'function' ? a : b);
  1369. const modeNum = (0, util_1.modeToNumber)(opts.mode, 0o777);
  1370. const filename = (0, util_1.pathToFilename)(path);
  1371. if (opts.recursive)
  1372. this.wrapAsync(this.mkdirpBase, [filename, modeNum], callback);
  1373. else
  1374. this.wrapAsync(this.mkdirBase, [filename, modeNum], callback);
  1375. }
  1376. mkdtempBase(prefix, encoding, retry = 5) {
  1377. const filename = prefix + (0, util_1.genRndStr6)();
  1378. try {
  1379. this.mkdirBase(filename, 511 /* MODE.DIR */);
  1380. return (0, encoding_1.strToEncoding)(filename, encoding);
  1381. }
  1382. catch (err) {
  1383. if (err.code === EEXIST) {
  1384. if (retry > 1)
  1385. return this.mkdtempBase(prefix, encoding, retry - 1);
  1386. else
  1387. throw Error('Could not create temp dir.');
  1388. }
  1389. else
  1390. throw err;
  1391. }
  1392. }
  1393. mkdtempSync(prefix, options) {
  1394. const { encoding } = (0, options_1.getDefaultOpts)(options);
  1395. if (!prefix || typeof prefix !== 'string')
  1396. throw new TypeError('filename prefix is required');
  1397. (0, util_1.nullCheck)(prefix);
  1398. return this.mkdtempBase(prefix, encoding);
  1399. }
  1400. mkdtemp(prefix, a, b) {
  1401. const [{ encoding }, callback] = (0, options_1.getDefaultOptsAndCb)(a, b);
  1402. if (!prefix || typeof prefix !== 'string')
  1403. throw new TypeError('filename prefix is required');
  1404. if (!(0, util_1.nullCheck)(prefix))
  1405. return;
  1406. this.wrapAsync(this.mkdtempBase, [prefix, encoding], callback);
  1407. }
  1408. rmdirBase(filename, options) {
  1409. const opts = (0, options_1.getRmdirOptions)(options);
  1410. const link = this.getLinkAsDirOrThrow(filename, 'rmdir');
  1411. // Check directory is empty.
  1412. if (link.length && !opts.recursive)
  1413. throw (0, util_1.createError)(ENOTEMPTY, 'rmdir', filename);
  1414. this.deleteLink(link);
  1415. }
  1416. rmdirSync(path, options) {
  1417. this.rmdirBase((0, util_1.pathToFilename)(path), options);
  1418. }
  1419. rmdir(path, a, b) {
  1420. const opts = (0, options_1.getRmdirOptions)(a);
  1421. const callback = (0, util_1.validateCallback)(typeof a === 'function' ? a : b);
  1422. this.wrapAsync(this.rmdirBase, [(0, util_1.pathToFilename)(path), opts], callback);
  1423. }
  1424. rmBase(filename, options = {}) {
  1425. // "stat" is used to match Node's native error message.
  1426. let link;
  1427. try {
  1428. link = this.getResolvedLinkOrThrow(filename, 'stat');
  1429. }
  1430. catch (err) {
  1431. // Silently ignore missing paths if force option is true
  1432. if (err.code === ENOENT && options.force)
  1433. return;
  1434. else
  1435. throw err;
  1436. }
  1437. if (link.getNode().isDirectory() && !options.recursive)
  1438. throw (0, util_1.createError)(ERR_FS_EISDIR, 'rm', filename);
  1439. // Check permissions
  1440. if (!link.parent.getNode().canWrite())
  1441. throw (0, util_1.createError)(EACCES, 'rm', filename);
  1442. this.deleteLink(link);
  1443. }
  1444. rmSync(path, options) {
  1445. this.rmBase((0, util_1.pathToFilename)(path), options);
  1446. }
  1447. rm(path, a, b) {
  1448. const [opts, callback] = (0, options_1.getRmOptsAndCb)(a, b);
  1449. this.wrapAsync(this.rmBase, [(0, util_1.pathToFilename)(path), opts], callback);
  1450. }
  1451. fchmodBase(fd, modeNum) {
  1452. const file = this.getFileByFdOrThrow(fd, 'fchmod');
  1453. file.chmod(modeNum);
  1454. }
  1455. fchmodSync(fd, mode) {
  1456. this.fchmodBase(fd, (0, util_1.modeToNumber)(mode));
  1457. }
  1458. fchmod(fd, mode, callback) {
  1459. this.wrapAsync(this.fchmodBase, [fd, (0, util_1.modeToNumber)(mode)], callback);
  1460. }
  1461. chmodBase(filename, modeNum, followSymlinks = true) {
  1462. const link = followSymlinks
  1463. ? this.getResolvedLinkOrThrow(filename, 'chmod')
  1464. : this.getLinkOrThrow(filename, 'chmod');
  1465. const node = link.getNode();
  1466. node.chmod(modeNum);
  1467. }
  1468. chmodSync(path, mode) {
  1469. const modeNum = (0, util_1.modeToNumber)(mode);
  1470. const filename = (0, util_1.pathToFilename)(path);
  1471. this.chmodBase(filename, modeNum, true);
  1472. }
  1473. chmod(path, mode, callback) {
  1474. const modeNum = (0, util_1.modeToNumber)(mode);
  1475. const filename = (0, util_1.pathToFilename)(path);
  1476. this.wrapAsync(this.chmodBase, [filename, modeNum], callback);
  1477. }
  1478. lchmodBase(filename, modeNum) {
  1479. this.chmodBase(filename, modeNum, false);
  1480. }
  1481. lchmodSync(path, mode) {
  1482. const modeNum = (0, util_1.modeToNumber)(mode);
  1483. const filename = (0, util_1.pathToFilename)(path);
  1484. this.lchmodBase(filename, modeNum);
  1485. }
  1486. lchmod(path, mode, callback) {
  1487. const modeNum = (0, util_1.modeToNumber)(mode);
  1488. const filename = (0, util_1.pathToFilename)(path);
  1489. this.wrapAsync(this.lchmodBase, [filename, modeNum], callback);
  1490. }
  1491. fchownBase(fd, uid, gid) {
  1492. this.getFileByFdOrThrow(fd, 'fchown').chown(uid, gid);
  1493. }
  1494. fchownSync(fd, uid, gid) {
  1495. validateUid(uid);
  1496. validateGid(gid);
  1497. this.fchownBase(fd, uid, gid);
  1498. }
  1499. fchown(fd, uid, gid, callback) {
  1500. validateUid(uid);
  1501. validateGid(gid);
  1502. this.wrapAsync(this.fchownBase, [fd, uid, gid], callback);
  1503. }
  1504. chownBase(filename, uid, gid) {
  1505. const link = this.getResolvedLinkOrThrow(filename, 'chown');
  1506. const node = link.getNode();
  1507. node.chown(uid, gid);
  1508. // if(node.isFile() || node.isSymlink()) {
  1509. //
  1510. // } else if(node.isDirectory()) {
  1511. //
  1512. // } else {
  1513. // TODO: What do we do here?
  1514. // }
  1515. }
  1516. chownSync(path, uid, gid) {
  1517. validateUid(uid);
  1518. validateGid(gid);
  1519. this.chownBase((0, util_1.pathToFilename)(path), uid, gid);
  1520. }
  1521. chown(path, uid, gid, callback) {
  1522. validateUid(uid);
  1523. validateGid(gid);
  1524. this.wrapAsync(this.chownBase, [(0, util_1.pathToFilename)(path), uid, gid], callback);
  1525. }
  1526. lchownBase(filename, uid, gid) {
  1527. this.getLinkOrThrow(filename, 'lchown').getNode().chown(uid, gid);
  1528. }
  1529. lchownSync(path, uid, gid) {
  1530. validateUid(uid);
  1531. validateGid(gid);
  1532. this.lchownBase((0, util_1.pathToFilename)(path), uid, gid);
  1533. }
  1534. lchown(path, uid, gid, callback) {
  1535. validateUid(uid);
  1536. validateGid(gid);
  1537. this.wrapAsync(this.lchownBase, [(0, util_1.pathToFilename)(path), uid, gid], callback);
  1538. }
  1539. watchFile(path, a, b) {
  1540. const filename = (0, util_1.pathToFilename)(path);
  1541. let options = a;
  1542. let listener = b;
  1543. if (typeof options === 'function') {
  1544. listener = a;
  1545. options = null;
  1546. }
  1547. if (typeof listener !== 'function') {
  1548. throw Error('"watchFile()" requires a listener function');
  1549. }
  1550. let interval = 5007;
  1551. let persistent = true;
  1552. if (options && typeof options === 'object') {
  1553. if (typeof options.interval === 'number')
  1554. interval = options.interval;
  1555. if (typeof options.persistent === 'boolean')
  1556. persistent = options.persistent;
  1557. }
  1558. let watcher = this.statWatchers[filename];
  1559. if (!watcher) {
  1560. watcher = new this.StatWatcher();
  1561. watcher.start(filename, persistent, interval);
  1562. this.statWatchers[filename] = watcher;
  1563. }
  1564. watcher.addListener('change', listener);
  1565. return watcher;
  1566. }
  1567. unwatchFile(path, listener) {
  1568. const filename = (0, util_1.pathToFilename)(path);
  1569. const watcher = this.statWatchers[filename];
  1570. if (!watcher)
  1571. return;
  1572. if (typeof listener === 'function') {
  1573. watcher.removeListener('change', listener);
  1574. }
  1575. else {
  1576. watcher.removeAllListeners('change');
  1577. }
  1578. if (watcher.listenerCount('change') === 0) {
  1579. watcher.stop();
  1580. delete this.statWatchers[filename];
  1581. }
  1582. }
  1583. createReadStream(path, options) {
  1584. return new this.ReadStream(path, options);
  1585. }
  1586. createWriteStream(path, options) {
  1587. return new this.WriteStream(path, options);
  1588. }
  1589. // watch(path: PathLike): FSWatcher;
  1590. // watch(path: PathLike, options?: IWatchOptions | string): FSWatcher;
  1591. watch(path, options, listener) {
  1592. const filename = (0, util_1.pathToFilename)(path);
  1593. let givenOptions = options;
  1594. if (typeof options === 'function') {
  1595. listener = options;
  1596. givenOptions = null;
  1597. }
  1598. // tslint:disable-next-line prefer-const
  1599. let { persistent, recursive, encoding } = (0, options_1.getDefaultOpts)(givenOptions);
  1600. if (persistent === undefined)
  1601. persistent = true;
  1602. if (recursive === undefined)
  1603. recursive = false;
  1604. const watcher = new this.FSWatcher();
  1605. watcher.start(filename, persistent, recursive, encoding);
  1606. if (listener) {
  1607. watcher.addListener('change', listener);
  1608. }
  1609. return watcher;
  1610. }
  1611. opendirBase(filename, options) {
  1612. const link = this.getResolvedLinkOrThrow(filename, 'scandir');
  1613. const node = link.getNode();
  1614. if (!node.isDirectory())
  1615. throw (0, util_1.createError)(ENOTDIR, 'scandir', filename);
  1616. return new Dir_1.Dir(link, options);
  1617. }
  1618. opendirSync(path, options) {
  1619. const opts = (0, options_1.getOpendirOptions)(options);
  1620. const filename = (0, util_1.pathToFilename)(path);
  1621. return this.opendirBase(filename, opts);
  1622. }
  1623. opendir(path, a, b) {
  1624. const [options, callback] = (0, options_1.getOpendirOptsAndCb)(a, b);
  1625. const filename = (0, util_1.pathToFilename)(path);
  1626. this.wrapAsync(this.opendirBase, [filename, options], callback);
  1627. }
  1628. }
  1629. exports.Volume = Volume;
  1630. /**
  1631. * Global file descriptor counter. UNIX file descriptors start from 0 and go sequentially
  1632. * up, so here, in order not to conflict with them, we choose some big number and descrease
  1633. * the file descriptor of every new opened file.
  1634. * @type {number}
  1635. * @todo This should not be static, right?
  1636. */
  1637. Volume.fd = 0x7fffffff;
  1638. function emitStop(self) {
  1639. self.emit('stop');
  1640. }
  1641. class StatWatcher extends events_1.EventEmitter {
  1642. constructor(vol) {
  1643. super();
  1644. this.onInterval = () => {
  1645. try {
  1646. const stats = this.vol.statSync(this.filename);
  1647. if (this.hasChanged(stats)) {
  1648. this.emit('change', stats, this.prev);
  1649. this.prev = stats;
  1650. }
  1651. }
  1652. finally {
  1653. this.loop();
  1654. }
  1655. };
  1656. this.vol = vol;
  1657. }
  1658. loop() {
  1659. this.timeoutRef = this.setTimeout(this.onInterval, this.interval);
  1660. }
  1661. hasChanged(stats) {
  1662. // if(!this.prev) return false;
  1663. if (stats.mtimeMs > this.prev.mtimeMs)
  1664. return true;
  1665. if (stats.nlink !== this.prev.nlink)
  1666. return true;
  1667. return false;
  1668. }
  1669. start(path, persistent = true, interval = 5007) {
  1670. this.filename = (0, util_1.pathToFilename)(path);
  1671. this.setTimeout = persistent
  1672. ? setTimeout.bind(typeof globalThis !== 'undefined' ? globalThis : global)
  1673. : setTimeoutUnref_1.default;
  1674. this.interval = interval;
  1675. this.prev = this.vol.statSync(this.filename);
  1676. this.loop();
  1677. }
  1678. stop() {
  1679. clearTimeout(this.timeoutRef);
  1680. (0, queueMicrotask_1.default)(() => {
  1681. emitStop.call(this, this);
  1682. });
  1683. }
  1684. }
  1685. exports.StatWatcher = StatWatcher;
  1686. /* tslint:disable no-var-keyword prefer-const */
  1687. // ---------------------------------------- ReadStream
  1688. var pool;
  1689. function allocNewPool(poolSize) {
  1690. pool = (0, buffer_1.bufferAllocUnsafe)(poolSize);
  1691. pool.used = 0;
  1692. }
  1693. util.inherits(FsReadStream, stream_1.Readable);
  1694. exports.ReadStream = FsReadStream;
  1695. function FsReadStream(vol, path, options) {
  1696. if (!(this instanceof FsReadStream))
  1697. return new FsReadStream(vol, path, options);
  1698. this._vol = vol;
  1699. // a little bit bigger buffer and water marks by default
  1700. options = Object.assign({}, (0, options_1.getOptions)(options, {}));
  1701. if (options.highWaterMark === undefined)
  1702. options.highWaterMark = 64 * 1024;
  1703. stream_1.Readable.call(this, options);
  1704. this.path = (0, util_1.pathToFilename)(path);
  1705. this.fd = options.fd === undefined ? null : typeof options.fd !== 'number' ? options.fd.fd : options.fd;
  1706. this.flags = options.flags === undefined ? 'r' : options.flags;
  1707. this.mode = options.mode === undefined ? 0o666 : options.mode;
  1708. this.start = options.start;
  1709. this.end = options.end;
  1710. this.autoClose = options.autoClose === undefined ? true : options.autoClose;
  1711. this.pos = undefined;
  1712. this.bytesRead = 0;
  1713. if (this.start !== undefined) {
  1714. if (typeof this.start !== 'number') {
  1715. throw new TypeError('"start" option must be a Number');
  1716. }
  1717. if (this.end === undefined) {
  1718. this.end = Infinity;
  1719. }
  1720. else if (typeof this.end !== 'number') {
  1721. throw new TypeError('"end" option must be a Number');
  1722. }
  1723. if (this.start > this.end) {
  1724. throw new Error('"start" option must be <= "end" option');
  1725. }
  1726. this.pos = this.start;
  1727. }
  1728. if (typeof this.fd !== 'number')
  1729. this.open();
  1730. this.on('end', function () {
  1731. if (this.autoClose) {
  1732. if (this.destroy)
  1733. this.destroy();
  1734. }
  1735. });
  1736. }
  1737. FsReadStream.prototype.open = function () {
  1738. var self = this; // tslint:disable-line no-this-assignment
  1739. this._vol.open(this.path, this.flags, this.mode, (er, fd) => {
  1740. if (er) {
  1741. if (self.autoClose) {
  1742. if (self.destroy)
  1743. self.destroy();
  1744. }
  1745. self.emit('error', er);
  1746. return;
  1747. }
  1748. self.fd = fd;
  1749. self.emit('open', fd);
  1750. // start the flow of data.
  1751. self.read();
  1752. });
  1753. };
  1754. FsReadStream.prototype._read = function (n) {
  1755. if (typeof this.fd !== 'number') {
  1756. return this.once('open', function () {
  1757. this._read(n);
  1758. });
  1759. }
  1760. if (this.destroyed)
  1761. return;
  1762. if (!pool || pool.length - pool.used < kMinPoolSpace) {
  1763. // discard the old pool.
  1764. allocNewPool(this._readableState.highWaterMark);
  1765. }
  1766. // Grab another reference to the pool in the case that while we're
  1767. // in the thread pool another read() finishes up the pool, and
  1768. // allocates a new one.
  1769. var thisPool = pool;
  1770. var toRead = Math.min(pool.length - pool.used, n);
  1771. var start = pool.used;
  1772. if (this.pos !== undefined)
  1773. toRead = Math.min(this.end - this.pos + 1, toRead);
  1774. // already read everything we were supposed to read!
  1775. // treat as EOF.
  1776. if (toRead <= 0)
  1777. return this.push(null);
  1778. // the actual read.
  1779. var self = this; // tslint:disable-line no-this-assignment
  1780. this._vol.read(this.fd, pool, pool.used, toRead, this.pos, onread);
  1781. // move the pool positions, and internal position for reading.
  1782. if (this.pos !== undefined)
  1783. this.pos += toRead;
  1784. pool.used += toRead;
  1785. function onread(er, bytesRead) {
  1786. if (er) {
  1787. if (self.autoClose && self.destroy) {
  1788. self.destroy();
  1789. }
  1790. self.emit('error', er);
  1791. }
  1792. else {
  1793. var b = null;
  1794. if (bytesRead > 0) {
  1795. self.bytesRead += bytesRead;
  1796. b = thisPool.slice(start, start + bytesRead);
  1797. }
  1798. self.push(b);
  1799. }
  1800. }
  1801. };
  1802. FsReadStream.prototype._destroy = function (err, cb) {
  1803. this.close(err2 => {
  1804. cb(err || err2);
  1805. });
  1806. };
  1807. FsReadStream.prototype.close = function (cb) {
  1808. var _a;
  1809. if (cb)
  1810. this.once('close', cb);
  1811. if (this.closed || typeof this.fd !== 'number') {
  1812. if (typeof this.fd !== 'number') {
  1813. this.once('open', closeOnOpen);
  1814. return;
  1815. }
  1816. return (0, queueMicrotask_1.default)(() => this.emit('close'));
  1817. }
  1818. // Since Node 18, there is only a getter for '.closed'.
  1819. // The first branch mimics other setters from Readable.
  1820. // See https://github.com/nodejs/node/blob/v18.0.0/lib/internal/streams/readable.js#L1243
  1821. if (typeof ((_a = this._readableState) === null || _a === void 0 ? void 0 : _a.closed) === 'boolean') {
  1822. this._readableState.closed = true;
  1823. }
  1824. else {
  1825. this.closed = true;
  1826. }
  1827. this._vol.close(this.fd, er => {
  1828. if (er)
  1829. this.emit('error', er);
  1830. else
  1831. this.emit('close');
  1832. });
  1833. this.fd = null;
  1834. };
  1835. // needed because as it will be called with arguments
  1836. // that does not match this.close() signature
  1837. function closeOnOpen(fd) {
  1838. this.close();
  1839. }
  1840. util.inherits(FsWriteStream, stream_1.Writable);
  1841. exports.WriteStream = FsWriteStream;
  1842. function FsWriteStream(vol, path, options) {
  1843. if (!(this instanceof FsWriteStream))
  1844. return new FsWriteStream(vol, path, options);
  1845. this._vol = vol;
  1846. options = Object.assign({}, (0, options_1.getOptions)(options, {}));
  1847. stream_1.Writable.call(this, options);
  1848. this.path = (0, util_1.pathToFilename)(path);
  1849. this.fd = options.fd === undefined ? null : typeof options.fd !== 'number' ? options.fd.fd : options.fd;
  1850. this.flags = options.flags === undefined ? 'w' : options.flags;
  1851. this.mode = options.mode === undefined ? 0o666 : options.mode;
  1852. this.start = options.start;
  1853. this.autoClose = options.autoClose === undefined ? true : !!options.autoClose;
  1854. this.pos = undefined;
  1855. this.bytesWritten = 0;
  1856. this.pending = true;
  1857. if (this.start !== undefined) {
  1858. if (typeof this.start !== 'number') {
  1859. throw new TypeError('"start" option must be a Number');
  1860. }
  1861. if (this.start < 0) {
  1862. throw new Error('"start" must be >= zero');
  1863. }
  1864. this.pos = this.start;
  1865. }
  1866. if (options.encoding)
  1867. this.setDefaultEncoding(options.encoding);
  1868. if (typeof this.fd !== 'number')
  1869. this.open();
  1870. // dispose on finish.
  1871. this.once('finish', function () {
  1872. if (this.autoClose) {
  1873. this.close();
  1874. }
  1875. });
  1876. }
  1877. FsWriteStream.prototype.open = function () {
  1878. this._vol.open(this.path, this.flags, this.mode, function (er, fd) {
  1879. if (er) {
  1880. if (this.autoClose && this.destroy) {
  1881. this.destroy();
  1882. }
  1883. this.emit('error', er);
  1884. return;
  1885. }
  1886. this.fd = fd;
  1887. this.pending = false;
  1888. this.emit('open', fd);
  1889. }.bind(this));
  1890. };
  1891. FsWriteStream.prototype._write = function (data, encoding, cb) {
  1892. if (!(data instanceof buffer_1.Buffer || data instanceof Uint8Array))
  1893. return this.emit('error', new Error('Invalid data'));
  1894. if (typeof this.fd !== 'number') {
  1895. return this.once('open', function () {
  1896. this._write(data, encoding, cb);
  1897. });
  1898. }
  1899. var self = this; // tslint:disable-line no-this-assignment
  1900. this._vol.write(this.fd, data, 0, data.length, this.pos, (er, bytes) => {
  1901. if (er) {
  1902. if (self.autoClose && self.destroy) {
  1903. self.destroy();
  1904. }
  1905. return cb(er);
  1906. }
  1907. self.bytesWritten += bytes;
  1908. cb();
  1909. });
  1910. if (this.pos !== undefined)
  1911. this.pos += data.length;
  1912. };
  1913. FsWriteStream.prototype._writev = function (data, cb) {
  1914. if (typeof this.fd !== 'number') {
  1915. return this.once('open', function () {
  1916. this._writev(data, cb);
  1917. });
  1918. }
  1919. const self = this; // tslint:disable-line no-this-assignment
  1920. const len = data.length;
  1921. const chunks = new Array(len);
  1922. var size = 0;
  1923. for (var i = 0; i < len; i++) {
  1924. var chunk = data[i].chunk;
  1925. chunks[i] = chunk;
  1926. size += chunk.length;
  1927. }
  1928. const buf = buffer_1.Buffer.concat(chunks);
  1929. this._vol.write(this.fd, buf, 0, buf.length, this.pos, (er, bytes) => {
  1930. if (er) {
  1931. if (self.destroy)
  1932. self.destroy();
  1933. return cb(er);
  1934. }
  1935. self.bytesWritten += bytes;
  1936. cb();
  1937. });
  1938. if (this.pos !== undefined)
  1939. this.pos += size;
  1940. };
  1941. FsWriteStream.prototype.close = function (cb) {
  1942. var _a;
  1943. if (cb)
  1944. this.once('close', cb);
  1945. if (this.closed || typeof this.fd !== 'number') {
  1946. if (typeof this.fd !== 'number') {
  1947. this.once('open', closeOnOpen);
  1948. return;
  1949. }
  1950. return (0, queueMicrotask_1.default)(() => this.emit('close'));
  1951. }
  1952. // Since Node 18, there is only a getter for '.closed'.
  1953. // The first branch mimics other setters from Writable.
  1954. // See https://github.com/nodejs/node/blob/v18.0.0/lib/internal/streams/writable.js#L766
  1955. if (typeof ((_a = this._writableState) === null || _a === void 0 ? void 0 : _a.closed) === 'boolean') {
  1956. this._writableState.closed = true;
  1957. }
  1958. else {
  1959. this.closed = true;
  1960. }
  1961. this._vol.close(this.fd, er => {
  1962. if (er)
  1963. this.emit('error', er);
  1964. else
  1965. this.emit('close');
  1966. });
  1967. this.fd = null;
  1968. };
  1969. FsWriteStream.prototype._destroy = FsReadStream.prototype._destroy;
  1970. // There is no shutdown() for files.
  1971. FsWriteStream.prototype.destroySoon = FsWriteStream.prototype.end;
  1972. // ---------------------------------------- FSWatcher
  1973. class FSWatcher extends events_1.EventEmitter {
  1974. constructor(vol) {
  1975. super();
  1976. this._filename = '';
  1977. this._filenameEncoded = '';
  1978. // _persistent: boolean = true;
  1979. this._recursive = false;
  1980. this._encoding = encoding_1.ENCODING_UTF8;
  1981. // inode -> removers
  1982. this._listenerRemovers = new Map();
  1983. this._onParentChild = (link) => {
  1984. if (link.getName() === this._getName()) {
  1985. this._emit('rename');
  1986. }
  1987. };
  1988. this._emit = (type) => {
  1989. this.emit('change', type, this._filenameEncoded);
  1990. };
  1991. this._persist = () => {
  1992. this._timer = setTimeout(this._persist, 1e6);
  1993. };
  1994. this._vol = vol;
  1995. // TODO: Emit "error" messages when watching.
  1996. // this._handle.onchange = function(status, eventType, filename) {
  1997. // if (status < 0) {
  1998. // self._handle.close();
  1999. // const error = !filename ?
  2000. // errnoException(status, 'Error watching file for changes:') :
  2001. // errnoException(status, `Error watching file ${filename} for changes:`);
  2002. // error.filename = filename;
  2003. // self.emit('error', error);
  2004. // } else {
  2005. // self.emit('change', eventType, filename);
  2006. // }
  2007. // };
  2008. }
  2009. _getName() {
  2010. return this._steps[this._steps.length - 1];
  2011. }
  2012. start(path, persistent = true, recursive = false, encoding = encoding_1.ENCODING_UTF8) {
  2013. this._filename = (0, util_1.pathToFilename)(path);
  2014. this._steps = filenameToSteps(this._filename);
  2015. this._filenameEncoded = (0, encoding_1.strToEncoding)(this._filename);
  2016. // this._persistent = persistent;
  2017. this._recursive = recursive;
  2018. this._encoding = encoding;
  2019. try {
  2020. this._link = this._vol.getLinkOrThrow(this._filename, 'FSWatcher');
  2021. }
  2022. catch (err) {
  2023. const error = new Error(`watch ${this._filename} ${err.code}`);
  2024. error.code = err.code;
  2025. error.errno = err.code;
  2026. throw error;
  2027. }
  2028. const watchLinkNodeChanged = (link) => {
  2029. var _a;
  2030. const filepath = link.getPath();
  2031. const node = link.getNode();
  2032. const onNodeChange = () => {
  2033. let filename = relative(this._filename, filepath);
  2034. if (!filename) {
  2035. filename = this._getName();
  2036. }
  2037. return this.emit('change', 'change', filename);
  2038. };
  2039. node.on('change', onNodeChange);
  2040. const removers = (_a = this._listenerRemovers.get(node.ino)) !== null && _a !== void 0 ? _a : [];
  2041. removers.push(() => node.removeListener('change', onNodeChange));
  2042. this._listenerRemovers.set(node.ino, removers);
  2043. };
  2044. const watchLinkChildrenChanged = (link) => {
  2045. var _a;
  2046. const node = link.getNode();
  2047. // when a new link added
  2048. const onLinkChildAdd = (l) => {
  2049. this.emit('change', 'rename', relative(this._filename, l.getPath()));
  2050. setTimeout(() => {
  2051. // 1. watch changes of the new link-node
  2052. watchLinkNodeChanged(l);
  2053. // 2. watch changes of the new link-node's children
  2054. watchLinkChildrenChanged(l);
  2055. });
  2056. };
  2057. // when a new link deleted
  2058. const onLinkChildDelete = (l) => {
  2059. // remove the listeners of the children nodes
  2060. const removeLinkNodeListeners = (curLink) => {
  2061. const ino = curLink.getNode().ino;
  2062. const removers = this._listenerRemovers.get(ino);
  2063. if (removers) {
  2064. removers.forEach(r => r());
  2065. this._listenerRemovers.delete(ino);
  2066. }
  2067. for (const [name, childLink] of curLink.children.entries()) {
  2068. if (childLink && name !== '.' && name !== '..') {
  2069. removeLinkNodeListeners(childLink);
  2070. }
  2071. }
  2072. };
  2073. removeLinkNodeListeners(l);
  2074. this.emit('change', 'rename', relative(this._filename, l.getPath()));
  2075. };
  2076. // children nodes changed
  2077. for (const [name, childLink] of link.children.entries()) {
  2078. if (childLink && name !== '.' && name !== '..') {
  2079. watchLinkNodeChanged(childLink);
  2080. }
  2081. }
  2082. // link children add/remove
  2083. link.on('child:add', onLinkChildAdd);
  2084. link.on('child:delete', onLinkChildDelete);
  2085. const removers = (_a = this._listenerRemovers.get(node.ino)) !== null && _a !== void 0 ? _a : [];
  2086. removers.push(() => {
  2087. link.removeListener('child:add', onLinkChildAdd);
  2088. link.removeListener('child:delete', onLinkChildDelete);
  2089. });
  2090. if (recursive) {
  2091. for (const [name, childLink] of link.children.entries()) {
  2092. if (childLink && name !== '.' && name !== '..') {
  2093. watchLinkChildrenChanged(childLink);
  2094. }
  2095. }
  2096. }
  2097. };
  2098. watchLinkNodeChanged(this._link);
  2099. watchLinkChildrenChanged(this._link);
  2100. const parent = this._link.parent;
  2101. if (parent) {
  2102. // parent.on('child:add', this._onParentChild);
  2103. parent.setMaxListeners(parent.getMaxListeners() + 1);
  2104. parent.on('child:delete', this._onParentChild);
  2105. }
  2106. if (persistent)
  2107. this._persist();
  2108. }
  2109. close() {
  2110. clearTimeout(this._timer);
  2111. this._listenerRemovers.forEach(removers => {
  2112. removers.forEach(r => r());
  2113. });
  2114. this._listenerRemovers.clear();
  2115. const parent = this._link.parent;
  2116. if (parent) {
  2117. // parent.removeListener('child:add', this._onParentChild);
  2118. parent.removeListener('child:delete', this._onParentChild);
  2119. }
  2120. }
  2121. }
  2122. exports.FSWatcher = FSWatcher;
  2123. //# sourceMappingURL=volume.js.map