@@ -190,7 +190,8 @@ export function fileOf(t: JThread, fd: number): OpenFile {
190190 return f ;
191191}
192192
193- export function flush ( t : JThread , f : OpenFile ) {
193+ // Writes the file's buffered bytes; false if the host refused them.
194+ export function flush ( t : JThread , f : OpenFile ) : boolean {
194195 if ( f . dirty && f . writable && f . path ) {
195196 const data = f . data . slice ( 0 , f . size ) ;
196197 let ok = t . vm . fs . write ( f . path , data ) ;
@@ -201,7 +202,25 @@ export function flush(t: JThread, f: OpenFile) {
201202 t . vm . fs . chmod ( f . path , ( ) => mode ) ;
202203 }
203204 if ( ok ) f . dirty = false ;
205+ return ok ;
204206 }
207+ return true ;
208+ }
209+
210+ const ERRNO : Record < string , string > = {
211+ ENOSPC : "No space left on device" , EFBIG : "File too large" , EDQUOT : "Disk quota exceeded" ,
212+ EACCES : "Permission denied" , EPERM : "Operation not permitted" , EROFS : "Read-only file system" ,
213+ ENOENT : "No such file or directory" ,
214+ } ;
215+
216+ // Where Java learns of a write that failed: HotSpot reports it from write(),
217+ // java-web, which buffers, from the flush, sync or close that writes it out.
218+ export function flushOrThrow ( t : JThread , f : OpenFile ) {
219+ if ( ! flush ( t , f ) ) throw writeFailed ( t , f . path ) ;
220+ }
221+
222+ function writeFailed ( t : JThread , path : string ) : PendingThrow {
223+ return new PendingThrow ( "java/io/IOException" , ERRNO [ t . vm . fs . writeError ( path ) ?? "" ] ?? "Input/output error" ) ;
205224}
206225
207226export function newFd ( t : JThread ) : number {
@@ -213,9 +232,11 @@ export function closeFd(t: JThread, fd: number) {
213232 const tab = table ( t ) ;
214233 const f = tab . files . get ( fd ) ;
215234 if ( f ) {
216- flush ( t , f ) ;
235+ const ok = flush ( t , f ) ;
217236 tab . files . delete ( fd ) ;
218237 if ( f . writable && f . path ) t . vm . opts . fileClosed ?.( f . path ) ;
238+ // the descriptor is closed either way, as with close(2)
239+ if ( ! ok ) throw writeFailed ( t , f . path ) ;
219240 }
220241}
221242
@@ -308,7 +329,7 @@ export const natives: Natives = {
308329 "java/io/FileDescriptor.sync0()V" : ( t , [ fdObj ] ) => {
309330 const fd = t . vm . getField ( fdObj , "java/io/FileDescriptor" , "fd" ) ;
310331 const f = table ( t ) . files . get ( fd ) ;
311- if ( f ) flush ( t , f ) ;
332+ if ( f ) flushOrThrow ( t , f ) ;
312333 } ,
313334 "java/io/FileDescriptor.close0()V" : ( t , [ fdObj ] ) => {
314335 const fd = t . vm . getField ( fdObj , "java/io/FileDescriptor" , "fd" ) ;
0 commit comments