@@ -93,7 +93,8 @@ func lockStateFromHeaders(st *registry.State, mode types.ObjectLockMode, retainU
9393// docs/s3-object-tagging.md §2): bucket, key existence, the lock-enabled gate
9494// when the operation requires it (the lock methods do; tagging has no gate),
9595// then versionId grammar and resolution (via resolveVersion) and the
96- // delete-marker sentinel. Key
96+ // delete-marker sentinel. Call through resolveLockTarget /
97+ // resolveTagTarget, which set the gate for their operation family. Key
9798// existence outranks the lock-enabled check — a missing key on a bucket
9899// without lock is NoSuchKey, never the missing-configuration error
99100// (GetObjectRetention_non_existing_object pins it, matching posix).
@@ -127,6 +128,13 @@ func (b *Backend) resolveStateTarget(ctx context.Context, bucketName, key, versi
127128 return rv , nil
128129}
129130
131+ // resolveLockTarget resolves a lock method's target: resolveStateTarget
132+ // with the lock-enabled gate required. The tagging twin is
133+ // resolveTagTarget (objecttag.go).
134+ func (b * Backend ) resolveLockTarget (ctx context.Context , bucketName , key , versionID string ) (* resolvedVersion , error ) {
135+ return b .resolveStateTarget (ctx , bucketName , key , versionID , true )
136+ }
137+
130138// versionStateOf seeks the resolved version's VersionState block: leaf →
131139// state tree → block. Nil (with no error) when the key or version carries no
132140// explicit state — the callers' ErrNoSuchObjectLockConfiguration case.
@@ -153,7 +161,7 @@ func (b *Backend) versionStateOf(ctx context.Context, rv *resolvedVersion) (*msb
153161// verbatim (§6). An expired retention is returned as stored; expiry is the
154162// controller's judgment.
155163func (b * Backend ) GetObjectRetention (ctx context.Context , bucket , object , versionId string ) ([]byte , error ) {
156- rv , err := b .resolveStateTarget (ctx , bucket , object , versionId , true )
164+ rv , err := b .resolveLockTarget (ctx , bucket , object , versionId )
157165 if err != nil {
158166 return nil , err
159167 }
@@ -171,7 +179,7 @@ func (b *Backend) GetObjectRetention(ctx context.Context, bucket, object, versio
171179// the §2 no-such-configuration sentinel; an explicit OFF is &false (§4.1's
172180// tri-valued hold).
173181func (b * Backend ) GetObjectLegalHold (ctx context.Context , bucket , object , versionId string ) (* bool , error ) {
174- rv , err := b .resolveStateTarget (ctx , bucket , object , versionId , true )
182+ rv , err := b .resolveLockTarget (ctx , bucket , object , versionId )
175183 if err != nil {
176184 return nil , err
177185 }
@@ -186,12 +194,19 @@ func (b *Backend) GetObjectLegalHold(ctx context.Context, bucket, object, versio
186194 return & on , nil
187195}
188196
197+ // mutateVersionLock runs a per-version state write for the lock methods:
198+ // mutateVersionState with the lock-enabled gate required. The tagging twin
199+ // is mutateVersionTags (objecttag.go).
200+ func (b * Backend ) mutateVersionLock (ctx context.Context , bucketName , key , versionID string , mutate func (* msbucket.VersionState )) error {
201+ return b .mutateVersionState (ctx , bucketName , key , versionID , true , mutate )
202+ }
203+
189204// PutObjectRetention stores the controller's retention document on the
190205// resolved version. Mode-transition policy (same-mode replacement,
191206// COMPLIANCE never weakened, governance bypass) ran in the controller before
192207// this is called.
193208func (b * Backend ) PutObjectRetention (ctx context.Context , bucket , object , versionId string , retention []byte ) error {
194- return b .mutateVersionState (ctx , bucket , object , versionId , true , func (vs * msbucket.VersionState ) {
209+ return b .mutateVersionLock (ctx , bucket , object , versionId , func (vs * msbucket.VersionState ) {
195210 vs .Retention = retention
196211 })
197212}
@@ -202,7 +217,7 @@ func (b *Backend) PutObjectLegalHold(ctx context.Context, bucket, object, versio
202217 if status {
203218 hold = msbucket .LegalHoldOn
204219 }
205- return b .mutateVersionState (ctx , bucket , object , versionId , true , func (vs * msbucket.VersionState ) {
220+ return b .mutateVersionLock (ctx , bucket , object , versionId , func (vs * msbucket.VersionState ) {
206221 vs .LegalHold = hold
207222 })
208223}
@@ -212,7 +227,9 @@ func (b *Backend) PutObjectLegalHold(ctx context.Context, bucket, object, versio
212227// version's state block (mutate owns its fields and every other field is
213228// carried, §4.1 rule 3), and the leaf/state-tree/top-MST splice. A
214229// manifest-arm key upgrades to a leaf on its first state write (§4.1 rule 1);
215- // an empty merged block is elided rather than stored. The check order runs
230+ // an empty merged block is elided rather than stored. Call through
231+ // mutateVersionLock / mutateVersionTags, which set the gate for their
232+ // operation family. The check order runs
216233// entirely inside the commit (a missing bucket surfaces through
217234// mapCommitError); key existence outranks lock-enabled, which outranks the
218235// versionId grammar, matching posix and the pinning conformance cases.
0 commit comments