You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

graceful-fs.js 8.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  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$readdir = fs.readdir
  148. fs.readdir = readdir
  149. function readdir (path, options, cb) {
  150. var args = [path]
  151. if (typeof options !== 'function') {
  152. args.push(options)
  153. } else {
  154. cb = options
  155. }
  156. args.push(go$readdir$cb)
  157. return go$readdir(args)
  158. function go$readdir$cb (err, files) {
  159. if (files && files.sort)
  160. files.sort()
  161. if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
  162. enqueue([go$readdir, [args]])
  163. else {
  164. if (typeof cb === 'function')
  165. cb.apply(this, arguments)
  166. retry()
  167. }
  168. }
  169. }
  170. function go$readdir (args) {
  171. return fs$readdir.apply(fs, args)
  172. }
  173. if (process.version.substr(0, 4) === 'v0.8') {
  174. var legStreams = legacy(fs)
  175. ReadStream = legStreams.ReadStream
  176. WriteStream = legStreams.WriteStream
  177. }
  178. var fs$ReadStream = fs.ReadStream
  179. if (fs$ReadStream) {
  180. ReadStream.prototype = Object.create(fs$ReadStream.prototype)
  181. ReadStream.prototype.open = ReadStream$open
  182. }
  183. var fs$WriteStream = fs.WriteStream
  184. if (fs$WriteStream) {
  185. WriteStream.prototype = Object.create(fs$WriteStream.prototype)
  186. WriteStream.prototype.open = WriteStream$open
  187. }
  188. Object.defineProperty(fs, 'ReadStream', {
  189. get: function () {
  190. return ReadStream
  191. },
  192. set: function (val) {
  193. ReadStream = val
  194. },
  195. enumerable: true,
  196. configurable: true
  197. })
  198. Object.defineProperty(fs, 'WriteStream', {
  199. get: function () {
  200. return WriteStream
  201. },
  202. set: function (val) {
  203. WriteStream = val
  204. },
  205. enumerable: true,
  206. configurable: true
  207. })
  208. // legacy names
  209. var FileReadStream = ReadStream
  210. Object.defineProperty(fs, 'FileReadStream', {
  211. get: function () {
  212. return FileReadStream
  213. },
  214. set: function (val) {
  215. FileReadStream = val
  216. },
  217. enumerable: true,
  218. configurable: true
  219. })
  220. var FileWriteStream = WriteStream
  221. Object.defineProperty(fs, 'FileWriteStream', {
  222. get: function () {
  223. return FileWriteStream
  224. },
  225. set: function (val) {
  226. FileWriteStream = val
  227. },
  228. enumerable: true,
  229. configurable: true
  230. })
  231. function ReadStream (path, options) {
  232. if (this instanceof ReadStream)
  233. return fs$ReadStream.apply(this, arguments), this
  234. else
  235. return ReadStream.apply(Object.create(ReadStream.prototype), arguments)
  236. }
  237. function ReadStream$open () {
  238. var that = this
  239. open(that.path, that.flags, that.mode, function (err, fd) {
  240. if (err) {
  241. if (that.autoClose)
  242. that.destroy()
  243. that.emit('error', err)
  244. } else {
  245. that.fd = fd
  246. that.emit('open', fd)
  247. that.read()
  248. }
  249. })
  250. }
  251. function WriteStream (path, options) {
  252. if (this instanceof WriteStream)
  253. return fs$WriteStream.apply(this, arguments), this
  254. else
  255. return WriteStream.apply(Object.create(WriteStream.prototype), arguments)
  256. }
  257. function WriteStream$open () {
  258. var that = this
  259. open(that.path, that.flags, that.mode, function (err, fd) {
  260. if (err) {
  261. that.destroy()
  262. that.emit('error', err)
  263. } else {
  264. that.fd = fd
  265. that.emit('open', fd)
  266. }
  267. })
  268. }
  269. function createReadStream (path, options) {
  270. return new fs.ReadStream(path, options)
  271. }
  272. function createWriteStream (path, options) {
  273. return new fs.WriteStream(path, options)
  274. }
  275. var fs$open = fs.open
  276. fs.open = open
  277. function open (path, flags, mode, cb) {
  278. if (typeof mode === 'function')
  279. cb = mode, mode = null
  280. return go$open(path, flags, mode, cb)
  281. function go$open (path, flags, mode, cb) {
  282. return fs$open(path, flags, mode, function (err, fd) {
  283. if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
  284. enqueue([go$open, [path, flags, mode, cb]])
  285. else {
  286. if (typeof cb === 'function')
  287. cb.apply(this, arguments)
  288. retry()
  289. }
  290. })
  291. }
  292. }
  293. return fs
  294. }
  295. function enqueue (elem) {
  296. debug('ENQUEUE', elem[0].name, elem[1])
  297. fs[gracefulQueue].push(elem)
  298. }
  299. function retry () {
  300. var elem = fs[gracefulQueue].shift()
  301. if (elem) {
  302. debug('RETRY', elem[0].name, elem[1])
  303. elem[0].apply(null, elem[1])
  304. }
  305. }