graceful-fs.js 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. var fs = require('fs')
  2. var polyfills = require('./polyfills.js')
  3. var legacy = require('./legacy-streams.js')
  4. var queue = []
  5. var util = require('util')
  6. function noop () {}
  7. var debug = noop
  8. if (util.debuglog)
  9. debug = util.debuglog('gfs4')
  10. else if (/\bgfs4\b/i.test(process.env.NODE_DEBUG || ''))
  11. debug = function() {
  12. var m = util.format.apply(util, arguments)
  13. m = 'GFS4: ' + m.split(/\n/).join('\nGFS4: ')
  14. console.error(m)
  15. }
  16. if (/\bgfs4\b/i.test(process.env.NODE_DEBUG || '')) {
  17. process.on('exit', function() {
  18. debug(queue)
  19. require('assert').equal(queue.length, 0)
  20. })
  21. }
  22. module.exports = patch(require('./fs.js'))
  23. if (process.env.TEST_GRACEFUL_FS_GLOBAL_PATCH) {
  24. module.exports = patch(fs)
  25. }
  26. // Always patch fs.close/closeSync, because we want to
  27. // retry() whenever a close happens *anywhere* in the program.
  28. // This is essential when multiple graceful-fs instances are
  29. // in play at the same time.
  30. fs.close = (function (fs$close) { return function (fd, cb) {
  31. return fs$close.call(fs, fd, function (err) {
  32. if (!err)
  33. retry()
  34. if (typeof cb === 'function')
  35. cb.apply(this, arguments)
  36. })
  37. }})(fs.close)
  38. fs.closeSync = (function (fs$closeSync) { return function (fd) {
  39. // Note that graceful-fs also retries when fs.closeSync() fails.
  40. // Looks like a bug to me, although it's probably a harmless one.
  41. var rval = fs$closeSync.apply(fs, arguments)
  42. retry()
  43. return rval
  44. }})(fs.closeSync)
  45. function patch (fs) {
  46. // Everything that references the open() function needs to be in here
  47. polyfills(fs)
  48. fs.gracefulify = patch
  49. fs.FileReadStream = ReadStream; // Legacy name.
  50. fs.FileWriteStream = WriteStream; // Legacy name.
  51. fs.createReadStream = createReadStream
  52. fs.createWriteStream = createWriteStream
  53. var fs$readFile = fs.readFile
  54. fs.readFile = readFile
  55. function readFile (path, options, cb) {
  56. if (typeof options === 'function')
  57. cb = options, options = null
  58. return go$readFile(path, options, cb)
  59. function go$readFile (path, options, cb) {
  60. return fs$readFile(path, options, function (err) {
  61. if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
  62. enqueue([go$readFile, [path, options, cb]])
  63. else {
  64. if (typeof cb === 'function')
  65. cb.apply(this, arguments)
  66. retry()
  67. }
  68. })
  69. }
  70. }
  71. var fs$writeFile = fs.writeFile
  72. fs.writeFile = writeFile
  73. function writeFile (path, data, options, cb) {
  74. if (typeof options === 'function')
  75. cb = options, options = null
  76. return go$writeFile(path, data, options, cb)
  77. function go$writeFile (path, data, options, cb) {
  78. return fs$writeFile(path, data, options, function (err) {
  79. if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
  80. enqueue([go$writeFile, [path, data, options, cb]])
  81. else {
  82. if (typeof cb === 'function')
  83. cb.apply(this, arguments)
  84. retry()
  85. }
  86. })
  87. }
  88. }
  89. var fs$appendFile = fs.appendFile
  90. if (fs$appendFile)
  91. fs.appendFile = appendFile
  92. function appendFile (path, data, options, cb) {
  93. if (typeof options === 'function')
  94. cb = options, options = null
  95. return go$appendFile(path, data, options, cb)
  96. function go$appendFile (path, data, options, cb) {
  97. return fs$appendFile(path, data, options, function (err) {
  98. if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
  99. enqueue([go$appendFile, [path, data, options, cb]])
  100. else {
  101. if (typeof cb === 'function')
  102. cb.apply(this, arguments)
  103. retry()
  104. }
  105. })
  106. }
  107. }
  108. var fs$readdir = fs.readdir
  109. fs.readdir = readdir
  110. function readdir (path, cb) {
  111. return go$readdir(path, cb)
  112. function go$readdir () {
  113. return fs$readdir(path, function (err, files) {
  114. if (files && files.sort)
  115. files.sort(); // Backwards compatibility with graceful-fs.
  116. if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
  117. enqueue([go$readdir, [path, cb]])
  118. else {
  119. if (typeof cb === 'function')
  120. cb.apply(this, arguments)
  121. retry()
  122. }
  123. })
  124. }
  125. }
  126. if (process.version.substr(0, 4) === 'v0.8') {
  127. var legStreams = legacy(fs)
  128. ReadStream = legStreams.ReadStream
  129. WriteStream = legStreams.WriteStream
  130. }
  131. var fs$ReadStream = fs.ReadStream
  132. ReadStream.prototype = Object.create(fs$ReadStream.prototype)
  133. ReadStream.prototype.open = ReadStream$open
  134. var fs$WriteStream = fs.WriteStream
  135. WriteStream.prototype = Object.create(fs$WriteStream.prototype)
  136. WriteStream.prototype.open = WriteStream$open
  137. fs.ReadStream = ReadStream
  138. fs.WriteStream = WriteStream
  139. function ReadStream (path, options) {
  140. if (this instanceof ReadStream)
  141. return fs$ReadStream.apply(this, arguments), this
  142. else
  143. return ReadStream.apply(Object.create(ReadStream.prototype), arguments)
  144. }
  145. function ReadStream$open () {
  146. var that = this
  147. open(that.path, that.flags, that.mode, function (err, fd) {
  148. if (err) {
  149. if (that.autoClose)
  150. that.destroy()
  151. that.emit('error', err)
  152. } else {
  153. that.fd = fd
  154. that.emit('open', fd)
  155. that.read()
  156. }
  157. })
  158. }
  159. function WriteStream (path, options) {
  160. if (this instanceof WriteStream)
  161. return fs$WriteStream.apply(this, arguments), this
  162. else
  163. return WriteStream.apply(Object.create(WriteStream.prototype), arguments)
  164. }
  165. function WriteStream$open () {
  166. var that = this
  167. open(that.path, that.flags, that.mode, function (err, fd) {
  168. if (err) {
  169. that.destroy()
  170. that.emit('error', err)
  171. } else {
  172. that.fd = fd
  173. that.emit('open', fd)
  174. }
  175. })
  176. }
  177. function createReadStream (path, options) {
  178. return new ReadStream(path, options)
  179. }
  180. function createWriteStream (path, options) {
  181. return new WriteStream(path, options)
  182. }
  183. var fs$open = fs.open
  184. fs.open = open
  185. function open (path, flags, mode, cb) {
  186. if (typeof mode === 'function')
  187. cb = mode, mode = null
  188. return go$open(path, flags, mode, cb)
  189. function go$open (path, flags, mode, cb) {
  190. return fs$open(path, flags, mode, function (err, fd) {
  191. if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
  192. enqueue([go$open, [path, flags, mode, cb]])
  193. else {
  194. if (typeof cb === 'function')
  195. cb.apply(this, arguments)
  196. retry()
  197. }
  198. })
  199. }
  200. }
  201. return fs
  202. }
  203. function enqueue (elem) {
  204. debug('ENQUEUE', elem[0].name, elem[1])
  205. queue.push(elem)
  206. }
  207. function retry () {
  208. var elem = queue.shift()
  209. if (elem) {
  210. debug('RETRY', elem[0].name, elem[1])
  211. elem[0].apply(null, elem[1])
  212. }
  213. }