Add GNU LGPL headers to all .c .C and .h files
[oweals/cde.git] / cde / programs / dtfile / dtcopy / fsrtns.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /* $TOG: fsrtns.c /main/6 1998/10/26 12:41:20 mgreess $ */
24 /************************************<+>*************************************
25  ****************************************************************************
26  *
27  *   FILE:           fsrtns.c
28  *
29  *
30  *   DESCRIPTION:    Routines to manipulate files and directores
31  *
32  *   FUNCTIONS: CopyDir
33  *              CopyFile
34  *              CopyLink
35  *              CopyObject
36  *              EmptyDir
37  *              EraseObject
38  *              fsCopy
39  *              fsCopyLink
40  *              fsEmpty
41  *              fsErase
42  *              fsMove
43  *              fsRename
44  *
45  *   (c) Copyright 1993, 1994, 1995 Hewlett-Packard Company
46  *   (c) Copyright 1993, 1994, 1995 International Business Machines Corp.
47  *   (c) Copyright 1993, 1994, 1995 Sun Microsystems, Inc.
48  *   (c) Copyright 1993, 1994, 1995 Novell, Inc.
49  *
50  ****************************************************************************
51  ************************************<+>*************************************/
52
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <unistd.h>
56 #include <sys/types.h>
57 #include <sys/stat.h>
58 #include <errno.h>
59 #include <fcntl.h>
60 #include <sys/time.h>
61 #include <utime.h>
62 #include <dirent.h>
63 #include <sys/file.h>
64 #include <string.h>
65 #include "fsrtns.h"
66
67
68 /*--------------------------------------------------------------------
69  * Callback functions
70  *------------------------------------------------------------------*/
71
72 int (*progressCallback)(char *fname) = NULL;
73 int (*errorCallback)(char *fname, int errnum) = NULL;
74 int (*periodicCallback)() = NULL;
75
76 /*--------------------------------------------------------------------
77  * Local subroutines
78  *------------------------------------------------------------------*/
79
80 static int CopyObject(char *sourceP, char *targetP, int repl, int link);
81 static int EraseObject(char *nameP, int force);
82
83
84 static int
85 CopyFile(char *sourceP, char *targetP, int repl, struct stat *statP)
86 /* copy a file; if repl non-zero, overwrite any existing file */
87 {
88   int src, tgt;
89   int nread, nwrite;
90   char buffer[2048];
91   struct utimbuf ut;
92   int rc;
93
94   /* open source file for read */
95   src = open(sourceP, O_RDONLY, 0);
96   if (src < 0)
97     return errno;
98
99   /* create target file */
100   tgt = open(targetP, O_CREAT | O_EXCL | O_WRONLY, statP->st_mode & 0777);
101   if (tgt < 0 && errno == EEXIST && repl) {
102     rc = EraseObject(targetP, repl);
103     if (rc) {
104       close(src);
105       return rc;
106     }
107     tgt = open(targetP, O_CREAT | O_EXCL | O_WRONLY, statP->st_mode & 0777);
108   }
109   if (tgt < 0) {
110     rc = errno;
111     close(src);
112     return rc;
113   }
114
115   /* if we have root privileges, make sure file ownership is preserved */
116   if (geteuid() == 0) {
117     if (statP->st_uid != 0 || statP->st_gid != getegid()) {
118       rc = fchown(tgt, statP->st_uid, statP->st_gid);
119       if (rc) {
120         rc = errno;
121         close(src);
122         close(tgt);
123         return rc;
124       }
125     }
126   }
127
128   /* copy data */
129   for (;;) {
130     /* read data from source file */
131     do {
132       errno = 0;
133       nread = read(src, buffer, sizeof(buffer));
134     } while (nread < 0 && errno == EINTR);
135     if (nread <= 0)
136       break;
137
138     /* write data to target file */
139     do {
140       errno = 0;
141       nwrite = write(tgt, buffer, nread);
142     } while (nwrite < 0 && errno == EINTR);
143     if (nwrite != nread)
144       break;
145
146     if (periodicCallback)
147       if (periodicCallback() != 0) {
148         unlink(targetP);
149         close(src);
150         close(tgt);
151         return -1;
152       }
153   }
154
155   /* check if data copy ended abnormally */
156   rc = (nread == 0)? 0: (errno != 0)? errno: -1;
157
158   /* close files */
159   close(src);
160   if (rc) {
161     close(tgt);
162     return rc;
163   }
164
165   if (close(tgt) != 0)
166     return errno;
167
168   /* set target file attributes */
169   ut.actime = statP->st_atime;
170   ut.modtime = statP->st_mtime;
171   rc = utime(targetP, &ut);
172
173   return (rc != 0)? errno: 0;
174 }
175
176
177 static int
178 CopyLink(char *sourceP, char *targetP, int repl, struct stat *statP)
179 /* copy a symbolic link */
180 {
181   int l, rc;
182   char buf[1024];
183
184   do {
185     errno = 0;
186     l = readlink(sourceP, buf, sizeof(buf));
187   } while (l < 0 && errno == EINTR);
188   if (l < 0)
189     return errno;
190   buf[l] = 0;
191   if (symlink(buf, targetP) == 0)
192     return 0;
193   else if (errno != EEXIST || !repl)
194     return errno;
195
196   if ((rc = EraseObject(targetP, repl)) != 0)
197     return rc;
198   if (symlink(buf, targetP) == 0)
199     return 0;
200   else
201     return errno;
202 }
203
204
205 static int
206 CopyDir(char *sourceP, char *targetP, int repl, int link, struct stat *statP)
207 /* copy a directory and recursively all its subdirectories */
208 {
209   DIR *dirP;                      /* open directory */
210   struct dirent *entryP;          /* directory entry */
211   char srcname[1024], tgtname[1024];
212   int srclen, tgtlen;
213   int rc;
214
215   /* open source directory */
216   dirP = opendir(sourceP);
217   if (dirP == NULL)
218     return errno;
219
220   /* create target directory */
221   rc = mkdir(targetP, statP->st_mode & 0777);
222   if (rc < 0 && errno == EEXIST && repl) {
223     rc = EraseObject(targetP, repl);
224     if (rc)
225       return rc;
226     rc = mkdir(targetP, statP->st_mode & 0777);
227   }
228   if (rc < 0) {
229     rc = errno;
230     closedir(dirP);
231     return rc;
232   }
233
234   /* if we have root privileges, make sure directory ownership is preserved */
235   if (geteuid() == 0) {
236     if (statP->st_uid != 0 || statP->st_gid != getegid()) {
237       rc = chown(targetP, statP->st_uid, statP->st_gid);
238       if (rc) {
239         rc = errno;
240         closedir(dirP);
241         return rc;
242       }
243     }
244   }
245
246   /* prepare source and target names */
247   strcpy(srcname, sourceP);
248   srclen = strlen(srcname);
249   if (srcname[srclen - 1] != '/')
250     srcname[srclen++] = '/';
251   strcpy(tgtname, targetP);
252   tgtlen = strlen(tgtname);
253   if (tgtname[tgtlen - 1] != '/')
254     tgtname[tgtlen++] = '/';
255
256   for (rc = 0; rc == 0; ) {
257     do {
258       errno = 0;
259       entryP = readdir(dirP);
260     } while (entryP == NULL && errno == EINTR);
261     if (entryP == NULL) {
262       rc = errno;
263       break;
264     }
265     if (strcmp(entryP->d_name, ".") == 0 || strcmp(entryP->d_name, "..") == 0)
266       continue;
267
268     strcpy(srcname + srclen, entryP->d_name);
269     strcpy(tgtname + tgtlen, entryP->d_name);
270
271     rc = CopyObject(srcname, tgtname, repl, link);
272   }
273
274   closedir(dirP);
275   return rc;
276 }
277
278
279 static int
280 CopyObject(char *sourceP, char *targetP, int repl, int link)
281 /* copy a directory, file, or symbolic link */
282 {
283   struct stat src_stat;
284   int rc;
285
286   if (progressCallback)
287     if (progressCallback(sourceP) != 0)
288       return -1;
289
290   if (periodicCallback)
291     if (periodicCallback() != 0)
292       return -1;
293
294   if (lstat(sourceP, &src_stat) < 0)
295     rc = errno;
296
297   else {
298
299 copy_switch:
300
301     switch(src_stat.st_mode & S_IFMT) {
302
303       case S_IFDIR:
304         rc = CopyDir(sourceP, targetP, repl, link, &src_stat);
305         break;
306
307       case S_IFREG:
308         rc = CopyFile(sourceP, targetP, repl, &src_stat);
309         break;
310
311       case S_IFLNK:
312         if (link)
313           rc = CopyLink(sourceP, targetP, repl, &src_stat);
314         else if (stat(sourceP, &src_stat) < 0)
315           rc = errno;
316         else
317           goto copy_switch;
318         break;
319
320       default:
321         rc = EINVAL;
322     }
323   }
324
325   /*
326    * Return code zero means everything is ok;
327    * return code -1 means the operation is aborted.
328    * In either case, propagated the return code up.
329    */
330   if (rc <= 0)
331     return rc;
332
333   /*
334    * Return code > 0 means an error occurred in the last operation.
335    * Call the the error callback function.  If the callback returns
336    * zero, we return zero; this will cause the error to be ignored.
337    * Otherwise we return -1 to signal that the operation is aborted.
338    */
339   if (!errorCallback)
340     return rc;
341   else if (errorCallback(sourceP, rc) == 0)
342     return 0;
343   else
344     return -1;
345 }
346
347
348 int
349 EmptyDir(char *sourceP, int rm, int force)
350 {
351   DIR *dirP;                      /* open directory */
352   struct dirent *entryP;          /* directory entry */
353   char srcname[1024];
354   int srclen;
355   int rc;
356
357   /* open source directory */
358   dirP = opendir(sourceP);
359   if (dirP == NULL)
360     return errno;
361
362   /* prepare source name */
363   strcpy(srcname, sourceP);
364   srclen = strlen(srcname);
365   if (srcname[srclen - 1] != '/')
366     srcname[srclen++] = '/';
367
368   rc = 0;
369   while (rc == 0 && (entryP = readdir(dirP)) != NULL) {
370     if (strcmp(entryP->d_name, ".") == 0 || strcmp(entryP->d_name, "..") == 0)
371       continue;
372     strcpy(srcname + srclen, entryP->d_name);
373     rc = EraseObject(srcname, force);
374   }
375
376   closedir(dirP);
377   if (rc == 0 && rm) {
378     rc = rmdir(sourceP);
379     if (rc < 0)
380       rc = errno;
381   }
382   return rc;
383 }
384
385
386 static int
387 EraseObject(char *nameP, int force)
388 {
389   struct stat src_stat;
390   int rc;
391
392   if (periodicCallback)
393     if (periodicCallback() != 0)
394       return -1;
395
396   if (lstat(nameP, &src_stat) < 0)
397     rc = errno;
398   else if ((src_stat.st_mode & S_IFMT) == S_IFDIR) {
399     if (! access(nameP, X_OK|W_OK))
400       return errno;
401     rc = EmptyDir(nameP, 1, force);
402   }
403   else {
404     if (! (force || access(nameP, W_OK)))
405       return errno;
406
407     if (! unlink(nameP))
408       rc = errno;
409   }
410
411   /*
412    * Return code zero means everything is ok;
413    * return code -1 means the operation is aborted.
414    * In either case, propagated the return code up.
415    */
416   if (rc <= 0)
417     return rc;
418
419   /*
420    * Return code > 0 means an error occurred in the last operation.
421    * Call the the error callback function.  If the callback returns
422    * zero, we return zero; this will cause the error to be ignored.
423    * Otherwise we return -1 to signal that the operation is aborted.
424    */
425   if (!errorCallback)
426     return rc;
427   else if (errorCallback(nameP, rc) == 0)
428     return 0;
429   else
430     return -1;
431 }
432
433 /*--------------------------------------------------------------------
434  * Exported routines
435  *------------------------------------------------------------------*/
436
437 void
438 fsRename(char *sourceP, char *targetP, int replace, int *rcP)
439 {
440   int rc;
441   struct stat buf;
442
443   if (!replace) {
444     /* return error if target file already exists */
445     rc = lstat(targetP, &buf);
446     if (rc == 0) {
447       *rcP = EEXIST;
448       return;
449     } else if (errno != ENOENT) {
450       *rcP = errno;
451       return;
452     }
453   }
454   *rcP = rename(sourceP, targetP);
455   if (*rcP < 0)
456     *rcP = errno;
457
458   if (replace && *rcP == ENOTDIR || *rcP == EISDIR) {
459     /* error reason: tried to replace file by directory or vice versa */
460     *rcP = EraseObject(targetP, replace);
461     if (*rcP < 0)
462       return;
463     *rcP = rename(sourceP, targetP);
464     if (*rcP < 0)
465       *rcP = errno;
466   }
467 }
468
469
470 void
471 fsMove(char *sourceP, char *targetP, int replace, int *rcP)
472 {
473   /* try to rename */
474   fsRename(sourceP, targetP, replace, rcP);
475   if (*rcP == 0 || *rcP != EXDEV)
476     return;
477
478   /* source and target on different file systems: do copy + erase */
479   {
480     /* first check if we have write permission in the source directory */
481     char dir[1024], *p;
482
483     strcpy(dir, sourceP);
484     p = strrchr(dir, '/');
485     if (p == 0)
486       strcpy(dir, ".");
487     else if (p == dir)
488       strcpy(dir, "/");
489     else
490       *p = 0;
491
492     if (access(dir, W_OK) != 0) {
493       *rcP = errno;
494       return;
495     }
496   }
497
498   *rcP = CopyObject(sourceP, targetP, replace, 1);
499   if (*rcP != 0)
500     return;
501   *rcP = EraseObject(sourceP, replace);
502 }
503
504
505 void
506 fsCopy(char *sourceP, char *targetP, int replace, int *rcP)
507 {
508    *rcP = CopyObject(sourceP, targetP, replace, 0);
509 }
510
511
512 void
513 fsCopyLink(char *sourceP, char *targetP, int replace, int *rcP)
514 {
515   *rcP = CopyObject(sourceP, targetP, replace, 1);
516 }
517
518
519 void
520 fsErase(char *nameP, int *rcP, int force)
521 {
522   *rcP = EraseObject(nameP, force);
523 }
524
525
526 void
527 fsEmpty(char *nameP, int *rcP)
528 {
529   *rcP = EmptyDir(nameP, 0, 0);
530 }
531