mirror of
https://github.com/linuxkit/linuxkit.git
synced 2025-07-19 17:26:28 +00:00
Merge pull request #1276 from rneugeba/infra-iso
infrakit: Pass config information to VM via an ISO
This commit is contained in:
commit
2eb0fe7d04
@ -14,6 +14,7 @@ import (
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
ps "github.com/mitchellh/go-ps"
|
||||
"github.com/rneugeba/iso9660wrap"
|
||||
|
||||
"github.com/docker/infrakit/pkg/spi/instance"
|
||||
"github.com/docker/infrakit/pkg/template"
|
||||
@ -91,7 +92,7 @@ func (v hyperkitPlugin) Provision(spec instance.Spec) (*instance.ID, error) {
|
||||
"Properties": properties,
|
||||
}
|
||||
|
||||
err = v.execHyperKit(params)
|
||||
err = v.execHyperKit(spec, params)
|
||||
if err != nil {
|
||||
v.Destroy(id)
|
||||
return nil, err
|
||||
@ -238,16 +239,18 @@ func (v hyperkitPlugin) DescribeInstances(tags map[string]string) ([]instance.De
|
||||
|
||||
const hyperkitArgs = "-A -u -F {{.VMLocation}}/hyperkit.pid " +
|
||||
"-c {{.Properties.CPUs}} -m {{.Properties.Memory}}M " +
|
||||
"-s 0:0,hostbridge -s 31,lpc -s 5,virtio-rnd " +
|
||||
"-s 4,virtio-blk,{{.VMLocation}}/disk.img " +
|
||||
"-s 2:0,virtio-vpnkit,path={{.VPNKitSock}} " +
|
||||
"-s 0:0,hostbridge " +
|
||||
"-s 1:0,virtio-vpnkit,path={{.VPNKitSock}} " +
|
||||
"-s 2,virtio-blk,{{.VMLocation}}/disk.img " +
|
||||
"-s 10,virtio-rnd " +
|
||||
"-s 31,lpc " +
|
||||
"-l com1,autopty={{.VMLocation}}/tty,log={{.VMLocation}}/console-ring"
|
||||
const hyperkitKernArgs = "kexec," +
|
||||
"{{.Properties.Moby}}-bzImage," +
|
||||
"{{.Properties.Moby}}-initrd.img," +
|
||||
"earlyprintk=serial console=ttyS0 panic=1 vsyscall=emulate page_poison=1 ntp=gateway"
|
||||
|
||||
func (v hyperkitPlugin) execHyperKit(params map[string]interface{}) error {
|
||||
func (v hyperkitPlugin) execHyperKit(spec instance.Spec, params map[string]interface{}) error {
|
||||
|
||||
instanceDir := params["VMLocation"].(string)
|
||||
|
||||
@ -259,7 +262,6 @@ func (v hyperkitPlugin) execHyperKit(params map[string]interface{}) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Build arguments
|
||||
c := []string{v.HyperKit}
|
||||
c = append(c, strings.Split(args, " ")...)
|
||||
@ -280,6 +282,14 @@ func (v hyperkitPlugin) execHyperKit(params map[string]interface{}) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(spec.Init) != 0 {
|
||||
err = createConfigISO(instanceDir, spec.Init)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c = append(c, "-s", "4,ahci-cd,"+path.Join(instanceDir, "config.iso"))
|
||||
}
|
||||
|
||||
cmd := exec.Command(c[0], c[1:]...)
|
||||
cmd.Env = os.Environ()
|
||||
|
||||
@ -338,6 +348,29 @@ func createDisk(instanceDir string, diskSz int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func createConfigISO(instanceDir, init string) error {
|
||||
inName := path.Join(instanceDir, "config")
|
||||
|
||||
if err := ioutil.WriteFile(inName, []byte(init), 0666); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
outfh, err := os.OpenFile(inName+".iso", os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0666)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
infh, err := os.Open(inName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = iso9660wrap.WriteFile(outfh, infh)
|
||||
if err != nil {
|
||||
log.Fatalf("writing file failed with %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func stream(r io.ReadCloser, dest chan<- string) {
|
||||
go func() {
|
||||
defer r.Close()
|
||||
|
@ -15,7 +15,16 @@
|
||||
},
|
||||
"Flavor": {
|
||||
"Plugin": "flavor-vanilla",
|
||||
"Properties": { }
|
||||
"Properties": {
|
||||
"Init": [
|
||||
"test1",
|
||||
"test2"
|
||||
],
|
||||
"Tags": {
|
||||
"tier": "sample",
|
||||
"project": "infrakit"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
github.com/Masterminds/sprig 2009c2546db608c737012557c9d3e836468f0423
|
||||
github.com/Masterminds/sprig 427e90187e0902bc04b64167610f2da7ba26e0e1
|
||||
github.com/Sirupsen/logrus 1deb2db2a6fff8a35532079061b903c3a25eed52
|
||||
github.com/aokoli/goutils 9c37978a95bd5c709a15883b6242714ea6709e64
|
||||
github.com/armon/go-radix 4239b77079c7b5d1243b7b4736304ce8ddb6f0f2
|
||||
@ -9,6 +9,7 @@ github.com/gorilla/rpc 22c016f3df3febe0c1f6727598b6389507e03a18
|
||||
github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
|
||||
github.com/jmespath/go-jmespath bd40a432e4c76585ef6b72d3fd96fb9b6dc7b68d
|
||||
github.com/mitchellh/go-ps 4fdf99ab29366514c69ccccddab5dc58b8d84062
|
||||
github.com/rneugeba/iso9660wrap 9c7eaf5ac74b2416be8b7b8d1f35b9af44a6e4fa
|
||||
github.com/satori/go.uuid b061729afc07e77a8aa4fad0a2fd840958f1942a
|
||||
github.com/spf13/cobra 16c014f1a19d865b765b420e74508f80eb831ada
|
||||
github.com/spf13/pflag 9ff6c6923cfffbcd502984b8e0c80539a94968b7
|
||||
|
21
tools/infrakit.hyperkit/vendor/github.com/rneugeba/iso9660wrap/LICENSE
generated
vendored
Normal file
21
tools/infrakit.hyperkit/vendor/github.com/rneugeba/iso9660wrap/LICENSE
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Marko Tiikkaja
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
4
tools/infrakit.hyperkit/vendor/github.com/rneugeba/iso9660wrap/README.md
generated
vendored
Normal file
4
tools/infrakit.hyperkit/vendor/github.com/rneugeba/iso9660wrap/README.md
generated
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
iso9660wrap
|
||||
===========
|
||||
This turns the [iso9660wrap](https://github.com/johto/iso9660wrap) utility into a package. It provides a simple means to create an ISO9660 file containing a single file.
|
||||
|
66
tools/infrakit.hyperkit/vendor/github.com/rneugeba/iso9660wrap/directories.go
generated
vendored
Normal file
66
tools/infrakit.hyperkit/vendor/github.com/rneugeba/iso9660wrap/directories.go
generated
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
package iso9660wrap
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
func WriteDirectoryRecord(w *SectorWriter, identifier string, firstSectorNum uint32) uint32 {
|
||||
if len(identifier) > 30 {
|
||||
Panicf("directory identifier length %d is out of bounds", len(identifier))
|
||||
}
|
||||
recordLength := 33 + len(identifier)
|
||||
|
||||
w.WriteByte(byte(recordLength))
|
||||
w.WriteByte(0) // number of sectors in extended attribute record
|
||||
w.WriteBothEndianDWord(firstSectorNum)
|
||||
w.WriteBothEndianDWord(SectorSize) // directory length
|
||||
writeDirectoryRecordtimestamp(w, time.Now())
|
||||
w.WriteByte(byte(3)) // bitfield; directory
|
||||
w.WriteByte(byte(0)) // file unit size for an interleaved file
|
||||
w.WriteByte(byte(0)) // interleave gap size for an interleaved file
|
||||
w.WriteBothEndianWord(1) // volume sequence number
|
||||
w.WriteByte(byte(len(identifier)))
|
||||
w.WriteString(identifier)
|
||||
// optional padding to even length
|
||||
if recordLength%2 == 1 {
|
||||
recordLength++
|
||||
w.WriteByte(0)
|
||||
}
|
||||
return uint32(recordLength)
|
||||
}
|
||||
|
||||
func WriteFileRecordHeader(w *SectorWriter, identifier string, firstSectorNum uint32, fileSize uint32) uint32 {
|
||||
if len(identifier) > 30 {
|
||||
Panicf("directory identifier length %d is out of bounds", len(identifier))
|
||||
}
|
||||
recordLength := 33 + len(identifier)
|
||||
|
||||
w.WriteByte(byte(recordLength))
|
||||
w.WriteByte(0) // number of sectors in extended attribute record
|
||||
w.WriteBothEndianDWord(firstSectorNum) // first sector
|
||||
w.WriteBothEndianDWord(fileSize)
|
||||
writeDirectoryRecordtimestamp(w, time.Now())
|
||||
w.WriteByte(byte(0)) // bitfield; normal file
|
||||
w.WriteByte(byte(0)) // file unit size for an interleaved file
|
||||
w.WriteByte(byte(0)) // interleave gap size for an interleaved file
|
||||
w.WriteBothEndianWord(1) // volume sequence number
|
||||
w.WriteByte(byte(len(identifier)))
|
||||
w.WriteString(identifier)
|
||||
// optional padding to even length
|
||||
if recordLength%2 == 1 {
|
||||
recordLength++
|
||||
w.WriteByte(0)
|
||||
}
|
||||
return uint32(recordLength)
|
||||
}
|
||||
|
||||
func writeDirectoryRecordtimestamp(w *SectorWriter, t time.Time) {
|
||||
t = t.UTC()
|
||||
w.WriteByte(byte(t.Year() - 1900))
|
||||
w.WriteByte(byte(t.Month()))
|
||||
w.WriteByte(byte(t.Day()))
|
||||
w.WriteByte(byte(t.Hour()))
|
||||
w.WriteByte(byte(t.Minute()))
|
||||
w.WriteByte(byte(t.Second()))
|
||||
w.WriteByte(0) // UTC offset
|
||||
}
|
148
tools/infrakit.hyperkit/vendor/github.com/rneugeba/iso9660wrap/iso9660_writer.go
generated
vendored
Normal file
148
tools/infrakit.hyperkit/vendor/github.com/rneugeba/iso9660wrap/iso9660_writer.go
generated
vendored
Normal file
@ -0,0 +1,148 @@
|
||||
package iso9660wrap
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"math"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const SectorSize uint32 = 2048
|
||||
|
||||
type SectorWriter struct {
|
||||
w io.Writer
|
||||
p uint32
|
||||
}
|
||||
|
||||
func (w *SectorWriter) Write(p []byte) uint32 {
|
||||
if len(p) >= math.MaxUint32 {
|
||||
Panicf("attempted write of length %d is out of sector bounds", len(p))
|
||||
}
|
||||
l := uint32(len(p))
|
||||
if l > w.RemainingSpace() {
|
||||
Panicf("attempted write of length %d at offset %d is out of sector bounds", w.p, len(p))
|
||||
}
|
||||
w.p += l
|
||||
_, err := w.w.Write(p)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
func (w *SectorWriter) WriteUnspecifiedDateTime() uint32 {
|
||||
b := make([]byte, 17)
|
||||
for i := 0; i < 16; i++ {
|
||||
b[i] = '0'
|
||||
}
|
||||
b[16] = 0
|
||||
return w.Write(b)
|
||||
}
|
||||
|
||||
func (w *SectorWriter) WriteDateTime(t time.Time) uint32 {
|
||||
f := t.UTC().Format("20060102150405")
|
||||
f += "00" // 1/100
|
||||
f += "\x00" // UTC offset
|
||||
if len(f) != 17 {
|
||||
Panicf("date and time field %q is of unexpected length %d", f, len(f))
|
||||
}
|
||||
return w.WriteString(f)
|
||||
}
|
||||
|
||||
func (w *SectorWriter) WriteString(str string) uint32 {
|
||||
return w.Write([]byte(str))
|
||||
}
|
||||
|
||||
func (w *SectorWriter) WritePaddedString(str string, length uint32) uint32 {
|
||||
l := w.WriteString(str)
|
||||
if l > 32 {
|
||||
Panicf("padded string %q exceeds length %d", str, length)
|
||||
} else if l < 32 {
|
||||
w.WriteString(strings.Repeat(" ", int(32-l)))
|
||||
}
|
||||
return 32
|
||||
}
|
||||
|
||||
func (w *SectorWriter) WriteByte(b byte) uint32 {
|
||||
return w.Write([]byte{b})
|
||||
}
|
||||
|
||||
func (w *SectorWriter) WriteWord(bo binary.ByteOrder, word uint16) uint32 {
|
||||
b := make([]byte, 2)
|
||||
bo.PutUint16(b, word)
|
||||
return w.Write(b)
|
||||
}
|
||||
|
||||
func (w *SectorWriter) WriteBothEndianWord(word uint16) uint32 {
|
||||
w.WriteWord(binary.LittleEndian, word)
|
||||
w.WriteWord(binary.BigEndian, word)
|
||||
return 4
|
||||
}
|
||||
|
||||
func (w *SectorWriter) WriteDWord(bo binary.ByteOrder, dword uint32) uint32 {
|
||||
b := make([]byte, 4)
|
||||
bo.PutUint32(b, dword)
|
||||
return w.Write(b)
|
||||
}
|
||||
|
||||
func (w *SectorWriter) WriteLittleEndianDWord(dword uint32) uint32 {
|
||||
return w.WriteDWord(binary.LittleEndian, dword)
|
||||
}
|
||||
|
||||
func (w *SectorWriter) WriteBigEndianDWord(dword uint32) uint32 {
|
||||
return w.WriteDWord(binary.BigEndian, dword)
|
||||
}
|
||||
|
||||
func (w *SectorWriter) WriteBothEndianDWord(dword uint32) uint32 {
|
||||
w.WriteLittleEndianDWord(dword)
|
||||
w.WriteBigEndianDWord(dword)
|
||||
return 8
|
||||
}
|
||||
|
||||
func (w *SectorWriter) WriteZeros(c int) uint32 {
|
||||
return w.Write(make([]byte, c))
|
||||
}
|
||||
|
||||
func (w *SectorWriter) PadWithZeros() uint32 {
|
||||
return w.Write(make([]byte, w.RemainingSpace()))
|
||||
}
|
||||
|
||||
func (w *SectorWriter) RemainingSpace() uint32 {
|
||||
return SectorSize - w.p
|
||||
}
|
||||
|
||||
func (w *SectorWriter) Reset() {
|
||||
w.p = 0
|
||||
}
|
||||
|
||||
type ISO9660Writer struct {
|
||||
sw *SectorWriter
|
||||
sectorNum uint32
|
||||
}
|
||||
|
||||
func (w *ISO9660Writer) CurrentSector() uint32 {
|
||||
return uint32(w.sectorNum)
|
||||
}
|
||||
|
||||
func (w *ISO9660Writer) NextSector() *SectorWriter {
|
||||
if w.sw.RemainingSpace() == SectorSize {
|
||||
Panicf("internal error: tried to leave sector %d empty", w.sectorNum)
|
||||
}
|
||||
w.sw.PadWithZeros()
|
||||
w.sw.Reset()
|
||||
w.sectorNum++
|
||||
return w.sw
|
||||
}
|
||||
|
||||
func (w *ISO9660Writer) Finish() {
|
||||
if w.sw.RemainingSpace() != SectorSize {
|
||||
w.sw.PadWithZeros()
|
||||
}
|
||||
w.sw = nil
|
||||
}
|
||||
|
||||
func NewISO9660Writer(w io.Writer) *ISO9660Writer {
|
||||
// start at the end of the last reserved sector
|
||||
return &ISO9660Writer{&SectorWriter{w, SectorSize}, 16 - 1}
|
||||
}
|
367
tools/infrakit.hyperkit/vendor/github.com/rneugeba/iso9660wrap/iso9660wrap.go
generated
vendored
Normal file
367
tools/infrakit.hyperkit/vendor/github.com/rneugeba/iso9660wrap/iso9660wrap.go
generated
vendored
Normal file
@ -0,0 +1,367 @@
|
||||
package iso9660wrap
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const reservedAreaData string = `
|
||||
Once upon a midnight dreary, while I pondered, weak and weary,
|
||||
Over many a quaint and curious volume of forgotten lore—
|
||||
While I nodded, nearly napping, suddenly there came a tapping,
|
||||
As of some one gently rapping, rapping at my chamber door.
|
||||
“’Tis some visitor,” I muttered, “tapping at my chamber door—
|
||||
Only this and nothing more.”
|
||||
|
||||
Ah, distinctly I remember it was in the bleak December;
|
||||
And each separate dying ember wrought its ghost upon the floor.
|
||||
Eagerly I wished the morrow;—vainly I had sought to borrow
|
||||
From my books surcease of sorrow—sorrow for the lost Lenore—
|
||||
For the rare and radiant maiden whom the angels name Lenore—
|
||||
Nameless here for evermore.
|
||||
|
||||
And the silken, sad, uncertain rustling of each purple curtain
|
||||
Thrilled me—filled me with fantastic terrors never felt before;
|
||||
So that now, to still the beating of my heart, I stood repeating
|
||||
“’Tis some visitor entreating entrance at my chamber door—
|
||||
Some late visitor entreating entrance at my chamber door;—
|
||||
This it is and nothing more.”
|
||||
|
||||
Presently my soul grew stronger; hesitating then no longer,
|
||||
“Sir,” said I, “or Madam, truly your forgiveness I implore;
|
||||
But the fact is I was napping, and so gently you came rapping,
|
||||
And so faintly you came tapping, tapping at my chamber door,
|
||||
That I scarce was sure I heard you”—here I opened wide the door;—
|
||||
Darkness there and nothing more.
|
||||
|
||||
Deep into that darkness peering, long I stood there wondering, fearing,
|
||||
Doubting, dreaming dreams no mortal ever dared to dream before;
|
||||
But the silence was unbroken, and the stillness gave no token,
|
||||
And the only word there spoken was the whispered word, “Lenore?”
|
||||
This I whispered, and an echo murmured back the word, “Lenore!”—
|
||||
Merely this and nothing more.
|
||||
|
||||
Back into the chamber turning, all my soul within me burning,
|
||||
Soon again I heard a tapping somewhat louder than before.
|
||||
“Surely,” said I, “surely that is something at my window lattice;
|
||||
Let me see, then, what thereat is, and this mystery explore—
|
||||
Let my heart be still a moment and this mystery explore;—
|
||||
’Tis the wind and nothing more!”
|
||||
|
||||
Open here I flung the shutter, when, with many a flirt and flutter,
|
||||
In there stepped a stately Raven of the saintly days of yore;
|
||||
Not the least obeisance made he; not a minute stopped or stayed he;
|
||||
But, with mien of lord or lady, perched above my chamber door—
|
||||
Perched upon a bust of Pallas just above my chamber door—
|
||||
Perched, and sat, and nothing more.
|
||||
|
||||
Then this ebony bird beguiling my sad fancy into smiling,
|
||||
By the grave and stern decorum of the countenance it wore,
|
||||
“Though thy crest be shorn and shaven, thou,” I said, “art sure no craven,
|
||||
Ghastly grim and ancient Raven wandering from the Nightly shore—
|
||||
Tell me what thy lordly name is on the Night’s Plutonian shore!”
|
||||
Quoth the Raven “Nevermore.”
|
||||
|
||||
Much I marvelled this ungainly fowl to hear discourse so plainly,
|
||||
Though its answer little meaning—little relevancy bore;
|
||||
For we cannot help agreeing that no living human being
|
||||
Ever yet was blessed with seeing bird above his chamber door—
|
||||
Bird or beast upon the sculptured bust above his chamber door,
|
||||
With such name as “Nevermore.”
|
||||
|
||||
But the Raven, sitting lonely on the placid bust, spoke only
|
||||
That one word, as if his soul in that one word he did outpour.
|
||||
Nothing farther then he uttered—not a feather then he fluttered—
|
||||
Till I scarcely more than muttered “Other friends have flown before—
|
||||
On the morrow he will leave me, as my Hopes have flown before.”
|
||||
Then the bird said “Nevermore.”
|
||||
|
||||
Startled at the stillness broken by reply so aptly spoken,
|
||||
“Doubtless,” said I, “what it utters is its only stock and store
|
||||
Caught from some unhappy master whom unmerciful Disaster
|
||||
Followed fast and followed faster till his songs one burden bore—
|
||||
Till the dirges of his Hope that melancholy burden bore
|
||||
Of ‘Never—nevermore’.”
|
||||
|
||||
But the Raven still beguiling all my fancy into smiling,
|
||||
Straight I wheeled a cushioned seat in front of bird, and bust and door;
|
||||
Then, upon the velvet sinking, I betook myself to linking
|
||||
Fancy unto fancy, thinking what this ominous bird of yore—
|
||||
What this grim, ungainly, ghastly, gaunt, and ominous bird of yore
|
||||
Meant in croaking “Nevermore.”
|
||||
|
||||
This I sat engaged in guessing, but no syllable expressing
|
||||
To the fowl whose fiery eyes now burned into my bosom’s core;
|
||||
This and more I sat divining, with my head at ease reclining
|
||||
On the cushion’s velvet lining that the lamp-light gloated o’er,
|
||||
But whose velvet-violet lining with the lamp-light gloating o’er,
|
||||
She shall press, ah, nevermore!
|
||||
|
||||
Then, methought, the air grew denser, perfumed from an unseen censer
|
||||
Swung by Seraphim whose foot-falls tinkled on the tufted floor.
|
||||
“Wretch,” I cried, “thy God hath lent thee—by these angels he hath sent thee
|
||||
Respite—respite and nepenthe from thy memories of Lenore;
|
||||
Quaff, oh quaff this kind nepenthe and forget this lost Lenore!”
|
||||
Quoth the Raven “Nevermore.”
|
||||
|
||||
“Prophet!” said I, “thing of evil!—prophet still, if bird or devil!—
|
||||
Whether Tempter sent, or whether tempest tossed thee here ashore,
|
||||
Desolate yet all undaunted, on this desert land enchanted—
|
||||
On this home by Horror haunted—tell me truly, I implore—
|
||||
Is there—is there balm in Gilead?—tell me—tell me, I implore!”
|
||||
Quoth the Raven “Nevermore.”
|
||||
|
||||
“Prophet!” said I, “thing of evil!—prophet still, if bird or devil!
|
||||
By that Heaven that bends above us—by that God we both adore—
|
||||
Tell this soul with sorrow laden if, within the distant Aidenn,
|
||||
It shall clasp a sainted maiden whom the angels name Lenore—
|
||||
Clasp a rare and radiant maiden whom the angels name Lenore.”
|
||||
Quoth the Raven “Nevermore.”
|
||||
|
||||
“Be that word our sign of parting, bird or fiend!” I shrieked, upstarting—
|
||||
“Get thee back into the tempest and the Night’s Plutonian shore!
|
||||
Leave no black plume as a token of that lie thy soul hath spoken!
|
||||
Leave my loneliness unbroken!—quit the bust above my door!
|
||||
Take thy beak from out my heart, and take thy form from off my door!”
|
||||
Quoth the Raven “Nevermore.”
|
||||
|
||||
And the Raven, never flitting, still is sitting, still is sitting
|
||||
On the pallid bust of Pallas just above my chamber door;
|
||||
And his eyes have all the seeming of a demon’s that is dreaming,
|
||||
And the lamp-light o’er him streaming throws his shadow on the floor;
|
||||
And my soul from out that shadow that lies floating on the floor
|
||||
Shall be lifted—nevermore!
|
||||
`
|
||||
|
||||
func Panicf(format string, v ...interface{}) {
|
||||
panic(fmt.Errorf(format, v...))
|
||||
}
|
||||
|
||||
const volumeDescriptorSetMagic = "\x43\x44\x30\x30\x31\x01"
|
||||
|
||||
const primaryVolumeSectorNum uint32 = 16
|
||||
const numVolumeSectors uint32 = 2 // primary + terminator
|
||||
const littleEndianPathTableSectorNum uint32 = primaryVolumeSectorNum + numVolumeSectors
|
||||
const bigEndianPathTableSectorNum uint32 = littleEndianPathTableSectorNum + 1
|
||||
const numPathTableSectors = 2 // no secondaries
|
||||
const rootDirectorySectorNum uint32 = primaryVolumeSectorNum + numVolumeSectors + numPathTableSectors
|
||||
|
||||
// WriteFile writes the contents of infh to an iso at outfh with the name provided
|
||||
func WriteFile(outfh, infh *os.File) error {
|
||||
inputFileSize, inputFilename, err := getInputFileSizeAndName(infh)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if inputFileSize == 0 {
|
||||
return fmt.Errorf("input file must be at least 1 byte in size")
|
||||
}
|
||||
inputFilename = strings.ToUpper(inputFilename)
|
||||
if !filenameSatisfiesISOConstraints(inputFilename) {
|
||||
return fmt.Errorf("Input file name %s does not satisfy the ISO9660 character set constraints", inputFilename)
|
||||
}
|
||||
|
||||
// reserved sectors
|
||||
reservedAreaLength := int64(16 * SectorSize)
|
||||
_, err = outfh.Write([]byte(reservedAreaData))
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not write to output file: %s", err)
|
||||
}
|
||||
err = outfh.Truncate(reservedAreaLength)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not truncate output file: %s", err)
|
||||
}
|
||||
_, err = outfh.Seek(reservedAreaLength, os.SEEK_SET)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not seek output file: %s", err)
|
||||
}
|
||||
|
||||
err = nil
|
||||
func() {
|
||||
defer func() {
|
||||
var ok bool
|
||||
e := recover()
|
||||
if e != nil {
|
||||
err, ok = e.(error)
|
||||
if !ok {
|
||||
panic(e)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
bufw := bufio.NewWriter(outfh)
|
||||
|
||||
w := NewISO9660Writer(bufw)
|
||||
|
||||
writePrimaryVolumeDescriptor(w, inputFileSize, inputFilename)
|
||||
writeVolumeDescriptorSetTerminator(w)
|
||||
writePathTable(w, binary.LittleEndian)
|
||||
writePathTable(w, binary.BigEndian)
|
||||
writeData(w, infh, inputFileSize, inputFilename)
|
||||
|
||||
w.Finish()
|
||||
|
||||
err := bufw.Flush()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}()
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not write to output file: %s", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func writePrimaryVolumeDescriptor(w *ISO9660Writer, inputFileSize uint32, inputFilename string) {
|
||||
if len(inputFilename) > 32 {
|
||||
inputFilename = inputFilename[:32]
|
||||
}
|
||||
now := time.Now()
|
||||
|
||||
sw := w.NextSector()
|
||||
if w.CurrentSector() != primaryVolumeSectorNum {
|
||||
Panicf("internal error: unexpected primary volume sector %d", w.CurrentSector())
|
||||
}
|
||||
|
||||
sw.WriteByte('\x01')
|
||||
sw.WriteString(volumeDescriptorSetMagic)
|
||||
sw.WriteByte('\x00')
|
||||
|
||||
sw.WritePaddedString("", 32)
|
||||
sw.WritePaddedString(inputFilename, 32)
|
||||
|
||||
sw.WriteZeros(8)
|
||||
sw.WriteBothEndianDWord(numTotalSectors(inputFileSize))
|
||||
sw.WriteZeros(32)
|
||||
|
||||
sw.WriteBothEndianWord(1) // volume set size
|
||||
sw.WriteBothEndianWord(1) // volume sequence number
|
||||
sw.WriteBothEndianWord(uint16(SectorSize))
|
||||
sw.WriteBothEndianDWord(SectorSize) // path table length
|
||||
|
||||
sw.WriteLittleEndianDWord(littleEndianPathTableSectorNum)
|
||||
sw.WriteLittleEndianDWord(0) // no secondary path tables
|
||||
sw.WriteBigEndianDWord(bigEndianPathTableSectorNum)
|
||||
sw.WriteBigEndianDWord(0) // no secondary path tables
|
||||
|
||||
WriteDirectoryRecord(sw, "\x00", rootDirectorySectorNum) // root directory
|
||||
|
||||
sw.WritePaddedString("", 128) // volume set identifier
|
||||
sw.WritePaddedString("", 128) // publisher identifier
|
||||
sw.WritePaddedString("", 128) // data preparer identifier
|
||||
sw.WritePaddedString("", 128) // application identifier
|
||||
|
||||
sw.WritePaddedString("", 37) // copyright file identifier
|
||||
sw.WritePaddedString("", 37) // abstract file identifier
|
||||
sw.WritePaddedString("", 37) // bibliographical file identifier
|
||||
|
||||
sw.WriteDateTime(now) // volume creation
|
||||
sw.WriteDateTime(now) // most recent modification
|
||||
sw.WriteUnspecifiedDateTime() // expires
|
||||
sw.WriteUnspecifiedDateTime() // is effective (?)
|
||||
|
||||
sw.WriteByte('\x01') // version
|
||||
sw.WriteByte('\x00') // reserved
|
||||
|
||||
sw.PadWithZeros() // 512 (reserved for app) + 653 (zeros)
|
||||
}
|
||||
|
||||
func writeVolumeDescriptorSetTerminator(w *ISO9660Writer) {
|
||||
sw := w.NextSector()
|
||||
if w.CurrentSector() != primaryVolumeSectorNum+1 {
|
||||
Panicf("internal error: unexpected volume descriptor set terminator sector %d", w.CurrentSector())
|
||||
}
|
||||
|
||||
sw.WriteByte('\xFF')
|
||||
sw.WriteString(volumeDescriptorSetMagic)
|
||||
|
||||
sw.PadWithZeros()
|
||||
}
|
||||
|
||||
func writePathTable(w *ISO9660Writer, bo binary.ByteOrder) {
|
||||
sw := w.NextSector()
|
||||
sw.WriteByte(1) // name length
|
||||
sw.WriteByte(0) // number of sectors in extended attribute record
|
||||
sw.WriteDWord(bo, rootDirectorySectorNum)
|
||||
sw.WriteWord(bo, 1) // parent directory recno (root directory)
|
||||
sw.WriteByte(0) // identifier (root directory)
|
||||
sw.WriteByte(1) // padding
|
||||
sw.PadWithZeros()
|
||||
}
|
||||
|
||||
func writeData(w *ISO9660Writer, infh io.Reader, inputFileSize uint32, inputFilename string) {
|
||||
sw := w.NextSector()
|
||||
if w.CurrentSector() != rootDirectorySectorNum {
|
||||
Panicf("internal error: unexpected root directory sector %d", w.CurrentSector())
|
||||
}
|
||||
|
||||
WriteDirectoryRecord(sw, "\x00", w.CurrentSector())
|
||||
WriteDirectoryRecord(sw, "\x01", rootDirectorySectorNum)
|
||||
WriteFileRecordHeader(sw, inputFilename, w.CurrentSector()+1, inputFileSize)
|
||||
|
||||
// Now stream the data. Note that the first buffer is never of SectorSize,
|
||||
// since we've already filled a part of the sector.
|
||||
b := make([]byte, SectorSize)
|
||||
total := uint32(0)
|
||||
for {
|
||||
l, err := infh.Read(b)
|
||||
if err != nil && err != io.EOF {
|
||||
Panicf("could not read from input file: %s", err)
|
||||
}
|
||||
if l > 0 {
|
||||
sw = w.NextSector()
|
||||
sw.Write(b[:l])
|
||||
total += uint32(l)
|
||||
}
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
}
|
||||
if total != inputFileSize {
|
||||
Panicf("input file size changed while the ISO file was being created (expected to read %d, read %d)", inputFileSize, total)
|
||||
} else if w.CurrentSector() != numTotalSectors(inputFileSize)-1 {
|
||||
Panicf("internal error: unexpected last sector number (expected %d, actual %d)",
|
||||
numTotalSectors(inputFileSize)-1, w.CurrentSector())
|
||||
}
|
||||
}
|
||||
|
||||
func numTotalSectors(inputFileSize uint32) uint32 {
|
||||
var numDataSectors uint32
|
||||
numDataSectors = (inputFileSize + (SectorSize - 1)) / SectorSize
|
||||
return 1 + rootDirectorySectorNum + numDataSectors
|
||||
}
|
||||
|
||||
func getInputFileSizeAndName(fh *os.File) (uint32, string, error) {
|
||||
fi, err := fh.Stat()
|
||||
if err != nil {
|
||||
return 0, "", err
|
||||
}
|
||||
if fi.Size() >= math.MaxUint32 {
|
||||
return 0, "", fmt.Errorf("file size %d is too large", fi.Size())
|
||||
}
|
||||
return uint32(fi.Size()), fi.Name(), nil
|
||||
}
|
||||
|
||||
func filenameSatisfiesISOConstraints(filename string) bool {
|
||||
invalidCharacter := func(r rune) bool {
|
||||
// According to ISO9660, only capital letters, digits, and underscores
|
||||
// are permitted. Some sources say a dot is allowed as well. I'm too
|
||||
// lazy to figure it out right now.
|
||||
if r >= 'A' && r <= 'Z' {
|
||||
return false
|
||||
} else if r >= '0' && r <= '9' {
|
||||
return false
|
||||
} else if r == '_' {
|
||||
return false
|
||||
} else if r == '.' {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
return strings.IndexFunc(filename, invalidCharacter) == -1
|
||||
}
|
Loading…
Reference in New Issue
Block a user