/* * Copyright (c) 2000-2002, 2004, 2005 Sendmail, Inc. and its suppliers. * All rights reserved. * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Chris Torek. * * By using this file, you agree to the terms and conditions set * forth in the LICENSE file which can be found at the top level of * the sendmail distribution. */ #include "sm/generic.h" SM_RCSID("@(#)$Id: fopen.c,v 1.28 2006/07/18 02:45:02 ca Exp $") #include "sm/assert.h" #include "sm/error.h" #include "sm/varargs.h" #include "sm/time.h" #include "sm/heap.h" #include "sm/limits.h" #include "sm/io.h" #include "io-int.h" /* ** SM_FLAGS -- translate external (user) flags into internal flags ** ** Paramters: ** flags -- user select flags ** ** Returns: ** Internal flag value matching user selected flags */ /* fixme: Can't we use a simpler (direct) mapping?? */ static int sm_flags(int flags) { int ret; switch (flags) { case SM_IO_RDONLY: /* open for reading */ ret = SMRD; break; case SM_IO_WRONLY: /* open for writing */ case SM_IO_WREXCL: /* exclusive open for writing */ ret = SMWR; break; case SM_IO_APPEND: /* open for appending */ ret = SMWR; break; case SM_IO_RDWR: /* open for read and write */ case SM_IO_RDWRCR: case SM_IO_RDWRTR: case SM_IO_RDWRCRX: ret = SMRW; break; default: ret = 0; break; } return ret; } /* ** SM_IO_OPEN -- open a file of a specific type ** ** Parameters: ** type -- type of file to open ** info -- info describing what is to be opened (type dependent) ** flags -- user selected flags ** newfp -- pointer to new fp (in/out parameter) ** ** Returns: ** usual sm_error code */ sm_ret_T sm_io_open(const sm_stream_T *type, const void *info, int flags, sm_file_T **newfp, ...) { sm_ret_T res; sm_f_flags_T ioflags; sm_file_T *fp; va_list ap; SM_REQUIRE(newfp != NULL); *newfp = NULL; /* just being friendly... */ ioflags = sm_flags(flags); if (ioflags == 0) { /* must give some indication/intent */ return sm_error_perm(SM_EM_IO, EINVAL); } res = sm_fp(type, ioflags, NULL, &fp); if (sm_is_err(res)) return res; /* paranoia? SM_IS_FP(fp); */ va_start(ap, newfp); res = f_open(*fp)(fp, info, flags, ap); va_end(ap); if (sm_is_err(res)) { fp->sm_magic = SM_MAGIC_NULL; /* release */ f_flags(*fp) = 0; /* release */ return res; } #if SM_RPOOL if (rpool != NULL) sm_rpool_attach(rpool, sm_io_fclose, fp); #endif *newfp = fp; return SM_SUCCESS; } /* ** SM_IO_DUP -- duplicate a file pointer ** ** Parameters: ** fp -- file pointer to duplicate ** ** Returns: ** usual sm_error code ** ** Increments the duplicate counter (dup_cnt) for the open file pointer. ** The counter counts the number of duplicates. When the duplicate ** counter is 0 (zero) then the file pointer is the only one left ** (no duplicates, it is the only one). */ sm_ret_T sm_io_dup(sm_file_T *fp) { SM_IS_FP(fp); /* Note: this won't be reached if above macro is active */ if (fp->sm_magic != SM_FILE_MAGIC) return sm_error_perm(SM_EM_IO, EBADF); if (fp->f_dup_cnt >= INT_MAX - 1) { /* Can't let f_dup_cnt wrap! */ return sm_error_perm(SM_EM_IO, EMFILE); } fp->f_dup_cnt++; return SM_SUCCESS; } #if 0 /* ** SM_IO_REOPEN -- open a new file using the old file pointer ** ** Parameters: ** type -- file type to be opened ** timeout -- time to complete the reopen ** info -- infomation about what is to be "re-opened" (type dep.) ** flags -- user flags to map to internal flags ** fp -- the file pointer to reuse ** newfp -- pointer to new file pointer ** rpool -- rpool file to be associated with ** ** Returns: ** usual sm_error type */ sm_ret_T sm_io_reopen(const sm_stream_T *type, const void *info, int flags, sm_file_T *fp, sm_file_T **newfp, sm_rpool_P rpool) { sm_f_flags_T ioflags; sm_ret_T res; sm_file_T *fp2; ioflags = sm_flags(flags); if (ioflags == 0) { (void) sm_io_close(fp, SM_IO_CF_NONE); *newfp = NULL; return sm_error_perm(SM_EM_IO, EINVAL); } /* ** There are actually programs that depend on being able to "reopen" ** descriptors that weren't originally open. Keep this from breaking. ** Remember whether the stream was open to begin with, and which file ** descriptor (if any) was associated with it. If it was attached to ** a descriptor, defer closing it; reopen("/dev/stdin", "r", stdin) ** should work. This is unnecessary if it was not a Unix file. */ if (fp != NULL) { if (fp->sm_magic != SM_FILE_MAGIC) f_flags(*fp) = SMFEOF; /* hold on to it */ else { /* flush the stream; ANSI doesn't require this. */ (void) sm_io_flush(fp); (void) sm_io_close(fp, SM_IO_CF_NONE); } } res = sm_fp(type, ioflags, NULL, &fp2); if (sm_is_err(res)) return res; res = f_open(*fp2)(fp2, info, flags, SM_IO_WHAT_END); if (sm_is_err(res)) { f_flags(*fp2) = 0; /* release */ fp->sm_magic = SM_MAGIC_NULL; /* release */ return res; } /* ** We're not preserving this logic (below) for sm_io because it is now ** abstracted at least one "layer" away. By closing and reopening ** the 1st fd used should be the just released one (when Unix ** behavior followed). Old comment:: ** If reopening something that was open before on a real file, try ** to maintain the descriptor. Various C library routines (perror) ** assume stderr is always fd STDERR_FILENO, even if being reopen'd. */ #if SM_RPOOL if (rpool != NULL) sm_rpool_attach(rpool, sm_io_close, fp2); #endif *newfp = fp2; return SM_SUCCESS; } #endif /* 0 */