1
0
Fork 0
mirror of git://git.code.sf.net/p/cdesktopenv/code synced 2025-03-09 15:50:02 +00:00
cde/cde/programs/dtfile/dtcopy/fsrtns.c
2018-04-05 15:44:37 -06:00

531 lines
12 KiB
C

/*
* CDE - Common Desktop Environment
*
* Copyright (c) 1993-2012, The Open Group. All rights reserved.
*
* These libraries and programs are free software; you can
* redistribute them and/or modify them under the terms of the GNU
* Lesser General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* These libraries and programs are distributed in the hope that
* they will be useful, but WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with these librararies and programs; if not, write
* to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
* Floor, Boston, MA 02110-1301 USA
*/
/* $TOG: fsrtns.c /main/6 1998/10/26 12:41:20 mgreess $ */
/************************************<+>*************************************
****************************************************************************
*
* FILE: fsrtns.c
*
*
* DESCRIPTION: Routines to manipulate files and directores
*
* FUNCTIONS: CopyDir
* CopyFile
* CopyLink
* CopyObject
* EmptyDir
* EraseObject
* fsCopy
* fsCopyLink
* fsEmpty
* fsErase
* fsMove
* fsRename
*
* (c) Copyright 1993, 1994, 1995 Hewlett-Packard Company
* (c) Copyright 1993, 1994, 1995 International Business Machines Corp.
* (c) Copyright 1993, 1994, 1995 Sun Microsystems, Inc.
* (c) Copyright 1993, 1994, 1995 Novell, Inc.
*
****************************************************************************
************************************<+>*************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/time.h>
#include <utime.h>
#include <dirent.h>
#include <sys/file.h>
#include <string.h>
#include "fsrtns.h"
/*--------------------------------------------------------------------
* Callback functions
*------------------------------------------------------------------*/
int (*progressCallback)(char *fname) = NULL;
int (*errorCallback)(char *fname, int errnum) = NULL;
int (*periodicCallback)() = NULL;
/*--------------------------------------------------------------------
* Local subroutines
*------------------------------------------------------------------*/
static int CopyObject(char *sourceP, char *targetP, int repl, int link);
static int EraseObject(char *nameP, int force);
static int
CopyFile(char *sourceP, char *targetP, int repl, struct stat *statP)
/* copy a file; if repl non-zero, overwrite any existing file */
{
int src, tgt;
int nread, nwrite;
char buffer[2048];
struct utimbuf ut;
int rc;
/* open source file for read */
src = open(sourceP, O_RDONLY, 0);
if (src < 0)
return errno;
/* create target file */
tgt = open(targetP, O_CREAT | O_EXCL | O_WRONLY, statP->st_mode & 0777);
if (tgt < 0 && errno == EEXIST && repl) {
rc = EraseObject(targetP, repl);
if (rc) {
close(src);
return rc;
}
tgt = open(targetP, O_CREAT | O_EXCL | O_WRONLY, statP->st_mode & 0777);
}
if (tgt < 0) {
rc = errno;
close(src);
return rc;
}
/* if we have root privileges, make sure file ownership is preserved */
if (geteuid() == 0) {
if (statP->st_uid != 0 || statP->st_gid != getegid()) {
rc = fchown(tgt, statP->st_uid, statP->st_gid);
if (rc) {
rc = errno;
close(src);
close(tgt);
return rc;
}
}
}
/* copy data */
for (;;) {
/* read data from source file */
do {
errno = 0;
nread = read(src, buffer, sizeof(buffer));
} while (nread < 0 && errno == EINTR);
if (nread <= 0)
break;
/* write data to target file */
do {
errno = 0;
nwrite = write(tgt, buffer, nread);
} while (nwrite < 0 && errno == EINTR);
if (nwrite != nread)
break;
if (periodicCallback)
if (periodicCallback() != 0) {
unlink(targetP);
close(src);
close(tgt);
return -1;
}
}
/* check if data copy ended abnormally */
rc = (nread == 0)? 0: (errno != 0)? errno: -1;
/* close files */
close(src);
if (rc) {
close(tgt);
return rc;
}
if (close(tgt) != 0)
return errno;
/* set target file attributes */
ut.actime = statP->st_atime;
ut.modtime = statP->st_mtime;
rc = utime(targetP, &ut);
return (rc != 0)? errno: 0;
}
static int
CopyLink(char *sourceP, char *targetP, int repl, struct stat *statP)
/* copy a symbolic link */
{
int l, rc;
char buf[1024];
do {
errno = 0;
l = readlink(sourceP, buf, sizeof(buf));
} while (l < 0 && errno == EINTR);
if (l < 0)
return errno;
buf[l] = 0;
if (symlink(buf, targetP) == 0)
return 0;
else if (errno != EEXIST || !repl)
return errno;
if ((rc = EraseObject(targetP, repl)) != 0)
return rc;
if (symlink(buf, targetP) == 0)
return 0;
else
return errno;
}
static int
CopyDir(char *sourceP, char *targetP, int repl, int link, struct stat *statP)
/* copy a directory and recursively all its subdirectories */
{
DIR *dirP; /* open directory */
struct dirent *entryP; /* directory entry */
char srcname[1024], tgtname[1024];
int srclen, tgtlen;
int rc;
/* open source directory */
dirP = opendir(sourceP);
if (dirP == NULL)
return errno;
/* create target directory */
rc = mkdir(targetP, statP->st_mode & 0777);
if (rc < 0 && errno == EEXIST && repl) {
rc = EraseObject(targetP, repl);
if (rc)
return rc;
rc = mkdir(targetP, statP->st_mode & 0777);
}
if (rc < 0) {
rc = errno;
closedir(dirP);
return rc;
}
/* if we have root privileges, make sure directory ownership is preserved */
if (geteuid() == 0) {
if (statP->st_uid != 0 || statP->st_gid != getegid()) {
rc = chown(targetP, statP->st_uid, statP->st_gid);
if (rc) {
rc = errno;
closedir(dirP);
return rc;
}
}
}
/* prepare source and target names */
strcpy(srcname, sourceP);
srclen = strlen(srcname);
if (srcname[srclen - 1] != '/')
srcname[srclen++] = '/';
strcpy(tgtname, targetP);
tgtlen = strlen(tgtname);
if (tgtname[tgtlen - 1] != '/')
tgtname[tgtlen++] = '/';
for (rc = 0; rc == 0; ) {
do {
errno = 0;
entryP = readdir(dirP);
} while (entryP == NULL && errno == EINTR);
if (entryP == NULL) {
rc = errno;
break;
}
if (strcmp(entryP->d_name, ".") == 0 || strcmp(entryP->d_name, "..") == 0)
continue;
strcpy(srcname + srclen, entryP->d_name);
strcpy(tgtname + tgtlen, entryP->d_name);
rc = CopyObject(srcname, tgtname, repl, link);
}
closedir(dirP);
return rc;
}
static int
CopyObject(char *sourceP, char *targetP, int repl, int link)
/* copy a directory, file, or symbolic link */
{
struct stat src_stat;
int rc;
if (progressCallback)
if (progressCallback(sourceP) != 0)
return -1;
if (periodicCallback)
if (periodicCallback() != 0)
return -1;
if (lstat(sourceP, &src_stat) < 0)
rc = errno;
else {
copy_switch:
switch(src_stat.st_mode & S_IFMT) {
case S_IFDIR:
rc = CopyDir(sourceP, targetP, repl, link, &src_stat);
break;
case S_IFREG:
rc = CopyFile(sourceP, targetP, repl, &src_stat);
break;
case S_IFLNK:
if (link)
rc = CopyLink(sourceP, targetP, repl, &src_stat);
else if (stat(sourceP, &src_stat) < 0)
rc = errno;
else
goto copy_switch;
break;
default:
rc = EINVAL;
}
}
/*
* Return code zero means everything is ok;
* return code -1 means the operation is aborted.
* In either case, propagated the return code up.
*/
if (rc <= 0)
return rc;
/*
* Return code > 0 means an error occurred in the last operation.
* Call the the error callback function. If the callback returns
* zero, we return zero; this will cause the error to be ignored.
* Otherwise we return -1 to signal that the operation is aborted.
*/
if (!errorCallback)
return rc;
else if (errorCallback(sourceP, rc) == 0)
return 0;
else
return -1;
}
int
EmptyDir(char *sourceP, int rm, int force)
{
DIR *dirP; /* open directory */
struct dirent *entryP; /* directory entry */
char srcname[1024];
int srclen;
int rc;
/* open source directory */
dirP = opendir(sourceP);
if (dirP == NULL)
return errno;
/* prepare source name */
strcpy(srcname, sourceP);
srclen = strlen(srcname);
if (srcname[srclen - 1] != '/')
srcname[srclen++] = '/';
rc = 0;
while (rc == 0 && (entryP = readdir(dirP)) != NULL) {
if (strcmp(entryP->d_name, ".") == 0 || strcmp(entryP->d_name, "..") == 0)
continue;
strcpy(srcname + srclen, entryP->d_name);
rc = EraseObject(srcname, force);
}
closedir(dirP);
if (rc == 0 && rm) {
rc = rmdir(sourceP);
if (rc < 0)
rc = errno;
}
return rc;
}
static int
EraseObject(char *nameP, int force)
{
struct stat src_stat;
int rc = 0;
if (periodicCallback)
if (periodicCallback() != 0)
return -1;
if (lstat(nameP, &src_stat) < 0)
rc = errno;
else if ((src_stat.st_mode & S_IFMT) == S_IFDIR) {
if (access(nameP, X_OK|W_OK))
return errno;
rc = EmptyDir(nameP, 1, force);
}
else {
if (!force && access(nameP, W_OK))
return errno;
if (unlink(nameP))
rc = errno;
}
/*
* Return code zero means everything is ok;
* return code -1 means the operation is aborted.
* In either case, propagated the return code up.
*/
if (rc <= 0)
return rc;
/*
* Return code > 0 means an error occurred in the last operation.
* Call the the error callback function. If the callback returns
* zero, we return zero; this will cause the error to be ignored.
* Otherwise we return -1 to signal that the operation is aborted.
*/
if (!errorCallback)
return rc;
else if (errorCallback(nameP, rc) == 0)
return 0;
else
return -1;
}
/*--------------------------------------------------------------------
* Exported routines
*------------------------------------------------------------------*/
void
fsRename(char *sourceP, char *targetP, int replace, int *rcP)
{
int rc;
struct stat buf;
if (!replace) {
/* return error if target file already exists */
rc = lstat(targetP, &buf);
if (rc == 0) {
*rcP = EEXIST;
return;
} else if (errno != ENOENT) {
*rcP = errno;
return;
}
}
*rcP = rename(sourceP, targetP);
if (*rcP < 0)
*rcP = errno;
if (replace && *rcP == ENOTDIR || *rcP == EISDIR) {
/* error reason: tried to replace file by directory or vice versa */
*rcP = EraseObject(targetP, replace);
if (*rcP < 0)
return;
*rcP = rename(sourceP, targetP);
if (*rcP < 0)
*rcP = errno;
}
}
void
fsMove(char *sourceP, char *targetP, int replace, int *rcP)
{
/* try to rename */
fsRename(sourceP, targetP, replace, rcP);
if (*rcP == 0 || *rcP != EXDEV)
return;
/* source and target on different file systems: do copy + erase */
{
/* first check if we have write permission in the source directory */
char dir[1024], *p;
strcpy(dir, sourceP);
p = strrchr(dir, '/');
if (p == 0)
strcpy(dir, ".");
else if (p == dir)
strcpy(dir, "/");
else
*p = 0;
if (access(dir, W_OK) != 0) {
*rcP = errno;
return;
}
}
*rcP = CopyObject(sourceP, targetP, replace, 1);
if (*rcP != 0)
return;
*rcP = EraseObject(sourceP, replace);
}
void
fsCopy(char *sourceP, char *targetP, int replace, int *rcP)
{
*rcP = CopyObject(sourceP, targetP, replace, 0);
}
void
fsCopyLink(char *sourceP, char *targetP, int replace, int *rcP)
{
*rcP = CopyObject(sourceP, targetP, replace, 1);
}
void
fsErase(char *nameP, int *rcP, int force)
{
*rcP = EraseObject(nameP, force);
}
void
fsEmpty(char *nameP, int *rcP)
{
*rcP = EmptyDir(nameP, 0, 0);
}