filesystem.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. 'use strict'
  2. const fs = require('./wrapped-fs')
  3. const os = require('os')
  4. const path = require('path')
  5. const { promisify } = require('util')
  6. const stream = require('stream')
  7. const getFileIntegrity = require('./integrity')
  8. const UINT32_MAX = 2 ** 32 - 1
  9. const pipeline = promisify(stream.pipeline)
  10. class Filesystem {
  11. constructor (src) {
  12. this.src = path.resolve(src)
  13. this.header = { files: {} }
  14. this.offset = BigInt(0)
  15. }
  16. searchNodeFromDirectory (p) {
  17. let json = this.header
  18. const dirs = p.split(path.sep)
  19. for (const dir of dirs) {
  20. if (dir !== '.') {
  21. if (!json.files[dir]) {
  22. json.files[dir] = { files: {} }
  23. }
  24. json = json.files[dir]
  25. }
  26. }
  27. return json
  28. }
  29. searchNodeFromPath (p) {
  30. p = path.relative(this.src, p)
  31. if (!p) { return this.header }
  32. const name = path.basename(p)
  33. const node = this.searchNodeFromDirectory(path.dirname(p))
  34. if (node.files == null) {
  35. node.files = {}
  36. }
  37. if (node.files[name] == null) {
  38. node.files[name] = {}
  39. }
  40. return node.files[name]
  41. }
  42. insertDirectory (p, shouldUnpack) {
  43. const node = this.searchNodeFromPath(p)
  44. if (shouldUnpack) {
  45. node.unpacked = shouldUnpack
  46. }
  47. node.files = {}
  48. return node.files
  49. }
  50. async insertFile (p, shouldUnpack, file, options) {
  51. const dirNode = this.searchNodeFromPath(path.dirname(p))
  52. const node = this.searchNodeFromPath(p)
  53. if (shouldUnpack || dirNode.unpacked) {
  54. node.size = file.stat.size
  55. node.unpacked = true
  56. node.integrity = await getFileIntegrity(p)
  57. return Promise.resolve()
  58. }
  59. let size
  60. const transformed = options.transform && options.transform(p)
  61. if (transformed) {
  62. const tmpdir = await fs.mkdtemp(path.join(os.tmpdir(), 'asar-'))
  63. const tmpfile = path.join(tmpdir, path.basename(p))
  64. const out = fs.createWriteStream(tmpfile)
  65. const readStream = fs.createReadStream(p)
  66. await pipeline(readStream, transformed, out)
  67. file.transformed = {
  68. path: tmpfile,
  69. stat: await fs.lstat(tmpfile)
  70. }
  71. size = file.transformed.stat.size
  72. } else {
  73. size = file.stat.size
  74. }
  75. // JavaScript cannot precisely present integers >= UINT32_MAX.
  76. if (size > UINT32_MAX) {
  77. throw new Error(`${p}: file size can not be larger than 4.2GB`)
  78. }
  79. node.size = size
  80. node.offset = this.offset.toString()
  81. node.integrity = await getFileIntegrity(p)
  82. if (process.platform !== 'win32' && (file.stat.mode & 0o100)) {
  83. node.executable = true
  84. }
  85. this.offset += BigInt(size)
  86. }
  87. insertLink (p) {
  88. const link = path.relative(fs.realpathSync(this.src), fs.realpathSync(p))
  89. if (link.substr(0, 2) === '..') {
  90. throw new Error(`${p}: file "${link}" links out of the package`)
  91. }
  92. const node = this.searchNodeFromPath(p)
  93. node.link = link
  94. return link
  95. }
  96. listFiles (options) {
  97. const files = []
  98. const fillFilesFromMetadata = function (basePath, metadata) {
  99. if (!metadata.files) {
  100. return
  101. }
  102. for (const [childPath, childMetadata] of Object.entries(metadata.files)) {
  103. const fullPath = path.join(basePath, childPath)
  104. const packState = childMetadata.unpacked ? 'unpack' : 'pack '
  105. files.push((options && options.isPack) ? `${packState} : ${fullPath}` : fullPath)
  106. fillFilesFromMetadata(fullPath, childMetadata)
  107. }
  108. }
  109. fillFilesFromMetadata('/', this.header)
  110. return files
  111. }
  112. getNode (p) {
  113. const node = this.searchNodeFromDirectory(path.dirname(p))
  114. const name = path.basename(p)
  115. if (name) {
  116. return node.files[name]
  117. } else {
  118. return node
  119. }
  120. }
  121. getFile (p, followLinks) {
  122. followLinks = typeof followLinks === 'undefined' ? true : followLinks
  123. const info = this.getNode(p)
  124. // if followLinks is false we don't resolve symlinks
  125. if (info.link && followLinks) {
  126. return this.getFile(info.link)
  127. } else {
  128. return info
  129. }
  130. }
  131. }
  132. module.exports = Filesystem