Logo Search packages:      
Sourcecode: vertex version File versions

backup.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>

#include "../include/string.h"
#include "../include/fio.h"
#include "../include/disk.h"

#include "backup.h"


#define VMA_BACKUP_BACKUP_PREFIX    "bak"

static char *VMABackupStringAppendNumber(
      const char *s, int n
);
static char *VMABackupGetBackupPrefix(
      const char *orig_path
);
static int VMABackupRemoveFile(const char *path);
static int VMAMoveFile(const char *src_path, const char *tar_path);
static int VMACopyFile(const char *src_path, const char *tar_path);
static int VMABackupShift(
        const char *orig_path,          /* Full path to original file name. */
        int min, int max,               /* Min and max indexes. */
      int *avail_index,               /* Returns the next available index number. */
        int shift_order,                /* One of VMA_BACKUP_ORDER_*. */
        int can_remove_overflow
);

int VMABackupFile(
        const char *orig_path,          /* Full path to original file name. */
        int min, int max,               /* Min and max indexes. */
        int shift_order,                /* One of VMA_BACKUP_ORDER_*. */
        int can_remove_overflow
);



#define VMA_BACKUP_BACKUP_PREFIX        "bak"



/*
 *    Returns a dynamically allocated string containing the given
 *    string s with a number string created by n appended.
 */
static char *VMABackupStringAppendNumber(
        const char *s, int n
)
{
        int len1, len2, len;
        char *rtn_str;
      char num_str[256];


      if(s == NULL)
          return(NULL);

      sprintf(num_str, "%i", n);

        len1 = strlen(s);
        len2 = strlen(num_str);
        len = len1 + len2 + 1;
        rtn_str = (char *)malloc(len * sizeof(char));
        if(rtn_str != NULL)
        {
            (*rtn_str) = '\0';
            strcat(rtn_str, s);
            strcat(rtn_str, num_str);
        }

        return(rtn_str);
}

/*
 *    Returns a dynamically allocated string containing the original
 *    path with the bak prefix appended.
 *
 *    The calling function needs to deallocate the returned pointer
 *    to the new string.
 */
static char *VMABackupGetBackupPrefix(
        const char *orig_path
)
{
      int len1, len2, len;
      int delim_len;
      const char *delim_str = ".";
      char *rtn_str;


      if(orig_path == NULL)
          return(NULL);

      len1 = strlen(orig_path);
      len2 = strlen(VMA_BACKUP_BACKUP_PREFIX);
      delim_len = strlen(delim_str);

      len = len1 + delim_len + len2 + 1;
      rtn_str = (char *)malloc(len * sizeof(char));
      if(rtn_str != NULL)
      {
          (*rtn_str) = '\0';
          strcat(rtn_str, orig_path);
          strcat(rtn_str, delim_str);
          strcat(rtn_str, VMA_BACKUP_BACKUP_PREFIX);
      }

      return(rtn_str);
}


/*
 *    Removes the given file specified by path.
 */
static int VMABackupRemoveFile(const char *path)
{
      if(path == NULL)
          return(VMA_BACKUP_ERROR);

      if(unlink(path))
          return(VMA_BACKUP_ERROR);

      return(VMA_BACKUP_SUCCESS);
}

/*
 *    Moves the file from source path to the target path.
 *
 *    Does not permit overwrites.
 */
static int VMAMoveFile(const char *src_path, const char *tar_path)
{
      struct stat stat_buf;

      if((src_path == NULL) || (tar_path == NULL))
          return(VMA_BACKUP_ERROR);

      /* Make sure target path does not exist. */
      if(!stat(tar_path, &stat_buf))
          return(VMA_BACKUP_ERROR);

      /* Make sure source path exists and is a regular file
       * (not a link, so use lstat()).
       */
      if(lstat(src_path, &stat_buf))
          return(VMA_BACKUP_ERROR);
      if(!S_ISREG(stat_buf.st_mode))
          return(VMA_BACKUP_ERROR); 

      /* All checks passed, move the file. */
      rename(src_path, tar_path);

      return(VMA_BACKUP_SUCCESS);
}

/*
 *    Makes a copy of the file specified by src_path to the tar_path
 *    which must not exist.
 */
static int VMACopyFile(const char *src_path, const char *tar_path)
{
      FILE *fp_src, *fp_tar;
      int c;
      mode_t orig_m;
        struct stat stat_buf;


        if((src_path == NULL) || (tar_path == NULL))
            return(VMA_BACKUP_ERROR);

        /* Make sure target path does not exist. */
        if(!stat(tar_path, &stat_buf))
            return(VMA_BACKUP_ERROR);

        /* Make sure source path exists and is a regular file. */
        if(stat(src_path, &stat_buf))
            return(VMA_BACKUP_ERROR);
        if(!S_ISREG(stat_buf.st_mode))
            return(VMA_BACKUP_ERROR);
      /* Get original file permissions. */
      orig_m = stat_buf.st_mode;

        /* All checks passed, begin copying. */
      fp_src = FOpen(src_path, "rb");
      if(fp_src == NULL)
          return(VMA_BACKUP_ERROR);

      fp_tar = FOpen(tar_path, "wb");
      if(fp_tar == NULL)
      {
          FClose(fp_src);
          return(VMA_BACKUP_ERROR);
      }

        /* Set permissions on new target file to match the permissions
         * on the original file.
         */
        fchmod(fileno(fp_tar), orig_m);

      /* Begin reading from source file and writing to target file. */
      c = fgetc(fp_src);
      while(c != EOF)
      {
          fputc(c, fp_tar);
          c = fgetc(fp_src);
      }

      /* Close files. */
      FClose(fp_src);
      FClose(fp_tar);

      return(VMA_BACKUP_SUCCESS);
}

/*
 *    Shifts the backup files that are related to the given original
 *    full path file name orig_path.
 *
 *    The can_remove_overflow should be set to true, when it is it
 *    will allow the oldest backup to be removed if it is no longer 
 *    needed.
 *
 *    The avail_index will be updated with the next available index
 *    number in the bounds of min and max or -1 on failure.
 */
static int VMABackupShift(
      const char *orig_path,        /* Full path to original file name. */
      int min, int max,       /* Min and max indexes. */
      int *avail_index,       /* Returns the next available index number. */
      int shift_order,        /* One of VMA_BACKUP_ORDER_*. */
      int can_remove_overflow
)
{
      int i, status;
      char *strptr, *strptr2, *bak_path = NULL;
      struct stat stat_buf;


#define DO_FREE         \
{ \
 if(bak_path != NULL) \
 { \
  free(bak_path); \
  bak_path = NULL; \
 } \
}
      if(avail_index != NULL)
          (*avail_index) = -1;

      if(orig_path == NULL)
          return(VMA_BACKUP_ERROR);

      /* Get backup path. */
      bak_path = VMABackupGetBackupPrefix(orig_path);
      if(bak_path == NULL)
          return(VMA_BACKUP_ERROR);

      /* Handle by shift order. */
      switch(shift_order)
      {
          case VMA_BACKUP_ORDER_HL: /* Max index is newest. */
          /* Look for available index incase shifting is not needed. */
          for(i = min; i <= max; i++)
          {
            strptr = VMABackupStringAppendNumber(bak_path, i);
            if(strptr == NULL)
                continue;

            /* This backup file not exist? */
            if(stat(strptr, &stat_buf))
            {
                free(strptr);
                break;
            }

            free(strptr);
          }
          if((i >= min) && (i <= max))
          {
            /* Got available index, no shifting needed. */
                if(avail_index != NULL)
                    (*avail_index) = i;
          }
          else
          {
            /* No available index, need shifting. */

            /* The oldest backup needs to be deleted. */
            strptr = VMABackupStringAppendNumber(bak_path, min);
            status = VMA_BACKUP_SUCCESS;
            if(can_remove_overflow)
                status = VMABackupRemoveFile(strptr);
            free(strptr);
            strptr = NULL;

            /* Could not remove backup, give up. */
            if(status != VMA_BACKUP_SUCCESS)
            {
                DO_FREE
                return(VMA_BACKUP_ERROR);
            }

            /* Begin moving files. */
            for(i = min; i < max; i++)
            {
                strptr = VMABackupStringAppendNumber(bak_path, i);
                    strptr2 = VMABackupStringAppendNumber(bak_path, i + 1);
                status = VMAMoveFile(strptr2, strptr);
                free(strptr2);
                free(strptr);

                if(status != VMA_BACKUP_SUCCESS)
                {
                  DO_FREE
                  return(VMA_BACKUP_ERROR);
                }
            }

            /* Update next available index. */
            if(avail_index != NULL)
                    (*avail_index) = max;
          }
            break;

          default:  /* VMA_BACKUP_ORDER_LH - Min index is newest. */
            /* Is min index available? */
            strptr = VMABackupStringAppendNumber(bak_path, min);
            if(strptr == NULL)
            {
                DO_FREE
                return(VMA_BACKUP_ERROR);
            }
          if(stat(strptr, &stat_buf))
          {
                /* Got available index, no shifting needed. */
            free(strptr);
                if(avail_index != NULL)
                    (*avail_index) = min;
          }
          else
          {
                /* No available min index, need shifting. */

                /* The oldest backup needs to be deleted. */
                strptr = VMABackupStringAppendNumber(bak_path, max);
                if(can_remove_overflow)
                    VMABackupRemoveFile(strptr);
                free(strptr);
                strptr = NULL;

                /* Begin moving files (note some files may not exist,
             * ignore them).
             */
                for(i = max; i > min; i--)
                {
                    strptr = VMABackupStringAppendNumber(bak_path, i);
                    strptr2 = VMABackupStringAppendNumber(bak_path, i - 1);
                    VMAMoveFile(strptr2, strptr);
                    free(strptr2);
                    free(strptr);
                }

                /* Update next available index. */
                if(avail_index != NULL)
                    (*avail_index) = min;
            }
            break;
      }

      /* Deallocate any locally allocated memory. */
      DO_FREE

#undef DO_FREE

      return(VMA_BACKUP_SUCCESS);
}

/*
 *    Front end call to make a backup of the specified orig_path.
 */
int VMABackupFile(
        const char *orig_path,          /* Full path to original file name. */
        int min, int max,               /* Min and max indexes. */
        int shift_order,                /* One of VMA_BACKUP_ORDER_*. */
        int can_remove_overflow
)
{
      int avail_index, status;
      char *orig_full_path = NULL;
      char *bak_full_path = NULL;
      const char *cstrptr;
      char *strptr;


      if(orig_path == NULL)
          return(VMA_BACKUP_ERROR);


      /* Make orig_path a full path as needed. */
      if(ISPATHABSOLUTE(orig_path))
      {
          orig_full_path = strdup(orig_path);
      }
      else
      {
          char parent[PATH_MAX];

          if(getcwd(parent, PATH_MAX - 1) == NULL)
            return(VMA_BACKUP_ERROR);

          cstrptr = (const char *)PrefixPaths(
            parent, orig_path
          );
          if(cstrptr == NULL)
            return(VMA_BACKUP_ERROR);

          orig_full_path = strdup(cstrptr);
      }
      if(orig_full_path == NULL)
          return(VMA_BACKUP_ERROR);

      /* Get backup file name. */
      bak_full_path = VMABackupGetBackupPrefix(orig_full_path);
        if(bak_full_path == NULL)
            return(VMA_BACKUP_ERROR);


      /* Reset status, it will be updated to VMA_BACKUP_ERROR or
       * some other error code if any problems are encountered below.
       */
      status = VMA_BACKUP_SUCCESS;

      /* Shift any existing backups. */
      if(VMABackupShift(
          orig_full_path,
          min, max, &avail_index, shift_order, can_remove_overflow
      ) == VMA_BACKUP_SUCCESS)
      {
          /* Backup files (if any) were shifted successfully. Now, begin
           * copying the orig_full_path to the index specified by
           * avail_index if it is not -1.
           */
          if(avail_index > -1)
          {
            strptr = VMABackupStringAppendNumber(bak_full_path, avail_index);
            if(strptr != NULL)
            {
                /* Copy orig_full_path to strptr. */
                status = VMACopyFile(orig_full_path, strptr);
                free(strptr);
            }
            else
            {
                status = VMA_BACKUP_ERROR;
            }
          }
          else
          {
            status = VMA_BACKUP_ERROR;
          }
      }
      else
      {
          status = VMA_BACKUP_ERROR;
      }

      /* Deallocate the original full path. */
      if(orig_full_path != NULL)
      {
          free(orig_full_path);
          orig_full_path = NULL;
      }

      if(bak_full_path != NULL)
      {
          free(bak_full_path);
          bak_full_path = NULL;
      }

        return(status);
}


Generated by  Doxygen 1.6.0   Back to index