1
0

filesystem.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  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: Object.create(null) }
  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: Object.create(null) }
  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 = Object.create(null)
  36. }
  37. if (node.files[name] == null) {
  38. node.files[name] = Object.create(null)
  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 = node.files || Object.create(null)
  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 symlink = fs.readlinkSync(p)
  89. const parentPath = path.dirname(p)
  90. const link = path.relative(fs.realpathSync(this.src), path.join(parentPath, symlink))
  91. if (link.substr(0, 2) === '..') {
  92. throw new Error(`${p}: file "${link}" links out of the package`)
  93. }
  94. const node = this.searchNodeFromPath(p)
  95. node.link = link
  96. return link
  97. }
  98. listFiles (options) {
  99. const files = []
  100. const fillFilesFromMetadata = function (basePath, metadata) {
  101. if (!metadata.files) {
  102. return
  103. }
  104. for (const [childPath, childMetadata] of Object.entries(metadata.files)) {
  105. const fullPath = path.join(basePath, childPath)
  106. const packState = childMetadata.unpacked ? 'unpack' : 'pack '
  107. files.push((options && options.isPack) ? `${packState} : ${fullPath}` : fullPath)
  108. fillFilesFromMetadata(fullPath, childMetadata)
  109. }
  110. }
  111. fillFilesFromMetadata('/', this.header)
  112. return files
  113. }
  114. getNode (p) {
  115. const node = this.searchNodeFromDirectory(path.dirname(p))
  116. const name = path.basename(p)
  117. if (name) {
  118. return node.files[name]
  119. } else {
  120. return node
  121. }
  122. }
  123. getFile (p, followLinks) {
  124. followLinks = typeof followLinks === 'undefined' ? true : followLinks
  125. const info = this.getNode(p)
  126. if (!info) {
  127. throw new Error(`"${p}" was not found in this archive`)
  128. }
  129. // if followLinks is false we don't resolve symlinks
  130. if (info.link && followLinks) {
  131. return this.getFile(info.link)
  132. } else {
  133. return info
  134. }
  135. }
  136. }
  137. module.exports = Filesystem