Example #1
0
/* TODO isolate important code and move to files_lib.c. */
static bool FileIsSparse(const char *filename)
{
    MAYBE_SYNC_NOW;

    struct stat statbuf;
    int ret = stat(filename, &statbuf);
    assert_int_not_equal(ret, -1);

    Log(LOG_LEVEL_DEBUG,
        " st_size=%ju ST_NBYTES=%ju ST_NBLOCKS=%ju ST_BLKSIZE=%ju DEV_BSIZE=%ju",
        (uint64_t) statbuf.st_size, (uint64_t) ST_NBYTES(statbuf),
        (uint64_t) ST_NBLOCKS(statbuf), (uint64_t) ST_BLKSIZE(statbuf),
        (uint64_t) DEV_BSIZE);

    if (statbuf.st_size <= ST_NBYTES(statbuf))
    {
        Log(LOG_LEVEL_DEBUG, "File is probably non-sparse");
        return false;
    }
    else
    {
        /* We definitely know the file is sparse, since the allocated bytes
         * are less than the real size. */
        Log(LOG_LEVEL_DEBUG, "File is definitely sparse");
        return true;
    }
}
Example #2
0
bool CopyRegularFileDisk(const char *source, const char *destination)
{
    bool ok1 = false, ok2 = false;       /* initialize before the goto end; */

    int sd = safe_open(source, O_RDONLY | O_BINARY);
    if (sd == -1)
    {
        Log(LOG_LEVEL_INFO, "Can't copy '%s' (open: %s)",
            source, GetErrorStr());
        goto end;
    }

    /* We need to stat the file to get the right source permissions. */
    struct stat statbuf;
    if (stat(source, &statbuf) == -1)
    {
        Log(LOG_LEVEL_INFO, "Can't copy '%s' (stat: %s)",
            source, GetErrorStr());
        goto end;
    }

    /* unlink() + safe_open(O_CREAT|O_EXCL) to avoid
       symlink attacks and races. */
    unlink(destination);

    int dd = safe_open(destination,
                       O_WRONLY | O_CREAT | O_TRUNC | O_EXCL | O_BINARY,
                       statbuf.st_mode);
    if (dd == -1)
    {
        Log(LOG_LEVEL_INFO,
            "Unable to open destination file while copying '%s' to '%s'"
            " (open: %s)", source, destination, GetErrorStr());
        goto end;
    }

    size_t total_bytes_written;
    bool   last_write_was_hole;
    ok1 = FileSparseCopy(sd, source, dd, destination,
                         ST_BLKSIZE(statbuf),
                         &total_bytes_written, &last_write_was_hole);
    bool do_sync = false;
    ok2= FileSparseClose(dd, destination, do_sync,
                         total_bytes_written, last_write_was_hole);

    if (!ok1 || !ok2)
    {
        unlink(destination);
    }

  end:
    if (sd != -1)
    {
        close(sd);
    }
    return ok1 && ok2;
}
Example #3
0
bool CopyRegularFileDisk(const char *source, const char *destination)
{
    int sd, dd;

    if ((sd = open(source, O_RDONLY | O_BINARY)) == -1)
    {
        CfOut(OUTPUT_LEVEL_INFORM, "open", "Can't copy %s!\n", source);
        unlink(destination);
        return false;
    }
    /*
     * We need to stat the file in order to get the right source permissions.
     */
    struct stat statbuf;

    if (cfstat(source, &statbuf) == -1)
    {
        CfOut(OUTPUT_LEVEL_INFORM, "stat", "Can't copy %s!\n", source);
        unlink(destination);
        return false;
    }

    unlink(destination);                /* To avoid link attacks */

    if ((dd = open(destination, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL | O_BINARY, statbuf.st_mode)) == -1)
    {
        CfOut(OUTPUT_LEVEL_INFORM, "open", "Unable to open destination file while doing %s to %s", source, destination);
        close(sd);
        unlink(destination);
        return false;
    }

    int buf_size = ST_BLKSIZE(dstat);
    char *buf = xmalloc(buf_size);

    bool result = CopyData(source, sd, destination, dd, buf, buf_size);

    if (!result)
    {
        unlink(destination);
    }

    close(sd);
    close(dd);
    free(buf);
    return result;
}
Example #4
0
static void init(void)
{
    LogSetGlobalLevel(LOG_LEVEL_DEBUG);

    char *ok = mkdtemp(TEST_DIR);
    assert_int_not_equal(ok, NULL);

    /* Set blk_size */
    struct stat statbuf;
    int ret1 = stat(TEST_DIR, &statbuf);
    assert_int_not_equal(ret1, -1);

    blk_size = ST_BLKSIZE(statbuf);
    Log(LOG_LEVEL_NOTICE,
        "Running sparse file tests with blocksize=%d TESTFILE_SIZE=%d",
        blk_size, TESTFILE_SIZE);
    Log(LOG_LEVEL_NOTICE, "Temporary directory: %s", TEST_DIR);

    // /tmp/files_copy_test-XXXXXX/subdir
    xsnprintf(TEST_SUBDIR, sizeof(TEST_SUBDIR), "%s/%s",
              TEST_DIR, "subdir");
    // /tmp/files_copy_test-XXXXXX/testfile
    xsnprintf(TEST_SRC_FILE, sizeof(TEST_SRC_FILE), "%s/%s",
              TEST_DIR, TEST_FILENAME);
    // /tmp/files_copy_test-XXXXXX/subdir/testfile
    xsnprintf(TEST_DST_FILE, sizeof(TEST_DST_FILE), "%s/%s",
              TEST_SUBDIR, TEST_FILENAME);

    int ret2 = mkdir(TEST_SUBDIR, 0700);
    assert_int_equal(ret2, 0);

    SPARSE_SUPPORT_OK = true;
    if (!FsSupportsSparseFiles(TEST_DST_FILE))
    {
        Log(LOG_LEVEL_NOTICE,
            "filesystem for directory '%s' doesn't seem to support sparse files!"
            " TEST WILL ONLY VERIFY FILE INTEGRITY!", TEST_DIR);

        SPARSE_SUPPORT_OK = false;
    }

    test_has_run[0] = true;
    success[0]      = true;
}
Example #5
0
bool CopyRegularFileDisk(char *source, char *destination, bool make_holes)
{
    int sd, dd, buf_size;
    char *buf, *cp;
    int n_read, *intp;
    long n_read_total = 0;
    int last_write_made_hole = 0;

    if ((sd = open(source, O_RDONLY | O_BINARY)) == -1)
    {
        CfOut(cf_inform, "open", "Can't copy %s!\n", source);
        unlink(destination);
        return false;
    }

    unlink(destination);                /* To avoid link attacks */

    if ((dd = open(destination, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL | O_BINARY, 0600)) == -1)
    {
        close(sd);
        unlink(destination);
        return false;
    }

    buf_size = ST_BLKSIZE(dstat);
    buf = xmalloc(buf_size + sizeof(int));

    while (true)
    {
        if ((n_read = read(sd, buf, buf_size)) == -1)
        {
            if (errno == EINTR)
            {
                continue;
            }

            close(sd);
            close(dd);
            free(buf);
            return false;
        }

        if (n_read == 0)
        {
            break;
        }

        n_read_total += n_read;

        intp = 0;

        if (make_holes)
        {
            buf[n_read] = 1;    /* Sentinel to stop loop.  */

            /* Find first non-zero *word*, or the word with the sentinel.  */

            intp = (int *) buf;

            while (*intp++ == 0)
            {
            }

            /* Find the first non-zero *byte*, or the sentinel.  */

            cp = (char *) (intp - 1);

            while (*cp++ == 0)
            {
            }

            /* If we found the sentinel, the whole input block was zero,
               and we can make a hole.  */

            if (cp > buf + n_read)
            {
                /* Make a hole.  */
                if (lseek(dd, (off_t) n_read, SEEK_CUR) < 0L)
                {
                    CfOut(cf_error, "lseek", "Copy failed (no space?) while doing %s to %s\n", source, destination);
                    free(buf);
                    unlink(destination);
                    close(dd);
                    close(sd);
                    return false;
                }
                last_write_made_hole = 1;
            }
            else
            {
                /* Clear to indicate that a normal write is needed. */
                intp = 0;
            }
        }

        if (intp == 0)
        {
            if (FullWrite(dd, buf, n_read) < 0)
            {
                CfOut(cf_error, "", "Copy failed (no space?) while doing %s to %s\n", source, destination);
                close(sd);
                close(dd);
                free(buf);
                unlink(destination);
                return false;
            }
            last_write_made_hole = 0;
        }
    }

    /* If the file ends with a `hole', something needs to be written at
       the end.  Otherwise the kernel would truncate the file at the end
       of the last write operation.  */

    if (last_write_made_hole)
    {
        /* Write a null character and truncate it again.  */

        if (FullWrite(dd, "", 1) < 0 || ftruncate(dd, n_read_total) < 0)
        {
            CfOut(cf_error, "write", "cfengine: full_write or ftruncate error in CopyReg\n");
            free(buf);
            unlink(destination);
            close(sd);
            close(dd);
            return false;
        }
    }

    close(sd);
    close(dd);

    free(buf);
    return true;
}
Example #6
0
static int
copy_reg (const char *src_path, const char *dst_path,
	  const struct cp_options *x, mode_t dst_mode, int *new_dst,
	  struct stat const *src_sb)
{
  char *buf;
  int buf_size;
  int dest_desc;
  int source_desc;
  struct stat sb;
  struct stat src_open_sb;
  char *cp;
  int *ip;
  int return_val = 0;
  off_t n_read_total = 0;
  int last_write_made_hole = 0;
  int make_holes = (x->sparse_mode == SPARSE_ALWAYS);

  source_desc = open (src_path, O_RDONLY);
  if (source_desc < 0)
    {
      error (0, errno, _("cannot open %s for reading"), quote (src_path));
      return -1;
    }

  if (fstat (source_desc, &src_open_sb))
    {
      error (0, errno, _("cannot fstat %s"), quote (src_path));
      return_val = -1;
      goto close_src_desc;
    }

  /* Compare the source dev/ino from the open file to the incoming,
     saved ones obtained via a previous call to stat.  */
  if (! SAME_INODE (*src_sb, src_open_sb))
    {
      error (0, 0,
	     _("skipping file %s, as it was replaced while being copied"),
	     quote (src_path));
      return_val = -1;
      goto close_src_desc;
    }

  /* These semantics are required for cp.
     The if-block will be taken in move_mode.  */
  if (*new_dst)
    {
      dest_desc = open (dst_path, O_WRONLY | O_CREAT, dst_mode);
    }
  else
    {
      dest_desc = open (dst_path, O_WRONLY | O_TRUNC, dst_mode);

      if (dest_desc < 0 && x->unlink_dest_after_failed_open)
	{
	  if (unlink (dst_path))
	    {
	      error (0, errno, _("cannot remove %s"), quote (dst_path));
	      return_val = -1;
	      goto close_src_desc;
	    }

	  /* Tell caller that the destination file was unlinked.  */
	  *new_dst = 1;

	  /* Try the open again, but this time with different flags.  */
	  dest_desc = open (dst_path, O_WRONLY | O_CREAT, dst_mode);
	}
    }

  if (dest_desc < 0)
    {
      error (0, errno, _("cannot create regular file %s"), quote (dst_path));
      return_val = -1;
      goto close_src_desc;
    }

  /* Determine the optimal buffer size.  */

  if (fstat (dest_desc, &sb))
    {
      error (0, errno, _("cannot fstat %s"), quote (dst_path));
      return_val = -1;
      goto close_src_and_dst_desc;
    }

  buf_size = ST_BLKSIZE (sb);

#if HAVE_STRUCT_STAT_ST_BLOCKS
  if (x->sparse_mode == SPARSE_AUTO && S_ISREG (sb.st_mode))
    {
      /* Use a heuristic to determine whether SRC_PATH contains any
	 sparse blocks. */

      if (fstat (source_desc, &sb))
	{
	  error (0, errno, _("cannot fstat %s"), quote (src_path));
	  return_val = -1;
	  goto close_src_and_dst_desc;
	}

      /* If the file has fewer blocks than would normally
	 be needed for a file of its size, then
	 at least one of the blocks in the file is a hole. */
      if (S_ISREG (sb.st_mode)
	  && sb.st_size / ST_NBLOCKSIZE > ST_NBLOCKS (sb))
	make_holes = 1;
    }
#endif

  /* Make a buffer with space for a sentinel at the end.  */

  buf = (char *) alloca (buf_size + sizeof (int));

  for (;;)
    {
      ssize_t n_read = read (source_desc, buf, buf_size);
      if (n_read < 0)
	{
#ifdef EINTR
	  if (errno == EINTR)
	    continue;
#endif
	  error (0, errno, _("reading %s"), quote (src_path));
	  return_val = -1;
	  goto close_src_and_dst_desc;
	}
      if (n_read == 0)
	break;

      n_read_total += n_read;

      ip = 0;
      if (make_holes)
	{
	  buf[n_read] = 1;	/* Sentinel to stop loop.  */

	  /* Find first nonzero *word*, or the word with the sentinel.  */

	  ip = (int *) buf;
	  while (*ip++ == 0)
	    ;

	  /* Find the first nonzero *byte*, or the sentinel.  */

	  cp = (char *) (ip - 1);
	  while (*cp++ == 0)
	    ;

	  /* If we found the sentinel, the whole input block was zero,
	     and we can make a hole.  */

	  if (cp > buf + n_read)
	    {
	      /* Make a hole.  */
	      if (lseek (dest_desc, (off_t) n_read, SEEK_CUR) < 0L)
		{
		  error (0, errno, _("cannot lseek %s"), quote (dst_path));
		  return_val = -1;
		  goto close_src_and_dst_desc;
		}
	      last_write_made_hole = 1;
	    }
	  else
	    /* Clear to indicate that a normal write is needed. */
	    ip = 0;
	}
      if (ip == 0)
	{
	  size_t n = n_read;
	  if (full_write (dest_desc, buf, n) != n)
	    {
	      error (0, errno, _("writing %s"), quote (dst_path));
	      return_val = -1;
	      goto close_src_and_dst_desc;
	    }
	  last_write_made_hole = 0;
	}
    }

  /* If the file ends with a `hole', something needs to be written at
     the end.  Otherwise the kernel would truncate the file at the end
     of the last write operation.  */

  if (last_write_made_hole)
    {
#if HAVE_FTRUNCATE
      /* Write a null character and truncate it again.  */
      if (full_write (dest_desc, "", 1) != 1
	  || ftruncate (dest_desc, n_read_total) < 0)
#else
      /* Seek backwards one character and write a null.  */
      if (lseek (dest_desc, (off_t) -1, SEEK_CUR) < 0L
	  || full_write (dest_desc, "", 1) != 1)
#endif
	{
	  error (0, errno, _("writing %s"), quote (dst_path));
	  return_val = -1;
	}
    }

close_src_and_dst_desc:
  if (close (dest_desc) < 0)
    {
      error (0, errno, _("closing %s"), quote (dst_path));
      return_val = -1;
    }
close_src_desc:
  if (close (source_desc) < 0)
    {
      error (0, errno, _("closing %s"), quote (src_path));
      return_val = -1;
    }

  return return_val;
}