extended-header-writer.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. module.exports = ExtendedHeaderWriter
  2. var inherits = require("inherits")
  3. , EntryWriter = require("./entry-writer.js")
  4. inherits(ExtendedHeaderWriter, EntryWriter)
  5. var tar = require("../tar.js")
  6. , path = require("path")
  7. , TarHeader = require("./header.js")
  8. // props is the props of the thing we need to write an
  9. // extended header for.
  10. // Don't be shy with it. Just encode everything.
  11. function ExtendedHeaderWriter (props) {
  12. // console.error(">> ehw ctor")
  13. var me = this
  14. if (!(me instanceof ExtendedHeaderWriter)) {
  15. return new ExtendedHeaderWriter(props)
  16. }
  17. me.fields = props
  18. var p =
  19. { path : ("PaxHeader" + path.join("/", props.path || ""))
  20. .replace(/\\/g, "/").substr(0, 100)
  21. , mode : props.mode || 0666
  22. , uid : props.uid || 0
  23. , gid : props.gid || 0
  24. , size : 0 // will be set later
  25. , mtime : props.mtime || Date.now() / 1000
  26. , type : "x"
  27. , linkpath : ""
  28. , ustar : "ustar\0"
  29. , ustarver : "00"
  30. , uname : props.uname || ""
  31. , gname : props.gname || ""
  32. , devmaj : props.devmaj || 0
  33. , devmin : props.devmin || 0
  34. }
  35. EntryWriter.call(me, p)
  36. // console.error(">> ehw props", me.props)
  37. me.props = p
  38. me._meta = true
  39. }
  40. ExtendedHeaderWriter.prototype.end = function () {
  41. // console.error(">> ehw end")
  42. var me = this
  43. if (me._ended) return
  44. me._ended = true
  45. me._encodeFields()
  46. if (me.props.size === 0) {
  47. // nothing to write!
  48. me._ready = true
  49. me._stream.end()
  50. return
  51. }
  52. me._stream.write(TarHeader.encode(me.props))
  53. me.body.forEach(function (l) {
  54. me._stream.write(l)
  55. })
  56. me._ready = true
  57. // console.error(">> ehw _process calling end()", me.props)
  58. this._stream.end()
  59. }
  60. ExtendedHeaderWriter.prototype._encodeFields = function () {
  61. // console.error(">> ehw _encodeFields")
  62. this.body = []
  63. if (this.fields.prefix) {
  64. this.fields.path = this.fields.prefix + "/" + this.fields.path
  65. this.fields.prefix = ""
  66. }
  67. encodeFields(this.fields, "", this.body, this.fields.noProprietary)
  68. var me = this
  69. this.body.forEach(function (l) {
  70. me.props.size += l.length
  71. })
  72. }
  73. function encodeFields (fields, prefix, body, nop) {
  74. // console.error(">> >> ehw encodeFields")
  75. // "%d %s=%s\n", <length>, <keyword>, <value>
  76. // The length is a decimal number, and includes itself and the \n
  77. // Numeric values are decimal strings.
  78. Object.keys(fields).forEach(function (k) {
  79. var val = fields[k]
  80. , numeric = tar.numeric[k]
  81. if (prefix) k = prefix + "." + k
  82. // already including NODETAR.type, don't need File=true also
  83. if (k === fields.type && val === true) return
  84. switch (k) {
  85. // don't include anything that's always handled just fine
  86. // in the normal header, or only meaningful in the context
  87. // of nodetar
  88. case "mode":
  89. case "cksum":
  90. case "ustar":
  91. case "ustarver":
  92. case "prefix":
  93. case "basename":
  94. case "dirname":
  95. case "needExtended":
  96. case "block":
  97. case "filter":
  98. return
  99. case "rdev":
  100. if (val === 0) return
  101. break
  102. case "nlink":
  103. case "dev": // Truly a hero among men, Creator of Star!
  104. case "ino": // Speak his name with reverent awe! It is:
  105. k = "SCHILY." + k
  106. break
  107. default: break
  108. }
  109. if (val && typeof val === "object" &&
  110. !Buffer.isBuffer(val)) encodeFields(val, k, body, nop)
  111. else if (val === null || val === undefined) return
  112. else body.push.apply(body, encodeField(k, val, nop))
  113. })
  114. return body
  115. }
  116. function encodeField (k, v, nop) {
  117. // lowercase keys must be valid, otherwise prefix with
  118. // "NODETAR."
  119. if (k.charAt(0) === k.charAt(0).toLowerCase()) {
  120. var m = k.split(".")[0]
  121. if (!tar.knownExtended[m]) k = "NODETAR." + k
  122. }
  123. // no proprietary
  124. if (nop && k.charAt(0) !== k.charAt(0).toLowerCase()) {
  125. return []
  126. }
  127. if (typeof val === "number") val = val.toString(10)
  128. var s = new Buffer(" " + k + "=" + v + "\n")
  129. , digits = Math.floor(Math.log(s.length) / Math.log(10)) + 1
  130. // console.error("1 s=%j digits=%j s.length=%d", s.toString(), digits, s.length)
  131. // if adding that many digits will make it go over that length,
  132. // then add one to it. For example, if the string is:
  133. // " foo=bar\n"
  134. // then that's 9 characters. With the "9", that bumps the length
  135. // up to 10. However, this is invalid:
  136. // "10 foo=bar\n"
  137. // but, since that's actually 11 characters, since 10 adds another
  138. // character to the length, and the length includes the number
  139. // itself. In that case, just bump it up again.
  140. if (s.length + digits >= Math.pow(10, digits)) digits += 1
  141. // console.error("2 s=%j digits=%j s.length=%d", s.toString(), digits, s.length)
  142. var len = digits + s.length
  143. // console.error("3 s=%j digits=%j s.length=%d len=%d", s.toString(), digits, s.length, len)
  144. var lenBuf = new Buffer("" + len)
  145. if (lenBuf.length + s.length !== len) {
  146. throw new Error("Bad length calculation\n"+
  147. "len="+len+"\n"+
  148. "lenBuf="+JSON.stringify(lenBuf.toString())+"\n"+
  149. "lenBuf.length="+lenBuf.length+"\n"+
  150. "digits="+digits+"\n"+
  151. "s="+JSON.stringify(s.toString())+"\n"+
  152. "s.length="+s.length)
  153. }
  154. return [lenBuf, s]
  155. }