Files
cdesktop/cde/programs/dtfile/dtcopy/sharedFuncs.c
David Cantrell 9cb1f309f2 Patch out use of deprecated and now removed ustat(2) function on Linux.
dtfile makes use of ustat(2) on certain systems.  This call has been
deprecated in glibc for a while and now, as of glibc-2.28, it has been
removed.  The recommended replacement is to use statfs(2).
2018-08-25 17:10:02 -06:00

828 lines
22 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 libraries and programs; if not, write
* to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
* Floor, Boston, MA 02110-1301 USA
*/
/* $TOG: sharedFuncs.c /main/9 1998/04/06 13:15:57 mgreess $ */
/************************************<+>*************************************
****************************************************************************
*
* FILE: sharedFuncs.c
*
*
* DESCRIPTION: Common functions for both dtfile and dtfile_copy
*
* FUNCTIONS: CreateDefaultImage
* ImageInitialize
* auto_rename
* build_path
* generate_NewPath
* split_path
*
* (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.
*
****************************************************************************
************************************<+>*************************************/
#if defined(SVR4)
# include <sys/fs/ufs_fs.h>
# define ROOTINO UFSROOTINO
#endif /* SVR4 */
#if defined(__linux__) || defined(CSRG_BASED)
# include <sys/param.h>
# define ROOTINO 2
#endif
#include <string.h>
#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/mount.h>
#include <pwd.h>
#include <fcntl.h>
#if !defined(CSRG_BASED) && !defined(__linux__)
#include <ustat.h>
#endif
#if defined(__linux__)
#include <sys/vfs.h>
#endif
#include <dirent.h>
#include <Xm/Xm.h>
#include <Xm/Form.h>
#include <Xm/PushB.h>
#include <Dt/Connect.h>
#include <Dt/DtNlUtils.h>
#include <Dt/SharedProcs.h>
#include "sharedFuncs.h"
#include "dtcopy.h"
/************************************************************************
* Bitmap Data for Default Symbol
**********************************<->***********************************/
static unsigned char errorBits[] = {
0x00, 0x00, 0x00, 0xc0, 0x0f, 0x00, 0xf0, 0x3a, 0x00, 0x58, 0x55, 0x00,
0x2c, 0xa0, 0x00, 0x56, 0x40, 0x01, 0xaa, 0x80, 0x02, 0x46, 0x81, 0x01,
0x8a, 0x82, 0x02, 0x06, 0x85, 0x01, 0x0a, 0x8a, 0x02, 0x06, 0x94, 0x01,
0x0a, 0xe8, 0x02, 0x14, 0x50, 0x01, 0x28, 0xb0, 0x00, 0xd0, 0x5f, 0x00,
0xa0, 0x2a, 0x00, 0x40, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
static unsigned char infoBits[] = {
0x00, 0x00, 0x78, 0x00, 0x54, 0x00, 0x2c, 0x00, 0x54, 0x00, 0x28, 0x00,
0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x2a, 0x00, 0x5c, 0x00, 0x28, 0x00,
0x58, 0x00, 0x28, 0x00, 0x58, 0x00, 0x28, 0x00, 0x58, 0x00, 0x28, 0x00,
0x58, 0x00, 0xae, 0x01, 0x56, 0x01, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00};
static unsigned char questionBits[] = {
0xf0, 0x3f, 0x00, 0x58, 0x55, 0x00, 0xac, 0xaa, 0x00, 0xd6, 0x5f, 0x01,
0xea, 0xbf, 0x02, 0xf6, 0x7f, 0x01, 0xea, 0xba, 0x02, 0xf6, 0x7d, 0x05,
0xea, 0xba, 0x0a, 0x56, 0x7d, 0x15, 0xaa, 0xbe, 0x1e, 0x56, 0x5f, 0x01,
0xac, 0xaf, 0x02, 0x58, 0x57, 0x01, 0xb0, 0xaf, 0x00, 0x60, 0x55, 0x01,
0xa0, 0xaa, 0x00, 0x60, 0x17, 0x00, 0xa0, 0x2f, 0x00, 0x60, 0x17, 0x00,
0xb0, 0x2a, 0x00, 0x50, 0x55, 0x00};
static unsigned char warningBits[] = {
0x00, 0x00, 0x18, 0x00, 0x2c, 0x00, 0x56, 0x00, 0x2a, 0x00, 0x56, 0x00,
0x2a, 0x00, 0x56, 0x00, 0x2c, 0x00, 0x14, 0x00, 0x2c, 0x00, 0x14, 0x00,
0x2c, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x14, 0x00,
0x2c, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00};
static unsigned char workingBits[] = {
0x00, 0x00, 0x00, 0xfe, 0xff, 0x0f, 0xaa, 0xaa, 0x0a, 0x44, 0x55, 0x06,
0xcc, 0x2a, 0x02, 0x44, 0x55, 0x06, 0xcc, 0x2a, 0x02, 0x84, 0x15, 0x06,
0x8c, 0x2a, 0x02, 0x04, 0x15, 0x06, 0x0c, 0x0a, 0x02, 0x04, 0x06, 0x06,
0x0c, 0x0b, 0x02, 0x84, 0x15, 0x06, 0xcc, 0x2a, 0x02, 0x44, 0x55, 0x06,
0xcc, 0x2a, 0x02, 0x44, 0x55, 0x06, 0xcc, 0x2a, 0x02, 0x44, 0x55, 0x06,
0xfe, 0xff, 0x0f, 0x56, 0x55, 0x05, 0x00, 0x00, 0x00};
Widget G_toplevel;
int G_dialog_closed = FALSE;
/*----------------------------------------------------------
*
* split_path
*
* Given a path, return a pointer to the directory (folder)
* and file (object). On error, both will be set to empty
* strings.
*
* The calling routine is responsible for allocating space
* for object and folder.
*
* This function simply searches for the last slash (/) in
* the path and returns the characters preceding the slash
* as folder and the characters after the last slash as
* object. Thus, object could be a directory/folder or a
* file.
*
* There is a complimentary function, build_path, to put
* object and folder back together to form a path.
*
*-----------------------------------------------------------*/
void
split_path(const String path, String folder, String object)
{
String lastSlash;
if ( (lastSlash = strrchr(path,'/')) != NULL)
{
strcpy(object,lastSlash+1);
if(lastSlash == path) /* Must be root Folder */
strcpy(folder,"/");
else
{
*lastSlash = '\0';
strcpy(folder,path);
*lastSlash = '/';
}
}
else
{
folder[0] = object[0] = '\0';
}
} /* end split_path */
char *
get_path(char *path)
{
char *rpath, tmppath[MAX_PATH];
char * _DtCopyPathFromInput();
if (!getcwd(tmppath,MAX_PATH))
return NULL;
rpath = _DtCopyPathFromInput(path,tmppath);
return rpath;
}
/*-----------------------------------------------------------------
*
* build_path
*
* Given a directory (folder) and file (object), build a string
* with the complete path and return it.
*
* The calling routine is responsible for freeing storage used
* for the returned string.
*
* There is a complimentary function, split_path, to take
* path apart to form folder and object
*
*-----------------------------------------------------------------*/
String
build_path(const String folder, const String object)
{
char s[MAX_PATH];
String path;
strncpy(s, folder, MAX_PATH);
strncat(s, "/", MAX_PATH-strlen(s));
strncat(s, object, MAX_PATH-strlen(s));
path = (String) malloc(strlen(s)+1);
strcpy(path,s);
return path;
} /* end build_path */
/*--------------------------------------------------------------
*
* auto_rename
*
* Given a path, generate a new file name and rename the file.
*
* The rc from the system call is returned and errno is set.
*
*--------------------------------------------------------------*/
int
auto_rename(const String path)
{
char newPath[MAX_PATH];
generate_NewPath(newPath,path);
errno = 0;
return (rename(path,newPath));
} /* end auto_rename */
/*-----------------------------------------------------------------------
*
* generate_NewPath
*
* Given a path (and a complete path is required), append a
* sequence number to generate a new file name. The new file
* will not exist. The sequence number consists of a one-character
* delimiter and an integer. The function will start with
* 1, if the file with 1 exists it will continue to 2, etc.
* newPath and oldPath can point to the same area. The sequence number
* is appended to the end of the name unless it contains a
* dot, in which case the sequence number precedes the dot. For example,
* /cde/dtfile/dtcopy/overwrtdialog.c becomes
* /cde/dtfile/dtcopy/overwrtdialog~1.c. However, if the dot
* is the first character in the file name, the sequence number is
* appended, (e.g. .profile --> .profile~1).
*
*-----------------------------------------------------------------------*/
void
generate_NewPath(String newPath, String oldPath)
{
const char delim = '_';
struct stat buf;
String lastDot, lastSlash;
char firstPart[MAX_PATH], lastPart[MAX_PATH];
int i = 0;
int len;
lastDot = strrchr(oldPath,'.');
lastSlash = strrchr(oldPath,'/');
if (lastSlash == NULL)
lastSlash = oldPath - 1; /* allows for no path and first char is dot */
len = lastDot - oldPath;
if ( lastDot != NULL && /* no dot */
lastDot > lastSlash+1 && /* dot is in filename, not directory name */
len != strlen(oldPath)-1 ) /* dot is not last character of filename */
{
/* sequence number will be inserted before filename suffix */
memcpy(firstPart,oldPath,len);
firstPart[len] = '\0';
strcpy(lastPart,lastDot);
}
else
{
/* sequence number will be appended to filename */
strcpy(firstPart,oldPath);
lastPart[0] = '\0';
}
do
{
i++;
sprintf(newPath,"%s%c%d%s",firstPart,delim,i,lastPart);
} while (lstat(newPath,&buf) == 0);
return;
} /* end generate_NewPath */
/****************************************************************
* Create a default images for symbol... used in ClassInitialize.
****************/
XImage *
CreateDefaultImage(
Display *display,
char *bits,
unsigned int width,
unsigned int height )
{
XImage *image ;
image = XCreateImage(display,
DefaultVisual(display, DefaultScreen(display)),
1, XYBitmap, 0, bits, width, height, 8,
(width+7) >> 3);
image->byte_order = LSBFirst;
image->bitmap_unit = 8;
image->bitmap_bit_order = LSBFirst;
return( image) ;
} /* end CreateDefaultImage */
void
ImageInitialize( Display *display )
{
XImage *image;
/* create and install the default images for the symbol */
image = CreateDefaultImage (display, (char *)errorBits, 20, 20);
XmInstallImage (image, "default_xm_error");
image = CreateDefaultImage (display, (char *)infoBits, 11, 24);
XmInstallImage (image, "default_xm_information");
image = CreateDefaultImage (display, (char *)questionBits, 22, 22);
XmInstallImage (image, "default_xm_question");
image = CreateDefaultImage (display, (char *)warningBits, 9, 22);
XmInstallImage (image, "default_xm_warning");
image = CreateDefaultImage (display, (char *)workingBits, 21, 23);
XmInstallImage (image, "default_xm_working");
return ;
} /* end ImageInitialize */
#if !defined(CSRG_BASED) && !defined(__linux__)
static int
CopyFileSysType(
int dev)
{
struct ustat u1;
if(ustat(dev,&u1) < 0)
return -2;
return u1.f_tinode;
}
#else
static int
CopyFileSysType(
int dev)
{
struct statfs u1;
if(statfs(dev,&u1) < 0)
return -2;
return u1.f_ffree;
}
#endif
static int
CopyCheckDeletePermissionRecur(
char *destinationPath)
{
struct stat statbuf;
DIR *dirp;
struct dirent * dp;
Boolean first_file;
char *fnamep;
DPRINTF(("CheckDeletePermissionRecur(\"%s\")\n", destinationPath));
if (lstat(destinationPath, &statbuf) < 0)
return -1; /* probably does not exist */
if (! S_ISDIR(statbuf.st_mode))
{
if(access(destinationPath,04) < 0)
return -1;
return 0; /* no need to check anything more */
}
dirp = opendir (destinationPath);
if (dirp == NULL)
return -1; /* could not read directory */
first_file = True;
while (dp = readdir (dirp))
{
if (strcmp(dp->d_name, ".") != 0 && strcmp(dp->d_name, "..") != 0)
{
if (first_file)
{
/* check for write permission in this directory */
if (access(destinationPath, 04|02|01) < 0)
{
closedir(dirp);
return -1;
}
/* append a '/' to the end of directory name */
fnamep = destinationPath + strlen(destinationPath);
*fnamep++ = '/';
first_file = False;
}
/* append file name to end of directory name */
strcpy(fnamep, dp->d_name);
/* recursively check permission on this file */
if (CopyCheckDeletePermissionRecur(destinationPath))
{
closedir(dirp);
return -1;
}
}
}
closedir(dirp);
return 0;
}
static int
CopyCheckDeletePermission(
char *parentdir,
char *destinationPath)
{
#if defined(__FreeBSD__) || defined(__OpenBSD__)
struct statfs statbuf;
#elif defined(__NetBSD__)
struct statvfs statbuf;
#else
struct stat statbuf;
#endif
char fname[PATH_MAX];
#if defined(__FreeBSD__) || defined(__OpenBSD__)
if (statfs(parentdir,&statbuf) < 0) /* does not exist */
#elif defined(__NetBSD__)
if (statvfs(parentdir,&statbuf) < 0) /* does not exist */
#else
if (lstat(parentdir,&statbuf) < 0) /* does not exist */
#endif
return -1;
/* check if we are root */
if (getuid() == 0)
{
/* if NFS, need to check if server trusts root */
#if defined(CSRG_BASED)
if (!strcmp(statbuf.f_fstypename, "nfs")) /* Root user and nfs */
#else
if (CopyFileSysType(statbuf.st_dev) < 0) /* Root user and nfs */
#endif
{
char *tmpfile;
int rv;
tmpfile = tempnam(parentdir,"dtfile");
if (tmpfile)
{
/* Create a temporary file */
if ( (rv = creat(tmpfile,O_RDONLY)) < 0)
{
free(tmpfile);
return -1;
}
close(rv);
/* Delete the created file */
if (remove(tmpfile) < 0)
{
free(tmpfile);
return -1;
}
free(tmpfile);
}
else
return -1;
}
/* root user can delete anything */
return 0;
}
/* check for read/write and execute permission on parent dir */
if (access(parentdir, R_OK | W_OK | X_OK) < 0)
return -1;
/* copy destinationPath to tmp buffer */
strcpy(fname, destinationPath);
return CopyCheckDeletePermissionRecur(fname);
}
void
CloseTopLevel(
Widget w,
void *client_data,
void *call_data)
{
XtDestroyWidget((Widget)G_toplevel);
exit(-1);
}
void
CheckDeleteAccess(
XtAppContext app_context,
int delay,
Boolean checkPerms,
Boolean move,
char *source_name)
{
if(checkPerms && move)
{
char title[200],*msg,*tmpmsg;
char *tmpstring = strdup(source_name),*tmpptr;
XEvent event;
int perm_status = 0;
delay = 10000;
tmpptr = strrchr(tmpstring,'/');
if(!tmpstring || !tmpptr) /* Error */
perm_status = 1;
else
{
if(tmpptr == tmpstring)
tmpptr = "/";
else
{
*tmpptr = '\0';
tmpptr = tmpstring;
}
perm_status = CopyCheckDeletePermission(tmpptr,source_name);
free(tmpstring);
tmpstring = NULL;
}
if(!perm_status) /* Everything is fine just return */
{
return;
}
free(tmpstring);
strcpy(title,GETMESSAGE(4,7,"Object Trash - Error"));
tmpmsg = GETMESSAGE(4,8,"You do not have permission to put the object \n\n%s\n\ninto trash.\n\nUse the Change Permissions choice from the object's\npopup menu or from the Selected menu to turn on your\nRead permission on the object.\n\nNote: If this object is a folder, you must also have\nRead permission for each of the objects inside the\nfolder before you can put the folder in the trash.");
msg = XtMalloc(strlen(tmpmsg)+strlen(source_name)+2);
sprintf(msg,tmpmsg,source_name);
_DtMessageDialog (G_toplevel, title, msg, 0, FALSE, NULL,
CloseTopLevel, NULL, NULL, False, ERROR_DIALOG);
/*
XtAppAddTimeOut(app_context, delay, TimeoutHandler, NULL);
*/
/* wait for user to close the dialog before exiting */
XtFree(msg);
while (!G_dialog_closed)
{
XtAppNextEvent(app_context, &event);
XtDispatchEvent(&event);
}
}
}
void
TimeoutHandler(XtPointer client_data, XtIntervalId *id)
{
exit(0);
}
/*
* This is a generic function for resolving a cannonical path from user input.
*/
char *
_DtCopyPathFromInput(char *input_string, char *current_dir)
{
char *path = NULL;
char *tmp_path = NULL;
int dir_len;
char *_DtCopyChangeTildeToHome();
/* find relative path */
tmp_path = path = XtNewString(input_string);
/* Strip any spaces from name -- input is overwritten */
path = (char *) _DtStripSpaces(path);
/* Resolve, if there're any, environement variables */
{
FILE *pfp;
char command[MAX_PATH];
sprintf(command,"echo %s",path);
if((pfp=popen(command,"r")) == NULL)
{
}
else
{
if (fscanf(pfp,"%s",command) >= 1)
{
XtFree(path);
path = XtNewString(command);
}
else
path = NULL;
pclose(pfp);
}
}
if (!path)
return NULL;
/* Resolve '~' -- new memory is allocated, old memory is freed */
if (*path == '~')
path = _DtCopyChangeTildeToHome(path);
/* If current dir provided, check for relative path */
if (path && current_dir)
{
if (*path != '/')
{
/* file is relative path i.e. xyz/abc */
if (strcmp(current_dir, "/") == 0)
{
tmp_path = (char *)XtMalloc(strlen(current_dir) + strlen(path) + 1);
sprintf(tmp_path, "%s%s", current_dir, path);
}
else
{
tmp_path = (char *)XtMalloc(strlen(current_dir) + strlen(path) + 2);
sprintf(tmp_path, "%s/%s", current_dir, path);
}
XtFree(path);
path = tmp_path;
tmp_path = NULL;
}
}
else if (!path)
{
XtFree(tmp_path);
return NULL;
}
/* Resolve '.' or '..' -- input is overwritten, output may be NULL! */
/* Save pointer to path to free if output is NULL. */
tmp_path = path;
path = (char *) XeEliminateDots(path);
/* Strip off trailing '/' */
dir_len = strlen(path);
if (dir_len > 1 && *(path + dir_len - 1) == '/')
*(path + dir_len - 1) = '\0';
return path;
}
char *
#ifdef _NO_PROTO
_DtCopyChangeTildeToHome (input_string)
char *input_string;
#else
_DtCopyChangeTildeToHome (
char *input_string)
#endif
{
char *path;
char *full_path;
struct passwd * pwInfo;
char * homedir = (char *) XtNewString(getenv("HOME"));
if ((input_string[1] != '/'))
{
char *path;
/* ~user or ~user/path format */
/* is there a path? */
path = (char *) DtStrchr(input_string, '/');
/* find user */
if (path)
*path = '/';
if ((pwInfo = getpwnam(input_string + 1)) == NULL)
{
/* user doesn't exist */
if (path)
*path = '/';
return NULL;
}
if (path)
{
/* ~user/path format */
*path = '/';
if (strcmp(pwInfo->pw_dir, "/") == 0)
{
/* We don't want to end up with double '/' in the path */
full_path = (char *) XtMalloc(strlen(path) + 1);
strcpy(full_path, path);
}
else
{
full_path = (char *) XtMalloc(strlen(pwInfo->pw_dir) +
strlen(path) + 1);
sprintf(full_path, "%s%s", pwInfo->pw_dir, path);
}
}
else
{
/* ~user format */
full_path = XtMalloc(strlen(pwInfo->pw_dir) + 1);
strcpy(full_path, pwInfo->pw_dir);
}
}
else if (input_string[1])
{
/* ~/path format */
/* NOTE: users_home_dir has trailing '/' */
full_path = (char *) XtMalloc(strlen(homedir) + strlen(input_string+2) + 1);
sprintf(full_path, "%s%s", homedir, (input_string + 2));
}
else
{
/* ~ format */
full_path = XtMalloc(strlen(homedir) + 1);
strcpy(full_path, homedir);
}
XtFree(homedir);
XtFree(input_string);
return(full_path);
}
int
Check(char *spth, char *dpth, int mode)
{
struct stat sbuf, dbuf;
char filename [MAX_PATH];
char * msg;
char * tmpStr;
char title[300];
sbuf.st_ino = 0;
if (lstat (spth, &sbuf) < 0)
{
tmpStr = (GETMESSAGE(4,9, "Cannot open %s"));
msg = XtMalloc(strlen(tmpStr)+strlen(filename)+1);
sprintf(msg,tmpStr,filename);
_DtMessageDialog (G_toplevel, title, msg, 0, FALSE, NULL,
CloseTopLevel, NULL, NULL, False, ERROR_DIALOG);
XtFree(msg);
return 0;
}
(void) strcpy (filename, dpth);
if(mode)
strcpy(title,GETMESSAGE(4,5,"Object Move - Error"));
else
strcpy(title,GETMESSAGE(4,6,"Object Copy - Error"));
dbuf.st_ino = 0;
while (dbuf.st_ino != ROOTINO)
{
/* Destination may not be available, in which case we need to
create it, so just return as successful and the remaining
code takes care of everything */
if (lstat (filename, &dbuf) < 0)
return 0;
if (dbuf.st_ino == sbuf.st_ino)
{
if(mode)
tmpStr = GETMESSAGE(3,19,"Cannot move folder into itself. %s");
else
tmpStr = GETMESSAGE(3,20,"Cannot copy folder into itself. %s");
msg = XtMalloc(strlen(tmpStr)+strlen(dpth)+1);
sprintf(msg,tmpStr,dpth);
_DtMessageDialog (G_toplevel, title, msg, 0, FALSE, NULL,
CloseTopLevel, NULL, NULL, False, ERROR_DIALOG);
XtFree(msg);
return(1);
}
(void) strcat (filename, "/..");
}
return(0);
}