1 | 'use strict';
|
2 |
|
3 | const Fs = require('fs');
|
4 | const Util = require('util');
|
5 |
|
6 | const Boom = require('@hapi/boom');
|
7 | const Bounce = require('@hapi/bounce');
|
8 | const Hoek = require('@hapi/hoek');
|
9 |
|
10 |
|
11 | const internals = {
|
12 | methods: {
|
13 | promised: ['open', 'close', 'fstat', 'readdir'],
|
14 | raw: ['createReadStream']
|
15 | }
|
16 | };
|
17 |
|
18 |
|
19 | exports.File = function (path) {
|
20 |
|
21 | this.path = path;
|
22 | this.fd = null;
|
23 | };
|
24 |
|
25 |
|
26 | exports.File.prototype.open = async function (mode) {
|
27 |
|
28 | Hoek.assert(this.fd === null);
|
29 |
|
30 | try {
|
31 | this.fd = await exports.open(this.path, mode);
|
32 | }
|
33 | catch (err) {
|
34 | const data = { path: this.path };
|
35 |
|
36 | if (this.path.indexOf('\u0000') !== -1 || err.code === 'ENOENT') {
|
37 | throw Boom.notFound(null, data);
|
38 | }
|
39 |
|
40 | if (err.code === 'EACCES' || err.code === 'EPERM') {
|
41 | data.code = err.code;
|
42 | throw Boom.forbidden(null, data);
|
43 | }
|
44 |
|
45 | throw Boom.boomify(err, { message: 'Failed to open file', data });
|
46 | }
|
47 | };
|
48 |
|
49 |
|
50 | exports.File.prototype.close = function () {
|
51 |
|
52 | if (this.fd !== null) {
|
53 | Bounce.background(exports.close(this.fd));
|
54 | this.fd = null;
|
55 | }
|
56 | };
|
57 |
|
58 |
|
59 | exports.File.prototype.stat = async function () {
|
60 |
|
61 | Hoek.assert(this.fd !== null);
|
62 |
|
63 | try {
|
64 | const stat = await exports.fstat(this.fd);
|
65 |
|
66 | if (stat.isDirectory()) {
|
67 | throw Boom.forbidden(null, { code: 'EISDIR', path: this.path });
|
68 | }
|
69 |
|
70 | return stat;
|
71 | }
|
72 | catch (err) {
|
73 | this.close(this.fd);
|
74 |
|
75 | Bounce.rethrow(err, ['boom', 'system']);
|
76 | throw Boom.boomify(err, { message: 'Failed to stat file', data: { path: this.path } });
|
77 | }
|
78 | };
|
79 |
|
80 |
|
81 | exports.File.prototype.openStat = async function (mode) {
|
82 |
|
83 | await this.open(mode);
|
84 | return this.stat();
|
85 | };
|
86 |
|
87 |
|
88 | exports.File.prototype.createReadStream = function (options) {
|
89 |
|
90 | Hoek.assert(this.fd !== null);
|
91 |
|
92 | options = Object.assign({ fd: this.fd, start: 0 }, options);
|
93 |
|
94 | const stream = exports.createReadStream(this.path, options);
|
95 |
|
96 | if (options.autoClose !== false) {
|
97 | this.fd = null;
|
98 | }
|
99 |
|
100 | return stream;
|
101 | };
|
102 |
|
103 |
|
104 |
|
105 |
|
106 | for (let i = 0; i < internals.methods.raw.length; ++i) {
|
107 | const method = internals.methods.raw[i];
|
108 | exports[method] = Fs[method].bind(Fs);
|
109 | }
|
110 |
|
111 | for (let i = 0; i < internals.methods.promised.length; ++i) {
|
112 | const method = internals.methods.promised[i];
|
113 | exports[method] = Util.promisify(Fs[method]);
|
114 | }
|