1 | 'use strict';
|
2 |
|
3 | const {AbortError} = require('./error');
|
4 | const {FSReqCallback} = process.binding('fs');
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 | exports.getReadFileContextPrototype = function() {
|
12 | const fs = require('fs');
|
13 | const fsBinding = process.binding('fs');
|
14 |
|
15 | const originalOpen = fsBinding.open;
|
16 |
|
17 | let proto;
|
18 | fsBinding.open = (_path, _flags, _mode, req) => {
|
19 | proto = Object.getPrototypeOf(req.context);
|
20 | return originalOpen.apply(fsBinding, [_path, _flags, _mode, req]);
|
21 | };
|
22 |
|
23 | fs.readFile('/ignored.txt', () => {});
|
24 |
|
25 | fsBinding.open = originalOpen;
|
26 |
|
27 | return proto;
|
28 | };
|
29 |
|
30 |
|
31 |
|
32 |
|
33 |
|
34 |
|
35 |
|
36 |
|
37 |
|
38 |
|
39 |
|
40 |
|
41 |
|
42 | exports.patchReadFileContext = function(prototype) {
|
43 | const origRead = prototype.read;
|
44 | const origClose = prototype.close;
|
45 |
|
46 | const kReadFileUnknownBufferLength = 64 * 1024;
|
47 | const kReadFileBufferLength = 512 * 1024;
|
48 |
|
49 | function readFileAfterRead(err, bytesRead) {
|
50 | const context = this.context;
|
51 |
|
52 | if (err) {
|
53 | return context.close(err);
|
54 | }
|
55 | context.pos += bytesRead;
|
56 |
|
57 | if (context.pos === context.size || bytesRead === 0) {
|
58 | context.close();
|
59 | } else {
|
60 | if (context.size === 0) {
|
61 |
|
62 | const buffer =
|
63 | bytesRead === kReadFileUnknownBufferLength
|
64 | ? context.buffer
|
65 | : context.buffer.slice(0, bytesRead);
|
66 | context.buffers.push(buffer);
|
67 | }
|
68 | context.read();
|
69 | }
|
70 | }
|
71 |
|
72 | function readFileAfterClose(err) {
|
73 | const context = this.context;
|
74 | const callback = context.callback;
|
75 | let buffer = null;
|
76 |
|
77 | if (context.err || err) {
|
78 |
|
79 | return callback(context.err || err);
|
80 | }
|
81 |
|
82 | try {
|
83 | if (context.size === 0) {
|
84 | buffer = Buffer.concat(context.buffers, context.pos);
|
85 | } else if (context.pos < context.size) {
|
86 | buffer = context.buffer.slice(0, context.pos);
|
87 | } else {
|
88 | buffer = context.buffer;
|
89 | }
|
90 |
|
91 | if (context.encoding) {
|
92 | buffer = buffer.toString(context.encoding);
|
93 | }
|
94 | } catch (err) {
|
95 | return callback(err);
|
96 | }
|
97 |
|
98 | callback(null, buffer);
|
99 | }
|
100 |
|
101 | prototype.read = function read() {
|
102 | if (!prototype._mockedBinding) {
|
103 | return origRead.apply(this, arguments);
|
104 | }
|
105 |
|
106 | let buffer;
|
107 | let offset;
|
108 | let length;
|
109 |
|
110 | if (this.signal && this.signal.aborted) {
|
111 | return this.close(new AbortError());
|
112 | }
|
113 | if (this.size === 0) {
|
114 | buffer = Buffer.allocUnsafeSlow(kReadFileUnknownBufferLength);
|
115 | offset = 0;
|
116 | length = kReadFileUnknownBufferLength;
|
117 | this.buffer = buffer;
|
118 | } else {
|
119 | buffer = this.buffer;
|
120 | offset = this.pos;
|
121 | length = Math.min(kReadFileBufferLength, this.size - this.pos);
|
122 | }
|
123 |
|
124 | const req = new FSReqCallback();
|
125 | req.oncomplete = readFileAfterRead;
|
126 | req.context = this;
|
127 |
|
128 |
|
129 |
|
130 |
|
131 | prototype._mockedBinding.read(this.fd, buffer, offset, length, -1, req);
|
132 | };
|
133 |
|
134 | prototype.close = function close(err) {
|
135 | if (!prototype._mockedBinding) {
|
136 | return origClose.apply(this, arguments);
|
137 | }
|
138 |
|
139 | if (this.isUserFd) {
|
140 | process.nextTick(function tick(context) {
|
141 | readFileAfterClose.apply({context}, [null]);
|
142 | }, this);
|
143 | return;
|
144 | }
|
145 |
|
146 | const req = new FSReqCallback();
|
147 | req.oncomplete = readFileAfterClose;
|
148 | req.context = this;
|
149 | this.err = err;
|
150 |
|
151 | prototype._mockedBinding.close(this.fd, req);
|
152 | };
|
153 | };
|