From 9361934479e4b606a079279e9cfbd0188333d975 Mon Sep 17 00:00:00 2001
From: Felicitas Pojtinger <felicitas@pojtinger.com>
Date: Wed, 10 Jul 2024 13:53:32 -0700
Subject: [PATCH] feature: Allow setting read/write, UUID and volume name
 option for EXT4 file system writer

Signed-off-by: Felicitas Pojtinger <felicitas@pojtinger.com>
---
 ext4/internal/compactext4/compact.go | 30 +++++++++++++++++++++++++++-
 ext4/tar2ext4/tar2ext4.go            | 21 +++++++++++++++++++
 2 files changed, 50 insertions(+), 1 deletion(-)

diff --git a/ext4/internal/compactext4/compact.go b/ext4/internal/compactext4/compact.go
index 760b4dc4ef..90063fe9cb 100644
--- a/ext4/internal/compactext4/compact.go
+++ b/ext4/internal/compactext4/compact.go
@@ -31,6 +31,9 @@ type Writer struct {
 	initialized          bool
 	supportInlineData    bool
 	maxDiskSize          int64
+	readWrite            bool
+	uuid                 [16]byte
+	volumeName           [16]byte
 	gdBlocks             uint32
 }
 
@@ -1141,6 +1144,25 @@ func MaximumDiskSize(size int64) Option {
 	}
 }
 
+// ReadWrite instructs the writer to not mark the file system as write-protected.
+func ReadWrite(w *Writer) {
+	w.readWrite = true
+}
+
+// UUID instructs the writer to set the UUID.
+func UUID(uuid [16]byte) Option {
+	return func(w *Writer) {
+		w.uuid = uuid
+	}
+}
+
+// VolumeName instructs the writer to set the volume name.
+func VolumeName(volumeName [16]byte) Option {
+	return func(w *Writer) {
+		w.volumeName = volumeName
+	}
+}
+
 func (w *Writer) init() error {
 	// Skip the defective block inode.
 	w.inodes = make([]*inode, 1, 32)
@@ -1311,6 +1333,10 @@ func (w *Writer) Close() error {
 	// Write the super block
 	var blk [BlockSize]byte
 	b := bytes.NewBuffer(blk[:1024])
+	featureRoCompat := format.RoCompatLargeFile | format.RoCompatHugeFile | format.RoCompatExtraIsize
+	if !w.readWrite {
+		featureRoCompat |= format.RoCompatReadonly
+	}
 	sb := &format.SuperBlock{
 		InodesCount:        inodesPerGroup * groups,
 		BlocksCountLow:     diskSize,
@@ -1332,10 +1358,12 @@ func (w *Writer) Close() error {
 		InodeSize:          inodeSize,
 		FeatureCompat:      format.CompatSparseSuper2 | format.CompatExtAttr,
 		FeatureIncompat:    format.IncompatFiletype | format.IncompatExtents | format.IncompatFlexBg,
-		FeatureRoCompat:    format.RoCompatLargeFile | format.RoCompatHugeFile | format.RoCompatExtraIsize | format.RoCompatReadonly,
+		FeatureRoCompat:    featureRoCompat,
 		MinExtraIsize:      extraIsize,
 		WantExtraIsize:     extraIsize,
 		LogGroupsPerFlex:   31,
+		UUID:               w.uuid,
+		VolumeName:         w.volumeName,
 	}
 	if w.supportInlineData {
 		sb.FeatureIncompat |= format.IncompatInlineData
diff --git a/ext4/tar2ext4/tar2ext4.go b/ext4/tar2ext4/tar2ext4.go
index 5af6bc21bf..479034a3a7 100644
--- a/ext4/tar2ext4/tar2ext4.go
+++ b/ext4/tar2ext4/tar2ext4.go
@@ -75,6 +75,27 @@ func MaximumDiskSize(size int64) Option {
 	}
 }
 
+// ReadWrite instructs the writer to not mark the file system as write-protected.
+func ReadWrite() Option {
+	return func(p *params) {
+		p.ext4opts = append(p.ext4opts, compactext4.ReadWrite)
+	}
+}
+
+// UUID instructs the writer to set the UUID.
+func UUID(uuid [16]byte) Option {
+	return func(p *params) {
+		p.ext4opts = append(p.ext4opts, compactext4.UUID(uuid))
+	}
+}
+
+// VolumeName instructs the writer to set the volume name.
+func VolumeName(volumeName [16]byte) Option {
+	return func(p *params) {
+		p.ext4opts = append(p.ext4opts, compactext4.VolumeName(volumeName))
+	}
+}
+
 const (
 	whiteoutPrefix = ".wh."
 	opaqueWhiteout = ".wh..wh..opq"