SAI || Sommersemester 1997 || Systemnahe Software II || Übungen

Übungen zu Systemnahe Software II
Lösungen zu Blatt 1 (Aufgabe 1)


clone.c

/*
 *      clone.c - Duplication of regular Files (with times and permissions).
 *
 *      Martin Hasch, University of Ulm, April 1997.
 */

#include        <sys/types.h>
#include        <sys/stat.h>
#include        <fcntl.h>
#include        <utime.h>
#include        <string.h>
#include        <stdio.h>
#include        <stdlib.h>

#define PATHSEP '/'
#define NAMESIZ 1024                    /* maximum target name size */
#define BUFFSIZ 4096                    /* buffer size for file copy */

#ifndef EXIT_SUCCESS                    /* should be in stdlib.h, though */
#define EXIT_SUCCESS    0
#define EXIT_FAILURE    1
#endif

/*
 *      Find the last component of a path.
 *      Result points into original string.
 */
char * basename(char *path)
{
        char *base;

        base = path + strlen(path);
        /* ignore trailing separators: */
        while ( base > path && base[-1] == PATHSEP )
                --base;
        while ( base > path && base[-1] != PATHSEP )
                --base;
        return base;
}

/*
 *      Check whether file exists and is a directory.
 *      Result is 1 for a directory, else 0.
 */
int is_directory(char *filename)
{
        struct stat statbuf;

        return
                !stat(filename, &statbuf) &&
                (statbuf.st_mode & S_IFMT) == S_IFDIR;
}

/*
 *      Create a clone named clonename of file filename.
 *      Original file must exist and be a regular file, clone must not exist.
 *      Clone gets same permissions and modification time as original.
 *      Access time of original is updated (as a consequence of reading).
 *      Creation time of clone is updated (unavoidably).
 *      Result is 1 on success, otherwise 0.
 */
int clone(char *filename, char *clonename)
{
        struct stat statbuf;
        struct utimbuf ubuf;
        char copybuf[BUFFSIZ];
        int sourcefd, targetfd;
        size_t nbytes;

        if ( stat(filename, &statbuf) ) {
                perror(filename);
                return 0;
        }
        if ( (statbuf.st_mode & S_IFMT) != S_IFREG ) {
                fprintf(stderr, "%s: not a regular file\n", filename);
                return 0;
        }
        if ( (sourcefd = open(filename, O_RDONLY)) < 0 ) {
                perror(filename);
                return 0;
        }
        if ( (targetfd = open(clonename, O_WRONLY | O_CREAT | O_EXCL,
                        statbuf.st_mode)) < 0 ) {
                perror(clonename);
                (void) close(sourcefd);
                return 0;
        }

        while ( (nbytes = read(sourcefd, copybuf, sizeof copybuf)) > 0 &&
                        write(targetfd, copybuf, nbytes) == nbytes )
                ;

        if ( close(targetfd) || nbytes > 0 ) {
                perror(clonename);
                (void) close(sourcefd);
                return 0;
        }
        if ( close(sourcefd) || nbytes < 0 ) {
                perror(filename);
                return 0;
        }

        ubuf.actime = statbuf.st_atime;
        ubuf.modtime = statbuf.st_mtime;
        if ( utime(clonename, &ubuf) ) {
                perror(clonename);
                return 0;
        }
        return 1;
}

/*
 *      Create a clone in directory dirname of file filename.
 *      Result is 1 on success, otherwise 0.
 */
int clone_into(char *filename, char *dirname)
{
        char clonename[NAMESIZ];
        char *base;

        base = basename(filename);
        if ( strlen(dirname) + strlen(base) > NAMESIZ-2 ) {
                fprintf(stderr, "%s%c%s: name too long\n",
                                dirname, PATHSEP, base);
                return 0;
        }
        (void) sprintf(clonename, "%s%c%s", dirname, PATHSEP, base);

        return clone(filename, clonename);
}

/*
 *      Print usage message and exit.
 */
static void usage(char *cmdname)
{
        fprintf(stderr, "Usage: %s f1 f2\n"
                        "       %s f1 ... fn dir\n", cmdname, cmdname);
        exit(EXIT_FAILURE);
}

/*
 *      Main routine: command line processing.
 */
int main(int argc, char *argv[])
{
        int i;
        int errors;
        char * lastarg;

        if ( argc < 3 )
                usage(basename(argv[0]));

        (void) umask(0L);       /* => don't mask mode bits on file creation */
        errors = 0;
        lastarg = argv[argc-1];
        if ( is_directory(lastarg) ) {
                for ( i=1; i<argc-1; ++i )
                        if ( !clone_into(argv[i], lastarg) )
                                ++errors;
        } else {
                if ( argc != 3 )
                        usage(basename(argv[0]));
                if ( !clone(argv[1], argv[2]) )
                        ++errors;
        }
        exit(errors? EXIT_FAILURE: EXIT_SUCCESS);
}

SAI || Sommersemester 1997 || Systemnahe Software II || Übungen

Martin Hasch, April 1997