c - How to close a file? -


i felt @ peace posix after many years of experience.

then read this message linus torvalds, circa 2002:

int ret; {     ret = close(fd); } while(ret == -1 && errno != ebadf); 

no.

the above is

(a) not portable

(b) not current practice

the "not portable" part comes fact (as pointed out), threaded environment in kernel does close fd on errors, fd may have been validly re-used (by kernel) other thread, , closing fd second time bug.

not looping until ebadf unportable, loop is, due race condition have noticed if hadn't "made peace" taking such things granted.

however, in gcc c++ standard library implementation, basic_file_stdio.cc, have

          __err = fclose(_m_cfile);     while (__err && errno == eintr); 

the primary target library linux, seems not heeding linus.

as far i've come understand, eintr happens after system call blocks, implies kernel received request free descriptor before commencing whatever work got interrupted. there's no need loop. indeed, sa_restart signal behavior not apply close , generate such loop default, precisely because unsafe.

this standard library bug then, right? on every file ever closed c++ application.

edit: avoid causing alarm before guru comes along answer, should note close seems allowed block under specific circumstances, perhaps none of ever apply regular files. i'm not clear on details, should not see eintr close without opting fcntl or setsockopt. nevertheless possibility makes generic library code more dangerous.

with respect posix, r..'s answer related question clear , concise: close() non-restartable special case, , no loop should used.

this surprising me, decided describe findings, followed conclusions , chosen solution @ end.

this not answer. consider more opinion of fellow programmer, including reasoning behind opinion.


posix.1-2001 , posix.1-2008 describe 3 possible errno values may occur: ebadf, eintr, , eio. descriptor state after eintr , eio "unspecified", means may or may not have been closed. ebadf indicates fd not valid descriptor. in other words, posix.1 recommends using

    if (close(fd) == -1) {         /* error occurred, see 'errno'. */     } 

without retry looping close file descriptors.

(even austin group defect #519 r.. mentioned, not recovering close() errors: leaves unspecified whether i/o possible after eintr error, if descriptor left open.)


for linux, close() syscall defined in fs/open.c, __do_close() in fs/file.c managing descriptor table locking, , filp_close() in fs/open.c taking care of details.

in summary, descriptor entry removed table unconditionally first, followed filesystem-specific flushing (f_op->flush()), followed notification (dnotify/fsnotify hook), , removing record or file locks. (most local filesystems ext2, ext3, ext4, xfs, bfs, tmpfs, , on, not have ->flush(), given valid descriptor, close() cannot fail. ecryptfs, exofs, fuse, cifs, , nfs have ->flush() handlers in linux-3.13.6, far can tell.)

this mean in linux, if write error occurs in filesystem-specific ->flush() handler during close(), there no way retry; file descriptor closed, torvalds said.

the freebsd close() man page describes exact same behaviour.

neither openbsd nor mac os x close() man pages describe whether descriptor closed in case of errors, believe share freebsd behaviour.


it seems clear me no loop necessary or required close file descriptor safely. however, close() may still return error.

errno == ebadf indicates file descriptor closed. if code encounters unexpectedly, me indicates there significant fault in code logic, , process should gracefully exit; i'd rather processes die produce garbage.

any other errno values indicate error in finalizing file state. in linux, error related flushing remaining data actual storage. in particular, can imagine enomem in case there no room buffer data, eio if data not sent or written actual device or media, epipe if connection storage lost, enospc if storage full no reservation unflushed data, , on. if file log file, i'd have process report failure , exit gracefully. if file contents still available in memory, remove (unlink) entire file, , retry. otherwise i'd report failure user.

(remember in linux , freebsd, not "leak" file descriptors in error case; guaranteed closed if error occurs. assuming other operating systems might use behave same way.)

the helper function i'll use on like

#include <unistd.h> #include <errno.h>  /**  * closefd - close file descriptor , return error (errno) code  *  * @descriptor: file descriptor close  *  * actual errno stay unmodified. */ static int closefd(const int descriptor) {     int saved_errno, result;      if (descriptor == -1)         return ebadf;      saved_errno = errno;      result = close(descriptor);     if (result == -1)         result = errno;      errno = saved_errno;     return result; } 

i know above safe on linux , freebsd, , assume safe on other posix-y systems. if encounter 1 not, can replace above custom version, wrapping in suitable #ifdef os. reason maintains errno unchanged quirk of coding style; makes short-circuiting error paths shorter (less repeated code).

if closing file contains important user information, fsync() or fdatasync() on prior closing. ensures data hits storage, causes delay compared normal operation; therefore won't ordinary data files.

unless unlink()ing closed file, check closefd() return value, , act accordingly. if can retry, will, @ once or twice. log files , generated/streamed files, warn user.

i want remind reading far we cannot make reliable; not possible. can do, , in opinion should do, detect when error occurs, reliably can. if can , neglible resource use retry, should. in cases, should make sure notification (about error) propagated actual human user. let human worry whether other action, possibly complex, needs done before operation retried. after all, lot of tools used part of larger task, , best course of action depends on larger task.


Comments

Popular posts from this blog

android - Get AccessToken using signpost OAuth without opening a browser (Two legged Oauth) -

org.mockito.exceptions.misusing.InvalidUseOfMatchersException: mockito -

google shop client API returns 400 bad request error while adding an item -