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
Post a Comment