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.

filesystem.js 3.9KB

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