123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538 |
- /*
- * 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 libraries 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 <limits.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[PATH_MAX];
- do {
- errno = 0;
- l = readlink(sourceP, buf, PATH_MAX - 1);
- } 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[PATH_MAX], tgtname[PATH_MAX];
- 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)
- {
- closedir(dirP);
- 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 */
- snprintf(srcname, PATH_MAX, "%s", sourceP);
- srclen = strlen(srcname);
- if (srcname[srclen - 1] != '/')
- srcname[srclen++] = '/';
- snprintf(tgtname, PATH_MAX, "%s", 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 */
- snprintf(srcname, sizeof(srcname), "%s", 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;
- snprintf(dir, sizeof(dir), "%s", 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);
- }
|