fsrtns.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538
  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 libraries 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. #include <stdio.h>
  53. #include <stdlib.h>
  54. #include <unistd.h>
  55. #include <sys/types.h>
  56. #include <sys/stat.h>
  57. #include <errno.h>
  58. #include <fcntl.h>
  59. #include <sys/time.h>
  60. #include <utime.h>
  61. #include <dirent.h>
  62. #include <sys/file.h>
  63. #include <string.h>
  64. #include <limits.h>
  65. #include "fsrtns.h"
  66. /*--------------------------------------------------------------------
  67. * Callback functions
  68. *------------------------------------------------------------------*/
  69. int (*progressCallback)(char *fname) = NULL;
  70. int (*errorCallback)(char *fname, int errnum) = NULL;
  71. int (*periodicCallback)() = NULL;
  72. /*--------------------------------------------------------------------
  73. * Local subroutines
  74. *------------------------------------------------------------------*/
  75. static int CopyObject(char *sourceP, char *targetP, int repl, int link);
  76. static int EraseObject(char *nameP, int force);
  77. static int
  78. CopyFile(char *sourceP, char *targetP, int repl, struct stat *statP)
  79. /* copy a file; if repl non-zero, overwrite any existing file */
  80. {
  81. int src, tgt;
  82. int nread, nwrite;
  83. char buffer[2048];
  84. struct utimbuf ut;
  85. int rc;
  86. /* open source file for read */
  87. src = open(sourceP, O_RDONLY, 0);
  88. if (src < 0)
  89. return errno;
  90. /* create target file */
  91. tgt = open(targetP, O_CREAT | O_EXCL | O_WRONLY, statP->st_mode & 0777);
  92. if (tgt < 0 && errno == EEXIST && repl) {
  93. rc = EraseObject(targetP, repl);
  94. if (rc) {
  95. close(src);
  96. return rc;
  97. }
  98. tgt = open(targetP, O_CREAT | O_EXCL | O_WRONLY, statP->st_mode & 0777);
  99. }
  100. if (tgt < 0) {
  101. rc = errno;
  102. close(src);
  103. return rc;
  104. }
  105. /* if we have root privileges, make sure file ownership is preserved */
  106. if (geteuid() == 0) {
  107. if (statP->st_uid != 0 || statP->st_gid != getegid()) {
  108. rc = fchown(tgt, statP->st_uid, statP->st_gid);
  109. if (rc) {
  110. rc = errno;
  111. close(src);
  112. close(tgt);
  113. return rc;
  114. }
  115. }
  116. }
  117. /* copy data */
  118. for (;;) {
  119. /* read data from source file */
  120. do {
  121. errno = 0;
  122. nread = read(src, buffer, sizeof(buffer));
  123. } while (nread < 0 && errno == EINTR);
  124. if (nread <= 0)
  125. break;
  126. /* write data to target file */
  127. do {
  128. errno = 0;
  129. nwrite = write(tgt, buffer, nread);
  130. } while (nwrite < 0 && errno == EINTR);
  131. if (nwrite != nread)
  132. break;
  133. if (periodicCallback)
  134. if (periodicCallback() != 0) {
  135. unlink(targetP);
  136. close(src);
  137. close(tgt);
  138. return -1;
  139. }
  140. }
  141. /* check if data copy ended abnormally */
  142. rc = (nread == 0)? 0: (errno != 0)? errno: -1;
  143. /* close files */
  144. close(src);
  145. if (rc) {
  146. close(tgt);
  147. return rc;
  148. }
  149. if (close(tgt) != 0)
  150. return errno;
  151. /* set target file attributes */
  152. ut.actime = statP->st_atime;
  153. ut.modtime = statP->st_mtime;
  154. rc = utime(targetP, &ut);
  155. return (rc != 0)? errno: 0;
  156. }
  157. static int
  158. CopyLink(char *sourceP, char *targetP, int repl, struct stat *statP)
  159. /* copy a symbolic link */
  160. {
  161. int l, rc;
  162. char buf[PATH_MAX];
  163. do {
  164. errno = 0;
  165. l = readlink(sourceP, buf, PATH_MAX - 1);
  166. } while (l < 0 && errno == EINTR);
  167. if (l < 0)
  168. return errno;
  169. buf[l] = 0;
  170. if (symlink(buf, targetP) == 0)
  171. return 0;
  172. else if (errno != EEXIST || !repl)
  173. return errno;
  174. if ((rc = EraseObject(targetP, repl)) != 0)
  175. return rc;
  176. if (symlink(buf, targetP) == 0)
  177. return 0;
  178. else
  179. return errno;
  180. }
  181. static int
  182. CopyDir(char *sourceP, char *targetP, int repl, int link, struct stat *statP)
  183. /* copy a directory and recursively all its subdirectories */
  184. {
  185. DIR *dirP; /* open directory */
  186. struct dirent *entryP; /* directory entry */
  187. char srcname[PATH_MAX], tgtname[PATH_MAX];
  188. int srclen, tgtlen;
  189. int rc;
  190. /* open source directory */
  191. dirP = opendir(sourceP);
  192. if (dirP == NULL)
  193. return errno;
  194. /* create target directory */
  195. rc = mkdir(targetP, statP->st_mode & 0777);
  196. if (rc < 0 && errno == EEXIST && repl) {
  197. rc = EraseObject(targetP, repl);
  198. if (rc)
  199. {
  200. closedir(dirP);
  201. return rc;
  202. }
  203. rc = mkdir(targetP, statP->st_mode & 0777);
  204. }
  205. if (rc < 0) {
  206. rc = errno;
  207. closedir(dirP);
  208. return rc;
  209. }
  210. /* if we have root privileges, make sure directory ownership is preserved */
  211. if (geteuid() == 0) {
  212. if (statP->st_uid != 0 || statP->st_gid != getegid()) {
  213. rc = chown(targetP, statP->st_uid, statP->st_gid);
  214. if (rc) {
  215. rc = errno;
  216. closedir(dirP);
  217. return rc;
  218. }
  219. }
  220. }
  221. /* prepare source and target names */
  222. snprintf(srcname, PATH_MAX, "%s", sourceP);
  223. srclen = strlen(srcname);
  224. if (srcname[srclen - 1] != '/')
  225. srcname[srclen++] = '/';
  226. snprintf(tgtname, PATH_MAX, "%s", targetP);
  227. tgtlen = strlen(tgtname);
  228. if (tgtname[tgtlen - 1] != '/')
  229. tgtname[tgtlen++] = '/';
  230. for (rc = 0; rc == 0; ) {
  231. do {
  232. errno = 0;
  233. entryP = readdir(dirP);
  234. } while (entryP == NULL && errno == EINTR);
  235. if (entryP == NULL) {
  236. rc = errno;
  237. break;
  238. }
  239. if (strcmp(entryP->d_name, ".") == 0 || strcmp(entryP->d_name, "..") == 0)
  240. continue;
  241. strcpy(srcname + srclen, entryP->d_name);
  242. strcpy(tgtname + tgtlen, entryP->d_name);
  243. rc = CopyObject(srcname, tgtname, repl, link);
  244. }
  245. closedir(dirP);
  246. return rc;
  247. }
  248. static int
  249. CopyObject(char *sourceP, char *targetP, int repl, int link)
  250. /* copy a directory, file, or symbolic link */
  251. {
  252. struct stat src_stat;
  253. int rc;
  254. if (progressCallback)
  255. if (progressCallback(sourceP) != 0)
  256. return -1;
  257. if (periodicCallback)
  258. if (periodicCallback() != 0)
  259. return -1;
  260. if (lstat(sourceP, &src_stat) < 0)
  261. rc = errno;
  262. else {
  263. copy_switch:
  264. switch(src_stat.st_mode & S_IFMT) {
  265. case S_IFDIR:
  266. rc = CopyDir(sourceP, targetP, repl, link, &src_stat);
  267. break;
  268. case S_IFREG:
  269. rc = CopyFile(sourceP, targetP, repl, &src_stat);
  270. break;
  271. case S_IFLNK:
  272. if (link)
  273. rc = CopyLink(sourceP, targetP, repl, &src_stat);
  274. else if (stat(sourceP, &src_stat) < 0)
  275. rc = errno;
  276. else
  277. goto copy_switch;
  278. break;
  279. default:
  280. rc = EINVAL;
  281. }
  282. }
  283. /*
  284. * Return code zero means everything is ok;
  285. * return code -1 means the operation is aborted.
  286. * In either case, propagated the return code up.
  287. */
  288. if (rc <= 0)
  289. return rc;
  290. /*
  291. * Return code > 0 means an error occurred in the last operation.
  292. * Call the the error callback function. If the callback returns
  293. * zero, we return zero; this will cause the error to be ignored.
  294. * Otherwise we return -1 to signal that the operation is aborted.
  295. */
  296. if (!errorCallback)
  297. return rc;
  298. else if (errorCallback(sourceP, rc) == 0)
  299. return 0;
  300. else
  301. return -1;
  302. }
  303. int
  304. EmptyDir(char *sourceP, int rm, int force)
  305. {
  306. DIR *dirP; /* open directory */
  307. struct dirent *entryP; /* directory entry */
  308. char srcname[1024];
  309. int srclen;
  310. int rc;
  311. /* open source directory */
  312. dirP = opendir(sourceP);
  313. if (dirP == NULL)
  314. return errno;
  315. /* prepare source name */
  316. snprintf(srcname, sizeof(srcname), "%s", sourceP);
  317. srclen = strlen(srcname);
  318. if (srcname[srclen - 1] != '/')
  319. srcname[srclen++] = '/';
  320. rc = 0;
  321. while (rc == 0 && (entryP = readdir(dirP)) != NULL) {
  322. if (strcmp(entryP->d_name, ".") == 0 || strcmp(entryP->d_name, "..") == 0)
  323. continue;
  324. strcpy(srcname + srclen, entryP->d_name);
  325. rc = EraseObject(srcname, force);
  326. }
  327. closedir(dirP);
  328. if (rc == 0 && rm) {
  329. rc = rmdir(sourceP);
  330. if (rc < 0)
  331. rc = errno;
  332. }
  333. return rc;
  334. }
  335. static int
  336. EraseObject(char *nameP, int force)
  337. {
  338. struct stat src_stat;
  339. int rc = 0;
  340. if (periodicCallback)
  341. if (periodicCallback() != 0)
  342. return -1;
  343. if (lstat(nameP, &src_stat) < 0)
  344. rc = errno;
  345. else if ((src_stat.st_mode & S_IFMT) == S_IFDIR) {
  346. if (access(nameP, X_OK|W_OK))
  347. return errno;
  348. rc = EmptyDir(nameP, 1, force);
  349. }
  350. else {
  351. if (!force && access(nameP, W_OK))
  352. return errno;
  353. if (unlink(nameP))
  354. rc = errno;
  355. }
  356. /*
  357. * Return code zero means everything is ok;
  358. * return code -1 means the operation is aborted.
  359. * In either case, propagated the return code up.
  360. */
  361. if (rc <= 0)
  362. return rc;
  363. /*
  364. * Return code > 0 means an error occurred in the last operation.
  365. * Call the the error callback function. If the callback returns
  366. * zero, we return zero; this will cause the error to be ignored.
  367. * Otherwise we return -1 to signal that the operation is aborted.
  368. */
  369. if (!errorCallback)
  370. return rc;
  371. else if (errorCallback(nameP, rc) == 0)
  372. return 0;
  373. else
  374. return -1;
  375. }
  376. /*--------------------------------------------------------------------
  377. * Exported routines
  378. *------------------------------------------------------------------*/
  379. void
  380. fsRename(char *sourceP, char *targetP, int replace, int *rcP)
  381. {
  382. int rc;
  383. struct stat buf;
  384. if (!replace) {
  385. /* return error if target file already exists */
  386. rc = lstat(targetP, &buf);
  387. if (rc == 0) {
  388. *rcP = EEXIST;
  389. return;
  390. } else if (errno != ENOENT) {
  391. *rcP = errno;
  392. return;
  393. }
  394. }
  395. *rcP = rename(sourceP, targetP);
  396. if (*rcP < 0)
  397. *rcP = errno;
  398. if (replace && *rcP == ENOTDIR || *rcP == EISDIR) {
  399. /* error reason: tried to replace file by directory or vice versa */
  400. *rcP = EraseObject(targetP, replace);
  401. if (*rcP < 0)
  402. return;
  403. *rcP = rename(sourceP, targetP);
  404. if (*rcP < 0)
  405. *rcP = errno;
  406. }
  407. }
  408. void
  409. fsMove(char *sourceP, char *targetP, int replace, int *rcP)
  410. {
  411. /* try to rename */
  412. fsRename(sourceP, targetP, replace, rcP);
  413. if (*rcP == 0 || *rcP != EXDEV)
  414. return;
  415. /* source and target on different file systems: do copy + erase */
  416. {
  417. /* first check if we have write permission in the source directory */
  418. char dir[1024], *p;
  419. snprintf(dir, sizeof(dir), "%s", sourceP);
  420. p = strrchr(dir, '/');
  421. if (p == 0)
  422. strcpy(dir, ".");
  423. else if (p == dir)
  424. strcpy(dir, "/");
  425. else
  426. *p = 0;
  427. if (access(dir, W_OK) != 0) {
  428. *rcP = errno;
  429. return;
  430. }
  431. }
  432. *rcP = CopyObject(sourceP, targetP, replace, 1);
  433. if (*rcP != 0)
  434. return;
  435. *rcP = EraseObject(sourceP, replace);
  436. }
  437. void
  438. fsCopy(char *sourceP, char *targetP, int replace, int *rcP)
  439. {
  440. *rcP = CopyObject(sourceP, targetP, replace, 0);
  441. }
  442. void
  443. fsCopyLink(char *sourceP, char *targetP, int replace, int *rcP)
  444. {
  445. *rcP = CopyObject(sourceP, targetP, replace, 1);
  446. }
  447. void
  448. fsErase(char *nameP, int *rcP, int force)
  449. {
  450. *rcP = EraseObject(nameP, force);
  451. }
  452. void
  453. fsEmpty(char *nameP, int *rcP)
  454. {
  455. *rcP = EmptyDir(nameP, 0, 0);
  456. }