[Vm-dev] [OpenSmalltalk/opensmalltalk-vm] "Failed to write image file" if stdin is closed at snapshot-and-quit time on Unix (Issue #607)

Tony Garnock-Jones notifications at github.com
Mon Nov 29 15:44:18 UTC 2021


## To reproduce

1. Open a fresh Squeak trunk image
2. doIt:

```smalltalk
FileStream stdin close.
Smalltalk snapshot: true andQuit: true.
```

## Expected

Image saves and quits.

## Actual

Image fails to save, opens an error window with message `Failed to write image file (disk full?)`.

## Analysis

- `writeImageFileIO` calls `sqImageFileOpen`.
- `sqImageFileOpen` in `platforms/unix/vm/sqImageFileAccess.h` never observably fails to its caller - it calls `exit` if `open(2)` fails
- `sqImageFileOpen` can perfectly reasonably hand back file descriptor zero for the new image file! If `FileStream stdin` has been closed, it will in fact do so.
- If it does this, `writeImageFileIO` gets confused, because if the result of `sqImageFileOpen` is equal to `null`, it considers this a failure.

This looks annoyingly Unix-specific, a result of punning raw fds as `sqImageFile` values. Other platforms use e.g. a `FILE*` instead of a raw fd, in which case even if fd 0 were to be opened under the covers, the corresponding `FILE*` would not be `null`.

## Appendix (not very useful): Full bug report from debugger

29 November 2021 4:32:57.802885 pm

VM: unix - Smalltalk
Image: Squeak6.0alpha [latest update: #20751]

SecurityManager state:
Restricted: false
FileAccess: true
SocketAccess: true
Working Dir /tmp/squeaker-run-g38g7lcb
Trusted Dir /tmp/squeaker-run-g38g7lcb/secure
Untrusted Dir /tmp/squeaker-run-g38g7lcb/My Squeak

SmalltalkImage(Object)>>error:
	Receiver: Smalltalk
	Arguments and temporary variables: 
		aString: 	'Failed to write image file (disk full?)'
	Receiver's instance variables: 
		globals: 	Smalltalk

SmalltalkImage>>snapshot:andQuit:withExitCode:embedded:
	Receiver: Smalltalk
	Arguments and temporary variables: 
		save: 	true
		quit: 	true
		exitCode: 	nil
		embeddedFlag: 	false
		resuming: 	nil
		msg: 	'----QUIT----{29 November 2021 . 4:32:05 pm} squeak.image priorSource: 20637916...etc...
	Receiver's instance variables: 
		globals: 	Smalltalk

SmalltalkImage>>snapshot:andQuit:embedded:
	Receiver: Smalltalk
	Arguments and temporary variables: 
		save: 	true
		quit: 	true
		embeddedFlag: 	false
	Receiver's instance variables: 
		globals: 	Smalltalk

SmalltalkImage>>snapshot:andQuit:
	Receiver: Smalltalk
	Arguments and temporary variables: 
		save: 	true
		quit: 	true
	Receiver's instance variables: 
		globals: 	Smalltalk

UndefinedObject>>DoIt
	Receiver: nil
	Arguments and temporary variables: 

	Receiver's instance variables: 
nil

Compiler>>evaluateCue:ifFail:
	Receiver: a Compiler
	Arguments and temporary variables: 
		aCue: 	a CompilationCue
		failBlock: 	[closure] in Compiler>>evaluateCue:ifFail:logged:
		methodNode: 	DoIt
	^ Smalltalk snapshot: true andQuit: true
		method: 	(UndefinedObject>>#DoIt "a CompiledMethod(1015626)")
		value: 	nil
	Receiver's instance variables: 
		parser: 	a Parser
		cue: 	a CompilationCue

Compiler>>evaluateCue:ifFail:logged:
	Receiver: a Compiler
	Arguments and temporary variables: 
		aCue: 	a CompilationCue
		failBlock: 	[closure] in [] in SmalltalkEditor(TextEditor)>>evaluateSelectionAndDo:...etc...
		logFlag: 	true
		value: 	nil
	Receiver's instance variables: 
		parser: 	a Parser
		cue: 	a CompilationCue

Compiler>>evaluate:in:to:environment:notifying:ifFail:logged:
	Receiver: a Compiler
	Arguments and temporary variables: 
		textOrStream: 	a ReadStream
		aContext: 	nil
		receiver: 	nil
		anEnvironment: 	Smalltalk
		aRequestor: 	a SmalltalkEditor
		failBlock: 	[closure] in [] in SmalltalkEditor(TextEditor)>>evaluateSelectionAndDo:...etc...
		logFlag: 	true
	Receiver's instance variables: 
		parser: 	a Parser
		cue: 	a CompilationCue

[] in SmalltalkEditor(TextEditor)>>evaluateSelectionAndDo:
	Receiver: a SmalltalkEditor
	Arguments and temporary variables: 
		aBlock: 	[closure] in SmalltalkEditor(TextEditor)>>evaluateSelection
		result: 	nil
		rcvr: 	nil
		ctxt: 	nil
	Receiver's instance variables: 
		morph: 	a TextMorphForEditView(2236073)
		model: 	a Workspace
		paragraph: 	a NewParagraph
		markBlock: 	a CharacterBlock with index 1 and character $F and rectangle 3 at 0 cor...etc...
		pointBlock: 	a CharacterBlock with index 62 and rectangle 235 at 16 corner: 235 at 32
...etc...
		beginTypeInIndex: 	nil
		emphasisHere: 	#()
		lastParenLocation: 	nil
		otherInterval: 	(49 to: 56)
		oldInterval: 	nil
		typeAhead: 	a WriteStream
		history: 	a TextEditorCommandHistory

FullBlockClosure(BlockClosure)>>on:do:
	Receiver: [closure] in SmalltalkEditor(TextEditor)>>evaluateSelectionAndDo:
	Arguments and temporary variables: 
		exceptionOrExceptionSet: 	OutOfScopeNotification
		handlerAction: 	[closure] in SmalltalkEditor(TextEditor)>>evaluateSelectionAndDo:...etc...
		handlerActive: 	true
		handlerRearmed: 	false
	Receiver's instance variables: 
		outerContext: 	SmalltalkEditor(TextEditor)>>evaluateSelectionAndDo:
		startpcOrMethod: 	([] in TextEditor>>#evaluateSelectionAndDo: "a CompiledBlock(1...etc...
		numArgs: 	0
		receiver: 	a SmalltalkEditor

SmalltalkEditor(TextEditor)>>evaluateSelectionAndDo:
	Receiver: a SmalltalkEditor
	Arguments and temporary variables: 
		aBlock: 	[closure] in SmalltalkEditor(TextEditor)>>evaluateSelection
		result: 	nil
		rcvr: 	nil
		ctxt: 	nil
	Receiver's instance variables: 
		morph: 	a TextMorphForEditView(2236073)
		model: 	a Workspace
		paragraph: 	a NewParagraph
		markBlock: 	a CharacterBlock with index 1 and character $F and rectangle 3 at 0 cor...etc...
		pointBlock: 	a CharacterBlock with index 62 and rectangle 235 at 16 corner: 235 at 32
...etc...
		beginTypeInIndex: 	nil
		emphasisHere: 	#()
		lastParenLocation: 	nil
		otherInterval: 	(49 to: 56)
		oldInterval: 	nil
		typeAhead: 	a WriteStream
		history: 	a TextEditorCommandHistory

SmalltalkEditor(TextEditor)>>evaluateSelection
	Receiver: a SmalltalkEditor
	Arguments and temporary variables: 

	Receiver's instance variables: 
		morph: 	a TextMorphForEditView(2236073)
		model: 	a Workspace
		paragraph: 	a NewParagraph
		markBlock: 	a CharacterBlock with index 1 and character $F and rectangle 3 at 0 cor...etc...
		pointBlock: 	a CharacterBlock with index 62 and rectangle 235 at 16 corner: 235 at 32
...etc...
		beginTypeInIndex: 	nil
		emphasisHere: 	#()
		lastParenLocation: 	nil
		otherInterval: 	(49 to: 56)
		oldInterval: 	nil
		typeAhead: 	a WriteStream
		history: 	a TextEditorCommandHistory

SmalltalkEditor(TextEditor)>>doIt
	Receiver: a SmalltalkEditor
	Arguments and temporary variables: 

	Receiver's instance variables: 
		morph: 	a TextMorphForEditView(2236073)
		model: 	a Workspace
		paragraph: 	a NewParagraph
		markBlock: 	a CharacterBlock with index 1 and character $F and rectangle 3 at 0 cor...etc...
		pointBlock: 	a CharacterBlock with index 62 and rectangle 235 at 16 corner: 235 at 32
...etc...
		beginTypeInIndex: 	nil
		emphasisHere: 	#()
		lastParenLocation: 	nil
		otherInterval: 	(49 to: 56)
		oldInterval: 	nil
		typeAhead: 	a WriteStream
		history: 	a TextEditorCommandHistory

SmalltalkEditor(TextEditor)>>doIt:
	Receiver: a SmalltalkEditor
	Arguments and temporary variables: 
		aKeyboardEvent: 	[140 at 92 keystroke '<Opt-Cmd-d>' (100) 12807]
	Receiver's instance variables: 
		morph: 	a TextMorphForEditView(2236073)
		model: 	a Workspace
		paragraph: 	a NewParagraph
		markBlock: 	a CharacterBlock with index 1 and character $F and rectangle 3 at 0 cor...etc...
		pointBlock: 	a CharacterBlock with index 62 and rectangle 235 at 16 corner: 235 at 32
...etc...
		beginTypeInIndex: 	nil
		emphasisHere: 	#()
		lastParenLocation: 	nil
		otherInterval: 	(49 to: 56)
		oldInterval: 	nil
		typeAhead: 	a WriteStream
		history: 	a TextEditorCommandHistory

SmalltalkEditor(TextEditor)>>dispatchOnKeyboardEvent:
	Receiver: a SmalltalkEditor
	Arguments and temporary variables: 
		aKeyboardEvent: 	[140 at 92 keystroke '<Opt-Cmd-d>' (100) 12807]
		honorCommandKeys: 	true
		typedChar: 	$d
	Receiver's instance variables: 
		morph: 	a TextMorphForEditView(2236073)
		model: 	a Workspace
		paragraph: 	a NewParagraph
		markBlock: 	a CharacterBlock with index 1 and character $F and rectangle 3 at 0 cor...etc...
		pointBlock: 	a CharacterBlock with index 62 and rectangle 235 at 16 corner: 235 at 32
...etc...
		beginTypeInIndex: 	nil
		emphasisHere: 	#()
		lastParenLocation: 	nil
		otherInterval: 	(49 to: 56)
		oldInterval: 	nil
		typeAhead: 	a WriteStream
		history: 	a TextEditorCommandHistory

SmalltalkEditor(TextEditor)>>keyStroke:
	Receiver: a SmalltalkEditor
	Arguments and temporary variables: 
		anEvent: 	[140 at 92 keystroke '<Opt-Cmd-d>' (100) 12807]
	Receiver's instance variables: 
		morph: 	a TextMorphForEditView(2236073)
		model: 	a Workspace
		paragraph: 	a NewParagraph
		markBlock: 	a CharacterBlock with index 1 and character $F and rectangle 3 at 0 cor...etc...
		pointBlock: 	a CharacterBlock with index 62 and rectangle 235 at 16 corner: 235 at 32
...etc...
		beginTypeInIndex: 	nil
		emphasisHere: 	#()
		lastParenLocation: 	nil
		otherInterval: 	(49 to: 56)
		oldInterval: 	nil
		typeAhead: 	a WriteStream
		history: 	a TextEditorCommandHistory

[] in [] in TextMorphForEditView(TextMorph)>>keyStroke:
	Receiver: a TextMorphForEditView(2236073)
	Arguments and temporary variables: 
		evt: 	[140 at 92 keystroke '<Opt-Cmd-d>' (100) 12807]
		action: 	nil
	Receiver's instance variables: 
		bounds: 	0 at 0 corner: 435 at 32
		owner: 	a TransformMorph(3867971)
		submorphs: 	#()
		fullBounds: 	0 at 0 corner: 435 at 32
		color: 	Color black
		extension: 	a MorphExtension (3908410) [other:  (unfocusedSelectionColor -> (Col...etc...
		borderWidth: 	0
		borderColor: 	Color black
		textStyle: 	a TextStyle Bitmap DejaVu Sans 9
		text: 	a Text for 'FileStream stdin close
Smalltalk snapshot: true andQuit: true...etc...
		wrapFlag: 	true
		paragraph: 	a NewParagraph
		editor: 	a SmalltalkEditor
		container: 	nil
		predecessor: 	nil
		successor: 	nil
		backgroundColor: 	nil
		margins: 	3 at 0 corner: 0 at 0
		readOnly: 	false
		autoFit: 	true
		editView: 	a PluggableTextMorphPlus(2181400)
		acceptOnCR: 	false

TextMorphForEditView(TextMorph)>>handleInteraction:fromEvent:
	Receiver: a TextMorphForEditView(2236073)
	Arguments and temporary variables: 
		interactionBlock: 	[closure] in [] in TextMorphForEditView(TextMorph)>>keyStroke:...etc...
		evt: 	[140 at 92 keystroke '<Opt-Cmd-d>' (100) 12807]
		oldEditor: 	a SmalltalkEditor
		oldParagraph: 	a NewParagraph
		oldText: 	a Text for 'FileStream stdin close
Smalltalk save: true andQuit: true'...etc...
		oldSelection: 	{205 at 16 corner: 210 at 32}
	Receiver's instance variables: 
		bounds: 	0 at 0 corner: 435 at 32
		owner: 	a TransformMorph(3867971)
		submorphs: 	#()
		fullBounds: 	0 at 0 corner: 435 at 32
		color: 	Color black
		extension: 	a MorphExtension (3908410) [other:  (unfocusedSelectionColor -> (Col...etc...
		borderWidth: 	0
		borderColor: 	Color black
		textStyle: 	a TextStyle Bitmap DejaVu Sans 9
		text: 	a Text for 'FileStream stdin close
Smalltalk snapshot: true andQuit: true...etc...
		wrapFlag: 	true
		paragraph: 	a NewParagraph
		editor: 	a SmalltalkEditor
		container: 	nil
		predecessor: 	nil
		successor: 	nil
		backgroundColor: 	nil
		margins: 	3 at 0 corner: 0 at 0
		readOnly: 	false
		autoFit: 	true
		editView: 	a PluggableTextMorphPlus(2181400)
		acceptOnCR: 	false

TextMorphForEditView>>handleInteraction:fromEvent:
	Receiver: a TextMorphForEditView(2236073)
	Arguments and temporary variables: 
		interActionBlock: 	[closure] in [] in TextMorphForEditView(TextMorph)>>keyStroke:...etc...
		evt: 	[140 at 92 keystroke '<Opt-Cmd-d>' (100) 12807]
	Receiver's instance variables: 
		bounds: 	0 at 0 corner: 435 at 32
		owner: 	a TransformMorph(3867971)
		submorphs: 	#()
		fullBounds: 	0 at 0 corner: 435 at 32
		color: 	Color black
		extension: 	a MorphExtension (3908410) [other:  (unfocusedSelectionColor -> (Col...etc...
		borderWidth: 	0
		borderColor: 	Color black
		textStyle: 	a TextStyle Bitmap DejaVu Sans 9
		text: 	a Text for 'FileStream stdin close
Smalltalk snapshot: true andQuit: true...etc...
		wrapFlag: 	true
		paragraph: 	a NewParagraph
		editor: 	a SmalltalkEditor
		container: 	nil
		predecessor: 	nil
		successor: 	nil
		backgroundColor: 	nil
		margins: 	3 at 0 corner: 0 at 0
		readOnly: 	false
		autoFit: 	true
		editView: 	a PluggableTextMorphPlus(2181400)
		acceptOnCR: 	false


--- The full stack ---
SmalltalkImage(Object)>>error:
SmalltalkImage>>snapshot:andQuit:withExitCode:embedded:
SmalltalkImage>>snapshot:andQuit:embedded:
SmalltalkImage>>snapshot:andQuit:
UndefinedObject>>DoIt
Compiler>>evaluateCue:ifFail:
Compiler>>evaluateCue:ifFail:logged:
Compiler>>evaluate:in:to:environment:notifying:ifFail:logged:
[] in SmalltalkEditor(TextEditor)>>evaluateSelectionAndDo:
FullBlockClosure(BlockClosure)>>on:do:
SmalltalkEditor(TextEditor)>>evaluateSelectionAndDo:
SmalltalkEditor(TextEditor)>>evaluateSelection
SmalltalkEditor(TextEditor)>>doIt
SmalltalkEditor(TextEditor)>>doIt:
SmalltalkEditor(TextEditor)>>dispatchOnKeyboardEvent:
SmalltalkEditor(TextEditor)>>keyStroke:
[] in [] in TextMorphForEditView(TextMorph)>>keyStroke:
TextMorphForEditView(TextMorph)>>handleInteraction:fromEvent:
TextMorphForEditView>>handleInteraction:fromEvent:
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
[] in TextMorphForEditView(TextMorph)>>keyStroke:
StandardToolSet class>>codeCompletionAround:textMorph:keyStroke:
ToolSet class>>codeCompletionAround:textMorph:keyStroke:
TextMorphForEditView(TextMorph)>>keyStroke:
TextMorphForEditView>>keyStroke:
TextMorphForEditView(Morph)>>handleKeystroke:
TextMorphForEditView(TextMorph)>>handleKeystroke:
KeyboardEvent>>sentTo:
TextMorphForEditView(Morph)>>handleEvent:
TextMorphForEditView(Morph)>>handleFocusEvent:
MorphicEventDispatcher>>doHandlingForFocusEvent:with:
MorphicEventDispatcher>>dispatchFocusEvent:with:
TextMorphForEditView(Morph)>>processFocusEvent:using:
TextMorphForEditView(Morph)>>processFocusEvent:
[] in [] in [] in HandMorph>>sendFocusEvent:to:clear:
[] in ActiveEventVariable class(DynamicVariable class)>>value:during:
FullBlockClosure(BlockClosure)>>ensure:
ActiveEventVariable class(DynamicVariable class)>>value:during:
[] in ActiveEventVariable class>>value:during:
FullBlockClosure(BlockClosure)>>ensure:
ActiveEventVariable class>>value:during:
KeyboardEvent(MorphicEvent)>>becomeActiveDuring:
[] in [] in HandMorph>>sendFocusEvent:to:clear:
[] in ActiveHandVariable class(DynamicVariable class)>>value:during:
FullBlockClosure(BlockClosure)>>ensure:
ActiveHandVariable class(DynamicVariable class)>>value:during:
[] in ActiveHandVariable class>>value:during:
FullBlockClosure(BlockClosure)>>ensure:
ActiveHandVariable class>>value:during:
HandMorph>>becomeActiveDuring:
[] in HandMorph>>sendFocusEvent:to:clear:
[] in ActiveWorldVariable class(DynamicVariable class)>>value:during:
FullBlockClosure(BlockClosure)>>ensure:
ActiveWorldVariable class(DynamicVariable class)>>value:during:
[] in ActiveWorldVariable class>>value:during:
FullBlockClosure(BlockClosure)>>ensure:
ActiveWorldVariable class>>value:during:
PasteUpMorph>>becomeActiveDuring:
HandMorph>>sendFocusEvent:to:clear:
HandMorph>>sendEvent:focus:clear:
HandMorph>>sendKeyboardEvent:
HandMorph>>handleEvent:
-- and more not shown --

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/OpenSmalltalk/opensmalltalk-vm/issues/607
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.squeakfoundation.org/pipermail/vm-dev/attachments/20211129/ed0533ef/attachment-0001.html>


More information about the Vm-dev mailing list