graceful-fs.js 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. var fs = require('fs')
  2. var polyfills = require('./polyfills.js')
  3. var legacy = require('./legacy-streams.js')
  4. var clone = require('./clone.js')
  5. var util = require('util')
  6. /* istanbul ignore next - node 0.x polyfill */
  7. var gracefulQueue
  8. var previousSymbol
  9. /* istanbul ignore else - node 0.x polyfill */
  10. if (typeof Symbol === 'function' && typeof Symbol.for === 'function') {
  11. gracefulQueue = Symbol.for('graceful-fs.queue')
  12. // This is used in testing by future versions
  13. previousSymbol = Symbol.for('graceful-fs.previous')
  14. } else {
  15. gracefulQueue = '___graceful-fs.queue'
  16. previousSymbol = '___graceful-fs.previous'
  17. }
  18. function noop () {}
  19. function publishQueue(context, queue) {
  20. Object.defineProperty(context, gracefulQueue, {
  21. get: function() {
  22. return queue
  23. }
  24. })
  25. }
  26. var debug = noop
  27. if (util.debuglog)
  28. debug = util.debuglog('gfs4')
  29. else if (/\bgfs4\b/i.test(process.env.NODE_DEBUG || ''))
  30. debug = function() {
  31. var m = util.format.apply(util, arguments)
  32. m = 'GFS4: ' + m.split(/\n/).join('\nGFS4: ')
  33. console.error(m)
  34. }
  35. // Once time initialization
  36. if (!fs[gracefulQueue]) {
  37. // This queue can be shared by multiple loaded instances
  38. var queue = global[gracefulQueue] || []
  39. publishQueue(fs, queue)
  40. // Patch fs.close/closeSync to shared queue version, because we need
  41. // to retry() whenever a close happens *anywhere* in the program.
  42. // This is essential when multiple graceful-fs instances are
  43. // in play at the same time.
  44. fs.close = (function (fs$close) {
  45. function close (fd, cb) {
  46. return fs$close.call(fs, fd, function (err) {
  47. // This function uses the graceful-fs shared queue
  48. if (!err) {
  49. retry()
  50. }
  51. if (typeof cb === 'function')
  52. cb.apply(this, arguments)
  53. })
  54. }
  55. Object.defineProperty(close, previousSymbol, {
  56. value: fs$close
  57. })
  58. return close
  59. })(fs.close)
  60. fs.closeSync = (function (fs$closeSync) {
  61. function closeSync (fd) {
  62. // This function uses the graceful-fs shared queue
  63. fs$closeSync.apply(fs, arguments)
  64. retry()
  65. }
  66. Object.defineProperty(closeSync, previousSymbol, {
  67. value: fs$closeSync
  68. })
  69. return closeSync
  70. })(fs.closeSync)
  71. if (/\bgfs4\b/i.test(process.env.NODE_DEBUG || '')) {
  72. process.on('exit', function() {
  73. debug(fs[gracefulQueue])
  74. require('assert').equal(fs[gracefulQueue].length, 0)
  75. })
  76. }
  77. }
  78. if (!global[gracefulQueue]) {
  79. publishQueue(global, fs[gracefulQueue]);
  80. }
  81. module.exports = patch(clone(fs))
  82. if (process.env.TEST_GRACEFUL_FS_GLOBAL_PATCH && !fs.__patched) {
  83. module.exports = patch(fs)
  84. fs.__patched = true;
  85. }
  86. function patch (fs) {
  87. // Everything that references the open() function needs to be in here
  88. polyfills(fs)
  89. fs.gracefulify = patch
  90. fs.createReadStream = createReadStream
  91. fs.createWriteStream = createWriteStream
  92. var fs$readFile = fs.readFile
  93. fs.readFile = readFile
  94. function readFile (path, options, cb) {
  95. if (typeof options === 'function')
  96. cb = options, options = null
  97. return go$readFile(path, options, cb)
  98. function go$readFile (path, options, cb) {
  99. return fs$readFile(path, options, function (err) {
  100. if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
  101. enqueue([go$readFile, [path, options, cb]])
  102. else {
  103. if (typeof cb === 'function')
  104. cb.apply(this, arguments)
  105. retry()
  106. }
  107. })
  108. }
  109. }
  110. var fs$writeFile = fs.writeFile
  111. fs.writeFile = writeFile
  112. function writeFile (path, data, options, cb) {
  113. if (typeof options === 'function')
  114. cb = options, options = null
  115. return go$writeFile(path, data, options, cb)
  116. function go$writeFile (path, data, options, cb) {
  117. return fs$writeFile(path, data, options, function (err) {
  118. if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
  119. enqueue([go$writeFile, [path, data, options, cb]])
  120. else {
  121. if (typeof cb === 'function')
  122. cb.apply(this, arguments)
  123. retry()
  124. }
  125. })
  126. }
  127. }
  128. var fs$appendFile = fs.appendFile
  129. if (fs$appendFile)
  130. fs.appendFile = appendFile
  131. function appendFile (path, data, options, cb) {
  132. if (typeof options === 'function')
  133. cb = options, options = null
  134. return go$appendFile(path, data, options, cb)
  135. function go$appendFile (path, data, options, cb) {
  136. return fs$appendFile(path, data, options, function (err) {
  137. if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
  138. enqueue([go$appendFile, [path, data, options, cb]])
  139. else {
  140. if (typeof cb === 'function')
  141. cb.apply(this, arguments)
  142. retry()
  143. }
  144. })
  145. }
  146. }
  147. var fs$copyFile = fs.copyFile
  148. if (fs$copyFile)
  149. fs.copyFile = copyFile
  150. function copyFile (src, dest, flags, cb) {
  151. if (typeof flags === 'function') {
  152. cb = flags
  153. flags = 0
  154. }
  155. return fs$copyFile(src, dest, flags, function (err) {
  156. if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
  157. enqueue([fs$copyFile, [src, dest, flags, cb]])
  158. else {
  159. if (typeof cb === 'function')
  160. cb.apply(this, arguments)
  161. retry()
  162. }
  163. })
  164. }
  165. var fs$readdir = fs.readdir
  166. fs.readdir = readdir
  167. function readdir (path, options, cb) {
  168. var args = [path]
  169. if (typeof options !== 'function') {
  170. args.push(options)
  171. } else {
  172. cb = options
  173. }
  174. args.push(go$readdir$cb)
  175. return go$readdir(args)
  176. function go$readdir$cb (err, files) {
  177. if (files && files.sort)
  178. files.sort()
  179. if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
  180. enqueue([go$readdir, [args]])
  181. else {
  182. if (typeof cb === 'function')
  183. cb.apply(this, arguments)
  184. retry()
  185. }
  186. }
  187. }
  188. function go$readdir (args) {
  189. return fs$readdir.apply(fs, args)
  190. }
  191. if (process.version.substr(0, 4) === 'v0.8') {
  192. var legStreams = legacy(fs)
  193. ReadStream = legStreams.ReadStream
  194. WriteStream = legStreams.WriteStream
  195. }
  196. var fs$ReadStream = fs.ReadStream
  197. if (fs$ReadStream) {
  198. ReadStream.prototype = Object.create(fs$ReadStream.prototype)
  199. ReadStream.prototype.open = ReadStream$open
  200. }
  201. var fs$WriteStream = fs.WriteStream
  202. if (fs$WriteStream) {
  203. WriteStream.prototype = Object.create(fs$WriteStream.prototype)
  204. WriteStream.prototype.open = WriteStream$open
  205. }
  206. Object.defineProperty(fs, 'ReadStream', {
  207. get: function () {
  208. return ReadStream
  209. },
  210. set: function (val) {
  211. ReadStream = val
  212. },
  213. enumerable: true,
  214. configurable: true
  215. })
  216. Object.defineProperty(fs, 'WriteStream', {
  217. get: function () {
  218. return WriteStream
  219. },
  220. set: function (val) {
  221. WriteStream = val
  222. },
  223. enumerable: true,
  224. configurable: true
  225. })
  226. // legacy names
  227. var FileReadStream = ReadStream
  228. Object.defineProperty(fs, 'FileReadStream', {
  229. get: function () {
  230. return FileReadStream
  231. },
  232. set: function (val) {
  233. FileReadStream = val
  234. },
  235. enumerable: true,
  236. configurable: true
  237. })
  238. var FileWriteStream = WriteStream
  239. Object.defineProperty(fs, 'FileWriteStream', {
  240. get: function () {
  241. return FileWriteStream
  242. },
  243. set: function (val) {
  244. FileWriteStream = val
  245. },
  246. enumerable: true,
  247. configurable: true
  248. })
  249. function ReadStream (path, options) {
  250. if (this instanceof ReadStream)
  251. return fs$ReadStream.apply(this, arguments), this
  252. else
  253. return ReadStream.apply(Object.create(ReadStream.prototype), arguments)
  254. }
  255. function ReadStream$open () {
  256. var that = this
  257. open(that.path, that.flags, that.mode, function (err, fd) {
  258. if (err) {
  259. if (that.autoClose)
  260. that.destroy()
  261. that.emit('error', err)
  262. } else {
  263. that.fd = fd
  264. that.emit('open', fd)
  265. that.read()
  266. }
  267. })
  268. }
  269. function WriteStream (path, options) {
  270. if (this instanceof WriteStream)
  271. return fs$WriteStream.apply(this, arguments), this
  272. else
  273. return WriteStream.apply(Object.create(WriteStream.prototype), arguments)
  274. }
  275. function WriteStream$open () {
  276. var that = this
  277. open(that.path, that.flags, that.mode, function (err, fd) {
  278. if (err) {
  279. that.destroy()
  280. that.emit('error', err)
  281. } else {
  282. that.fd = fd
  283. that.emit('open', fd)
  284. }
  285. })
  286. }
  287. function createReadStream (path, options) {
  288. return new fs.ReadStream(path, options)
  289. }
  290. function createWriteStream (path, options) {
  291. return new fs.WriteStream(path, options)
  292. }
  293. var fs$open = fs.open
  294. fs.open = open
  295. function open (path, flags, mode, cb) {
  296. if (typeof mode === 'function')
  297. cb = mode, mode = null
  298. return go$open(path, flags, mode, cb)
  299. function go$open (path, flags, mode, cb) {
  300. return fs$open(path, flags, mode, function (err, fd) {
  301. if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
  302. enqueue([go$open, [path, flags, mode, cb]])
  303. else {
  304. if (typeof cb === 'function')
  305. cb.apply(this, arguments)
  306. retry()
  307. }
  308. })
  309. }
  310. }
  311. return fs
  312. }
  313. function enqueue (elem) {
  314. debug('ENQUEUE', elem[0].name, elem[1])
  315. fs[gracefulQueue].push(elem)
  316. }
  317. function retry () {
  318. var elem = fs[gracefulQueue].shift()
  319. if (elem) {
  320. debug('RETRY', elem[0].name, elem[1])
  321. elem[0].apply(null, elem[1])
  322. }
  323. }