Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Postfix/src/util/   (Postfix Mailserver Version 3.11©)  Datei vom 19.11.2021 mit Größe 12 kB image not shown  

Quelle  edit_file.c

  Sprache: C
 

/*++
/* NAME
/* edit_file 3
/* SUMMARY
/* simple cooperative file updating protocol
/* SYNOPSIS
/* #include <edit_file.h>
/*
/* typedef struct {
/* .in +4
/* char *tmp_path; /* temp. pathname */

/* VSTREAM *tmp_fp; /* temp. stream */
/* /* private members... */
/* .in -4
/* } EDIT_FILE;
/*
/* EDIT_FILE *edit_file_open(original_path, output_flags, output_mode)
/* const char *original_path;
/* int output_flags;
/* mode_t output_mode;
/*
/* int edit_file_close(edit_file)
/* EDIT_FILE *edit_file;
/*
/* void edit_file_cleanup(edit_file)
/* EDIT_FILE *edit_file;
/* DESCRIPTION
/* This module implements a simple protocol for cooperative
/* processes to update one file. The idea is to 1) create a
/* new file under a deterministic temporary pathname, 2)
/* populate the new file with updated information, and 3)
/* rename the new file into the place of the original file.
/* This module provides 1) and 3), and leaves 2) to the
/* application. The temporary pathname is deterministic to
/* avoid accumulation of thrash after program crashes.
/*
/* edit_file_open() implements the first phase of the protocol.
/* It creates or opens an output file with a deterministic
/* temporary pathname, obtained by appending the suffix defined
/* with EDIT_FILE_SUFFIX to the specified original file pathname.
/* The original file itself is not opened.  edit_file_open()
/* then locks the output file for exclusive access, and verifies
/* that the file still exists under the temporary pathname.
/* At this point in the protocol, the current process controls
/* both the output file content and its temporary pathname.
/*
/* In the second phase, the application opens the original
/* file if needed, and updates the output file via the
/* \fBtmp_fp\fR member of the EDIT_FILE data structure.  This
/* phase is not implemented by the edit_file() module.
/*
/* edit_file_close() implements the third and final phase of
/* the protocol.  It flushes the output file to persistent
/* storage, and renames the output file from its temporary
/* pathname into the place of the original file. When any of
/* these operations fails, edit_file_close() behaves as if
/* edit_file_cleanup() was called. Regardless of whether these
/* operations succeed, edit_file_close() releases the exclusive
/* lock, closes the output file, and frees up memory that was
/* allocated by edit_file_open().
/*
/* edit_file_cleanup() aborts the protocol. It discards the
/* output file, releases the exclusive lock, closes the output
/* file, and frees up memory that was allocated by edit_file_open().
/*
/* Arguments:
/* .IP original_path
/* The pathname of the original file that will be replaced by
/* the output file. The temporary pathname for the output file
/* is obtained by appending the suffix defined with EDIT_FILE_SUFFIX
/* to a copy of the specified original file pathname, and is
/* made available via the \fBtmp_path\fR member of the EDIT_FILE
/* data structure.
/* .IP output_flags
/* Flags for opening the output file. These are as with open(2),
/* except that the O_TRUNC flag is ignored.  edit_file_open()
/* always truncates the output file after it has obtained
/* exclusive control over the output file content and temporary
/* pathname.
/* .IP output_mode
/* Permissions for the output file. These are as with open(2),
/* except that the output file is initially created with no
/* group or other access permissions. The specified output
/* file permissions are applied by edit_file_close().
/* .IP edit_file
/* Pointer to data structure that is returned upon successful
/* completion by edit_file_open(), and that must be passed to
/* edit_file_close() or edit_file_cleanup().
/* DIAGNOSTICS
/* Fatal errors: memory allocation failure, fstat() failure,
/* unlink() failure, lock failure, ftruncate() failure.
/*
/* edit_file_open() immediately returns a null pointer when
/* it cannot open the output file.
/*
/* edit_file_close() returns zero on success, VSTREAM_EOF on
/* failure.
/*
/* With both functions, the global errno variable indicates
/* the nature of the problem.  All errors are relative to the
/* temporary output's pathname. With both functions, this
/* pathname is not available via the EDIT_FILE data structure,
/* because that structure was already destroyed, or not created.
/* BUGS
/* In the non-error case, edit_file_open() will not return
/* until it obtains exclusive control over the output file
/* content and temporary pathname.  Applications that are
/* concerned about deadlock should protect the edit_file_open()
/* call with a watchdog timer.
/*
/* When interrupted, edit_file_close() may leave behind a
/* world-readable output file under the temporary pathname.
/* On some systems this can be used to inflict a shared-lock
/* DOS on the protocol.  Applications that are concerned about
/* maximal safety should protect the edit_file_close() call
/* with sigdelay() and sigresume() calls, but this introduces
/* the risk that the program will get stuck forever.
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Based on code originally by:
/* Victor Duchovni
/* Morgan Stanley
/*
/* Packaged into one module with minor improvements by:
/* Wietse Venema
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*--*/


/* System library. */

#include <sys_defs.h>
#include <sys/stat.h>
#include <stdio.h>   /* rename(2) */
#include <errno.h>

 /*
  * This mask selects all permission bits in the st_mode stat data. There is
  * no portable definition (unlike S_IFMT, which is defined for the file type
  * bits). For example, BSD / Linux have ALLPERMS, while Solaris has S_IAMB.
  */

#define FILE_PERM_MASK \
 (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)

/* Utility Library. */

#include <msg.h>
#include <vstream.h>
#include <mymalloc.h>
#include <stringops.h>
#include <myflock.h>
#include <edit_file.h>
#include <warn_stat.h>

 /*
  * Do we reuse and truncate an output file that persists after a crash, or
  * do we unlink it and create a new file?
  */

#define EDIT_FILE_REUSE_AFTER_CRASH

 /*
  * Protocol internals: the temporary file permissions.
  */

#define EDIT_FILE_MODE (S_IRUSR | S_IWUSR) /* temp file mode */

 /*
  * Make complex operations more readable. We could use functions, instead.
  * The main thing is that we keep the _alloc and _free code together.
  */

#define EDIT_FILE_ALLOC(ep, path, mode) do { \
 (ep) = (EDIT_FILE *) mymalloc(sizeof(EDIT_FILE)); \
 (ep)->final_path = mystrdup(path); \
 (ep)->final_mode = (mode); \
 (ep)->tmp_path = concatenate((path), EDIT_FILE_SUFFIX, (char *) 0); \
 (ep)->tmp_fp = 0; \
    } while (0)

#define EDIT_FILE_FREE(ep) do { \
 myfree((ep)->final_path); \
 myfree((ep)->tmp_path); \
 myfree((void *) (ep)); \
    } while (0)

/* edit_file_open - open and lock file with deterministic temporary pathname */

EDIT_FILE *edit_file_open(const char *path, int flags, mode_t mode)
{
    struct stat before_lock;
    struct stat after_lock;
    int     saved_errno;
    EDIT_FILE *ep;

    /*
     * Initialize. Do not bother to optimize for the error case.
     */

    EDIT_FILE_ALLOC(ep, path, mode);

    /*
     * As long as the output file can be opened under the temporary pathname,
     * this code can loop or block forever.
     * 
     * Applications that are concerned about deadlock should protect the
     * edit_file_open() call with a watchdog timer.
     */

    for ( /* void */ ; /* void */ ; (void) vstream_fclose(ep->tmp_fp)) {

 /*
  * Try to open the output file under the temporary pathname. This
  * succeeds or fails immediately. To avoid creating a shared-lock DOS
  * opportunity after we crash, we create the output file with no
  * group or other permissions, and set the final permissions at the
  * end (this is one reason why we try to get exclusive control over
  * the output file instead of the original file). We postpone file
  * truncation until we have obtained exclusive control over the file
  * content and temporary pathname. If the open operation fails, we
  * give up immediately. The caller can retry the call if desirable.
  * 
  * XXX If we replace the vstream_fopen() call by safe_open(), then we
  * should replace the stat() call below by lstat().
 */

 if ((ep->tmp_fp = vstream_fopen(ep->tmp_path, flags & ~(O_TRUNC),
     EDIT_FILE_MODE)) == 0) {
     saved_errno = errno;
     EDIT_FILE_FREE(ep);
     errno = saved_errno;
     return (0);
 }

 /*
  * At this point we may have opened an existing output file that was
  * already locked. Try to lock the open file exclusively. This may
  * take some time.
 */

 if (myflock(vstream_fileno(ep->tmp_fp), INTERNAL_LOCK,
      MYFLOCK_OP_EXCLUSIVE) < 0)
     msg_fatal("lock %s: %m", ep->tmp_path);

 /*
  * At this point we have an exclusive lock, but some other process
  * may have renamed or removed the output file while we were waiting
  * for the lock. If that is the case, back out and try again.
 */

 if (fstat(vstream_fileno(ep->tmp_fp), &before_lock) < 0)
     msg_fatal("open %s: %m", ep->tmp_path);
 if (stat(ep->tmp_path, &after_lock) < 0
     || before_lock.st_dev != after_lock.st_dev
     || before_lock.st_ino != after_lock.st_ino
#ifdef HAS_ST_GEN
     || before_lock.st_gen != after_lock.st_gen
#endif
 /* No need to compare st_rdev or st_nlink here. */
     ) {
     continue;
 }

 /*
  * At this point we have exclusive control over the output file
  * content and its temporary pathname (within the rules of the
  * cooperative protocol). But wait, there is more.
  * 
  * There are many opportunities for trouble when opening a pre-existing
  * output file. Here are just a few.
  * 
  * - Victor observes that a system crash in the middle of the
  * final-phase rename() operation may result in the output file
  * having both the temporary pathname and the final pathname. In that
  * case we must not write to the output file.
  * 
  * - Wietse observes that crashes may also leave the output file in
  * other inconsistent states. To avoid permission-related trouble, we
  * simply refuse to work with an output file that has the wrong
  * temporary permissions. This won't stop the shared-lock DOS if we
  * crash after changing the file permissions, though.
  * 
  * To work around these crash-related problems, remove the temporary
  * pathname, back out, and try again.
 */

 if (!S_ISREG(after_lock.st_mode)
#ifndef EDIT_FILE_REUSE_AFTER_CRASH
     || after_lock.st_size > 0
#endif
     || after_lock.st_nlink > 1
     || (after_lock.st_mode & FILE_PERM_MASK) != EDIT_FILE_MODE) {
     if (unlink(ep->tmp_path) < 0 && errno != ENOENT)
  msg_fatal("unlink %s: %m", ep->tmp_path);
     continue;
 }

 /*
  * Settle the final details.
 */

#ifdef EDIT_FILE_REUSE_AFTER_CRASH
 if (ftruncate(vstream_fileno(ep->tmp_fp), 0) < 0)
     msg_fatal("truncate %s: %m", ep->tmp_path);
#endif
 return (ep);
    }
}

/* edit_file_cleanup - clean up without completing the protocol */

void    edit_file_cleanup(EDIT_FILE *ep)
{

    /*
     * Don't touch the file after we lose the exclusive lock!
     */

    if (unlink(ep->tmp_path) < 0 && errno != ENOENT)
 msg_fatal("unlink %s: %m", ep->tmp_path);
    (void) vstream_fclose(ep->tmp_fp);
    EDIT_FILE_FREE(ep);
}

/* edit_file_close - rename the file into place and close the file */

int     edit_file_close(EDIT_FILE *ep)
{
    VSTREAM *fp = ep->tmp_fp;
    int     fd = vstream_fileno(fp);
    int     saved_errno;

    /*
     * The rename/unlock portion of the protocol is relatively simple. The
     * only things that really matter here are that we change permissions as
     * late as possible, and that we rename the file to its final pathname
     * before we lose the exclusive lock.
     * 
     * Applications that are concerned about maximal safety should protect the
     * edit_file_close() call with sigdelay() and sigresume() calls. It is
     * not safe for us to call these functions directly, because the calls do
     * not nest. It is also not nice to force every caller to run with
     * interrupts turned off.
     */

    if (vstream_fflush(fp) < 0
 || fchmod(fd, ep->final_mode) < 0
#ifdef HAS_FSYNC
 || fsync(fd) < 0
#endif
 || rename(ep->tmp_path, ep->final_path) < 0) {
 saved_errno = errno;
 edit_file_cleanup(ep);
 errno = saved_errno;
 return (VSTREAM_EOF);
    } else {
 (void) vstream_fclose(ep->tmp_fp);
 EDIT_FILE_FREE(ep);
 return (0);
    }
}

Messung V0.5 in Prozent
C=71 H=94 G=83

¤ Dauer der Verarbeitung: 0.13 Sekunden  (vorverarbeitet am  2026-08-08) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.