diff --git a/filesystem/iso9660/directoryentrysystemuseextension.go b/filesystem/iso9660/directoryentrysystemuseextension.go index 1d10ec54..8bef9f6c 100644 --- a/filesystem/iso9660/directoryentrysystemuseextension.go +++ b/filesystem/iso9660/directoryentrysystemuseextension.go @@ -37,7 +37,7 @@ type suspExtension interface { Descriptor() string Source() string Version() uint8 - GetFileExtensions(string, bool, bool) ([]directoryEntrySystemUseExtension, error) + GetFileExtensions(*finalizeFileInfo, bool, bool) ([]directoryEntrySystemUseExtension, error) GetFinalizeExtensions(*finalizeFileInfo) ([]directoryEntrySystemUseExtension, error) Relocatable() bool Relocate(map[string]*finalizeFileInfo) ([]*finalizeFileInfo, map[string]*finalizeFileInfo, error) diff --git a/filesystem/iso9660/finalize.go b/filesystem/iso9660/finalize.go index f4cff559..4a2f972c 100644 --- a/filesystem/iso9660/finalize.go +++ b/filesystem/iso9660/finalize.go @@ -12,6 +12,7 @@ import ( "time" "github.com/diskfs/go-diskfs/util" + "github.com/djherbis/times" ) const ( @@ -41,6 +42,11 @@ type FinalizeOptions struct { // IsDir() bool // abbreviation for Mode().IsDir() // Sys() interface{} // underlying data source (can return nil) // +// Also supports: +// +// AccessTime() time.Time +// ChangeTime() time.Time +// //nolint:structcheck // keep unused members so that we can know their references type finalizeFileInfo struct { path string @@ -56,6 +62,8 @@ type finalizeFileInfo struct { size int64 mode os.FileMode modTime time.Time + accessTime time.Time + changeTime time.Time isDir bool isRoot bool bytes [][]byte @@ -64,7 +72,10 @@ type finalizeFileInfo struct { trueParent *finalizeFileInfo trueChild *finalizeFileInfo elToritoEntry *ElToritoEntry - content []byte + linkTarget string + // content in memory content of file. If this is anything other than nil, including a zero-length slice, + // then this content is used, rather than anything on disk. + content []byte } func (fi *finalizeFileInfo) Name() string { @@ -100,6 +111,15 @@ func (fi *finalizeFileInfo) updateDepth(depth int) { } } } +func (fi *finalizeFileInfo) AccessTime() time.Time { + return fi.accessTime +} +func (fi *finalizeFileInfo) ChangeTime() time.Time { + return fi.changeTime +} +func (fi *finalizeFileInfo) LinkTarget() string { + return fi.linkTarget +} func (fi *finalizeFileInfo) toDirectoryEntry(fs *FileSystem, isSelf, isParent bool) (*directoryEntry, error) { de := &directoryEntry{ @@ -127,7 +147,11 @@ func (fi *finalizeFileInfo) toDirectoryEntry(fs *FileSystem, isSelf, isParent bo } // add appropriate PX, TF, SL, NM extensions for _, e := range fs.suspExtensions { - ext, err := e.GetFileExtensions(path.Join(fs.workspace, fi.path), isSelf, isParent) + var ( + ext []directoryEntrySystemUseExtension + err error + ) + ext, err = e.GetFileExtensions(fi, isSelf, isParent) if err != nil { return nil, fmt.Errorf("error getting extensions for %s at path %s: %v", e.ID(), fi.path, err) } @@ -467,14 +491,18 @@ func (fs *FileSystem) Finalize(options FinalizeOptions) error { shortname, extension := calculateShortnameExtension(path.Base(catname)) // break down the catalog basename from the parent dir catSize := int64(len(bootcat)) + now := time.Now() catEntry = &finalizeFileInfo{ - content: bootcat, - size: catSize, - path: catname, - name: path.Base(catname), - shortname: shortname, - extension: extension, - blocks: calculateBlocks(catSize, fs.blocksize), + content: bootcat, + size: catSize, + path: catname, + name: path.Base(catname), + shortname: shortname, + extension: extension, + blocks: calculateBlocks(catSize, fs.blocksize), + modTime: now, + accessTime: now, + changeTime: now, } // make it the first file files = append([]*finalizeFileInfo{catEntry}, files...) @@ -601,8 +629,9 @@ func (fs *FileSystem) Finalize(options FinalizeOptions) error { }() for _, e := range files { var ( - from *os.File - copied int + from *os.File + copied int + bootTableSize int ) writeAt := int64(e.location) * int64(blocksize) if e.content == nil { @@ -632,6 +661,7 @@ func (fs *FileSystem) Finalize(options FinalizeOptions) error { return fmt.Errorf("failed to write 56 byte boot table to disk %s: %v", e.path, err) } copied += count + bootTableSize += count // remainder of file count, err = copyFileData(from, f, 64, writeAt+64, 0) if err != nil { @@ -644,7 +674,7 @@ func (fs *FileSystem) Finalize(options FinalizeOptions) error { return fmt.Errorf("failed to copy file to disk %s: %v", e.path, err) } } - if copied != int(e.Size()) { + if copied != int(e.Size()+int64(bootTableSize)) { return fmt.Errorf("error copying file %s to disk, copied %d bytes, expected %d", e.path, copied, e.Size()) } } else { @@ -832,7 +862,33 @@ func walkTree(workspace string) ([]*finalizeFileInfo, map[string]*finalizeFileIn name = string([]byte{0x00}) shortname = name } - entry = &finalizeFileInfo{path: fp, name: name, isDir: fi.IsDir(), isRoot: isRoot, modTime: fi.ModTime(), mode: fi.Mode(), size: fi.Size(), shortname: shortname} + actualPath := path.Join(workspace, fp) + t, err := times.Lstat(actualPath) + if err != nil { + return fmt.Errorf("could not get times information for %s: %w", actualPath, err) + } + mode := fi.Mode() + var target string + if fi.Mode()&os.ModeSymlink == os.ModeSymlink { + target, err = os.Readlink(actualPath) + if err != nil { + return fmt.Errorf("unable to read link for %s: %w", actualPath, err) + } + } + + entry = &finalizeFileInfo{ + path: fp, + name: name, + isDir: fi.IsDir(), + isRoot: isRoot, + modTime: fi.ModTime(), + accessTime: t.AccessTime(), + changeTime: t.ChangeTime(), + mode: mode, + size: fi.Size(), + shortname: shortname, + linkTarget: target, + } // we will have to save it as its parent parentDir := filepath.Dir(fp) diff --git a/filesystem/iso9660/rockridge.go b/filesystem/iso9660/rockridge.go index b2360727..c2d61a0a 100644 --- a/filesystem/iso9660/rockridge.go +++ b/filesystem/iso9660/rockridge.go @@ -96,7 +96,7 @@ func (r *rockRidgeExtension) GetFilename(de *directoryEntry) (string, error) { } return name, nil } -func (r *rockRidgeExtension) GetFileExtensions(fp string, isSelf, isParent bool) ([]directoryEntrySystemUseExtension, error) { +func (r *rockRidgeExtension) GetFileExtensionsOld(fp string, isSelf, isParent bool) ([]directoryEntrySystemUseExtension, error) { // we always do PX, TF, NM, SL order ret := []directoryEntrySystemUseExtension{} // do not follow symlinks @@ -148,6 +148,43 @@ func (r *rockRidgeExtension) GetFileExtensions(fp string, isSelf, isParent bool) return ret, nil } +func (r *rockRidgeExtension) GetFileExtensions(ffi *finalizeFileInfo, isSelf, isParent bool) ([]directoryEntrySystemUseExtension, error) { + // we always do PX, TF, NM, SL order + ret := []directoryEntrySystemUseExtension{} + // do not follow symlinks + + // PX + nlink, uid, gid := statt(ffi) + mtime := ffi.ModTime() + + ret = append(ret, rockRidgePosixAttributes{ + mode: ffi.Mode(), + linkCount: nlink, + uid: uid, + gid: gid, + length: r.pxLength, + }) + // TF + tf := rockRidgeTimestamps{longForm: false, stamps: []rockRidgeTimestamp{ + {timestampType: rockRidgeTimestampModify, time: mtime}, + {timestampType: rockRidgeTimestampAccess, time: ffi.AccessTime()}, + {timestampType: rockRidgeTimestampAttribute, time: ffi.ChangeTime()}, + }} + + ret = append(ret, tf) + // NM + if !isSelf && !isParent { + ret = append(ret, rockRidgeName{name: ffi.Name()}) + } + // SL + if ffi.Mode()&os.ModeSymlink == os.ModeSymlink { + // need the target if it is a symlink + ret = append(ret, rockRidgeSymlink{continued: false, name: ffi.LinkTarget()}) + } + + return ret, nil +} + func (r *rockRidgeExtension) GetFinalizeExtensions(fi *finalizeFileInfo) ([]directoryEntrySystemUseExtension, error) { // we look for CL, PL, RE entries ret := []directoryEntrySystemUseExtension{}