c6121cb11322ba3a9b42a04add578b7656cb303a
[oweals/busybox.git] / utility.c
1 /*
2  * Utility routines.
3  *
4  * Copyright (C) 1998 by Erik Andersen <andersee@debian.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19  *
20  * Based in part on code from sash, Copyright (c) 1999 by David I. Bell 
21  * Permission has been granted to redistribute this code under the GPL.
22  *
23  */
24
25 #include "internal.h"
26 #include <stdio.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <dirent.h>
31 #include <time.h>
32 #include <utime.h>
33 #include <sys/stat.h>
34 #include <unistd.h>
35
36
37
38 #if defined (BB_CP) || defined (BB_MV)
39 /*
40  * Return TRUE if a fileName is a directory.
41  * Nonexistant files return FALSE.
42  */
43 int isDirectory(const char *name)
44 {
45     struct stat statBuf;
46
47     if (stat(name, &statBuf) < 0)
48         return FALSE;
49     if (S_ISDIR(statBuf.st_mode))
50         return TRUE;
51     return(FALSE);
52 }
53
54
55 /*
56  * Copy one file to another, while possibly preserving its modes, times,
57  * and modes.  Returns TRUE if successful, or FALSE on a failure with an
58  * error message output.  (Failure is not indicted if the attributes cannot
59  * be set.)
60  */
61 int
62 copyFile( const char *srcName, const char *destName, 
63          int setModes, int followLinks)
64 {
65     int rfd;
66     int wfd;
67     int rcc;
68     int result;
69     char buf[BUF_SIZE];
70     struct stat srcStatBuf;
71     struct stat dstStatBuf;
72     struct utimbuf times;
73
74     if (followLinks == FALSE)
75         result = stat(srcName, &srcStatBuf);
76     else 
77         result = lstat(srcName, &srcStatBuf);
78     if (result < 0) {
79         perror(srcName);
80         return FALSE;
81     }
82
83     if (followLinks == FALSE)
84         result = stat(destName, &dstStatBuf);
85     else 
86         result = lstat(destName, &dstStatBuf);
87     if (result < 0) {
88         dstStatBuf.st_ino = -1;
89         dstStatBuf.st_dev = -1;
90     }
91
92     if ((srcStatBuf.st_dev == dstStatBuf.st_dev) &&
93         (srcStatBuf.st_ino == dstStatBuf.st_ino)) {
94         fprintf(stderr, "Copying file \"%s\" to itself\n", srcName);
95         return FALSE;
96     }
97
98     if (S_ISDIR(srcStatBuf.st_mode)) {
99         //fprintf(stderr, "copying directory %s to %s\n", srcName, destName);
100         /* Make sure the directory is writable */
101         if (mkdir(destName, 0777777 ^ umask(0))) {
102             perror(destName);
103             return (FALSE);
104         }
105     } else if (S_ISLNK(srcStatBuf.st_mode)) {
106         char *link_val;
107         int link_size;
108
109         //fprintf(stderr, "copying link %s to %s\n", srcName, destName);
110         link_val = (char *) alloca(PATH_MAX + 2);
111         link_size = readlink(srcName, link_val, PATH_MAX + 1);
112         if (link_size < 0) {
113             perror(srcName);
114             return (FALSE);
115         }
116         link_val[link_size] = '\0';
117         link_size = symlink(link_val, destName);
118         if (link_size != 0) {
119             perror(destName);
120             return (FALSE);
121         }
122     } else if (S_ISFIFO(srcStatBuf.st_mode)) {
123         //fprintf(stderr, "copying fifo %s to %s\n", srcName, destName);
124         if (mkfifo(destName, 644)) {
125             perror(destName);
126             return (FALSE);
127         }
128     } else if (S_ISBLK(srcStatBuf.st_mode) || S_ISCHR(srcStatBuf.st_mode) 
129             || S_ISSOCK (srcStatBuf.st_mode)) {
130         //fprintf(stderr, "copying soc, blk, or chr %s to %s\n", srcName, destName);
131         if (mknod(destName, srcStatBuf.st_mode, srcStatBuf.st_rdev)) {
132             perror(destName);
133             return (FALSE);
134         }
135     } else if (S_ISREG(srcStatBuf.st_mode)) {
136         //fprintf(stderr, "copying regular file %s to %s\n", srcName, destName);
137         rfd = open(srcName, O_RDONLY);
138         if (rfd < 0) {
139             perror(srcName);
140             return FALSE;
141         }
142
143         wfd = creat(destName, srcStatBuf.st_mode);
144         if (wfd < 0) {
145             perror(destName);
146             close(rfd);
147             return FALSE;
148         }
149
150         while ((rcc = read(rfd, buf, sizeof(buf))) > 0) {
151             if (fullWrite(wfd, buf, rcc) < 0)
152                 goto error_exit;
153         }
154         if (rcc < 0) {
155             goto error_exit;
156         }
157
158         close(rfd);
159         if (close(wfd) < 0) {
160             return FALSE;
161         }
162     }
163
164     if (setModes == TRUE) {
165         //fprintf(stderr, "Setting permissions for %s\n", destName);
166         chmod(destName, srcStatBuf.st_mode);
167         if (followLinks == TRUE)
168             chown(destName, srcStatBuf.st_uid, srcStatBuf.st_gid);
169         else
170             lchown(destName, srcStatBuf.st_uid, srcStatBuf.st_gid);
171
172         times.actime = srcStatBuf.st_atime;
173         times.modtime = srcStatBuf.st_mtime;
174
175         utime(destName, &times);
176     }
177
178     return TRUE;
179
180
181   error_exit:
182     perror(destName);
183     close(rfd);
184     close(wfd);
185
186     return FALSE;
187 }
188 #endif
189
190
191
192 #ifdef BB_TAR
193 /*
194  * Return the standard ls-like mode string from a file mode.
195  * This is static and so is overwritten on each call.
196  */
197 const char *modeString(int mode)
198 {
199     static char buf[12];
200
201     strcpy(buf, "----------");
202
203     /*
204      * Fill in the file type.
205      */
206     if (S_ISDIR(mode))
207         buf[0] = 'd';
208     if (S_ISCHR(mode))
209         buf[0] = 'c';
210     if (S_ISBLK(mode))
211         buf[0] = 'b';
212     if (S_ISFIFO(mode))
213         buf[0] = 'p';
214     if (S_ISLNK(mode))
215         buf[0] = 'l';
216     if (S_ISSOCK(mode))
217         buf[0] = 's';
218     /*
219      * Now fill in the normal file permissions.
220      */
221     if (mode & S_IRUSR)
222         buf[1] = 'r';
223     if (mode & S_IWUSR)
224         buf[2] = 'w';
225     if (mode & S_IXUSR)
226         buf[3] = 'x';
227     if (mode & S_IRGRP)
228         buf[4] = 'r';
229     if (mode & S_IWGRP)
230         buf[5] = 'w';
231     if (mode & S_IXGRP)
232         buf[6] = 'x';
233     if (mode & S_IROTH)
234         buf[7] = 'r';
235     if (mode & S_IWOTH)
236         buf[8] = 'w';
237     if (mode & S_IXOTH)
238         buf[9] = 'x';
239
240     /*
241      * Finally fill in magic stuff like suid and sticky text.
242      */
243     if (mode & S_ISUID)
244         buf[3] = ((mode & S_IXUSR) ? 's' : 'S');
245     if (mode & S_ISGID)
246         buf[6] = ((mode & S_IXGRP) ? 's' : 'S');
247     if (mode & S_ISVTX)
248         buf[9] = ((mode & S_IXOTH) ? 't' : 'T');
249
250     return buf;
251 }
252
253
254 /*
255  * Get the time string to be used for a file.
256  * This is down to the minute for new files, but only the date for old files.
257  * The string is returned from a static buffer, and so is overwritten for
258  * each call.
259  */
260 const char *timeString(time_t timeVal)
261 {
262     time_t now;
263     char *str;
264     static char buf[26];
265
266     time(&now);
267
268     str = ctime(&timeVal);
269
270     strcpy(buf, &str[4]);
271     buf[12] = '\0';
272
273     if ((timeVal > now) || (timeVal < now - 365 * 24 * 60 * 60L)) {
274         strcpy(&buf[7], &str[20]);
275         buf[11] = '\0';
276     }
277
278     return buf;
279 }
280
281
282 /*
283  * Routine to see if a text string is matched by a wildcard pattern.
284  * Returns TRUE if the text is matched, or FALSE if it is not matched
285  * or if the pattern is invalid.
286  *  *           matches zero or more characters
287  *  ?           matches a single character
288  *  [abc]       matches 'a', 'b' or 'c'
289  *  \c          quotes character c
290  *  Adapted from code written by Ingo Wilken.
291  */
292 int match(const char *text, const char *pattern)
293 {
294     const char *retryPat;
295     const char *retryText;
296     int ch;
297     int found;
298
299     retryPat = NULL;
300     retryText = NULL;
301
302     while (*text || *pattern) {
303         ch = *pattern++;
304
305         switch (ch) {
306         case '*':
307             retryPat = pattern;
308             retryText = text;
309             break;
310
311         case '[':
312             found = FALSE;
313
314             while ((ch = *pattern++) != ']') {
315                 if (ch == '\\')
316                     ch = *pattern++;
317
318                 if (ch == '\0')
319                     return FALSE;
320
321                 if (*text == ch)
322                     found = TRUE;
323             }
324
325             if (!found) {
326                 pattern = retryPat;
327                 text = ++retryText;
328             }
329
330             /* fall into next case */
331
332         case '?':
333             if (*text++ == '\0')
334                 return FALSE;
335
336             break;
337
338         case '\\':
339             ch = *pattern++;
340
341             if (ch == '\0')
342                 return FALSE;
343
344             /* fall into next case */
345
346         default:
347             if (*text == ch) {
348                 if (*text)
349                     text++;
350                 break;
351             }
352
353             if (*text) {
354                 pattern = retryPat;
355                 text = ++retryText;
356                 break;
357             }
358
359             return FALSE;
360         }
361
362         if (pattern == NULL)
363             return FALSE;
364     }
365
366     return TRUE;
367 }
368
369
370 /*
371  * Write all of the supplied buffer out to a file.
372  * This does multiple writes as necessary.
373  * Returns the amount written, or -1 on an error.
374  */
375 int fullWrite(int fd, const char *buf, int len)
376 {
377     int cc;
378     int total;
379
380     total = 0;
381
382     while (len > 0) {
383         cc = write(fd, buf, len);
384
385         if (cc < 0)
386             return -1;
387
388         buf += cc;
389         total += cc;
390         len -= cc;
391     }
392
393     return total;
394 }
395
396
397 /*
398  * Read all of the supplied buffer from a file.
399  * This does multiple reads as necessary.
400  * Returns the amount read, or -1 on an error.
401  * A short read is returned on an end of file.
402  */
403 int fullRead(int fd, char *buf, int len)
404 {
405     int cc;
406     int total;
407
408     total = 0;
409
410     while (len > 0) {
411         cc = read(fd, buf, len);
412
413         if (cc < 0)
414             return -1;
415
416         if (cc == 0)
417             break;
418
419         buf += cc;
420         total += cc;
421         len -= cc;
422     }
423
424     return total;
425 }
426 #endif
427
428
429 #if defined (BB_CHOWN) || defined (BB_CP) || defined (BB_FIND) || defined (BB_LS)
430 /*
431  * Walk down all the directories under the specified 
432  * location, and do something (something specified
433  * by the fileAction and dirAction function pointers).
434  */
435 int
436 recursiveAction(const char *fileName, int recurse, int followLinks, int delayDirAction,
437                 int (*fileAction) (const char *fileName, struct stat* statbuf),
438                 int (*dirAction) (const char *fileName, struct stat* statbuf))
439 {
440     int status;
441     struct stat statbuf;
442     struct dirent *next;
443
444     if (followLinks == FALSE)
445         status = stat(fileName, &statbuf);
446     else
447         status = lstat(fileName, &statbuf);
448
449     if (status < 0) {
450         perror(fileName);
451         return (FALSE);
452     }
453
454     if (recurse == FALSE) {
455         if (S_ISDIR(statbuf.st_mode)) {
456             if (dirAction != NULL)
457                 return (dirAction(fileName, &statbuf));
458             else
459                 return (TRUE);
460         } 
461     }
462
463     if (S_ISDIR(statbuf.st_mode)) {
464         DIR *dir;
465         dir = opendir(fileName);
466         if (!dir) {
467             perror(fileName);
468             return (FALSE);
469         }
470         if (dirAction != NULL && delayDirAction == FALSE) {
471             status = dirAction(fileName, &statbuf);
472             if (status == FALSE) {
473                 perror(fileName);
474                 return (FALSE);
475             }
476         }
477         while ((next = readdir(dir)) != NULL) {
478             char nextFile[NAME_MAX];
479             if ((strcmp(next->d_name, "..") == 0)
480                 || (strcmp(next->d_name, ".") == 0)) {
481                 continue;
482             }
483             sprintf(nextFile, "%s/%s", fileName, next->d_name);
484             status =
485                 recursiveAction(nextFile, TRUE, followLinks, delayDirAction, 
486                         fileAction, dirAction);
487             if (status < 0) {
488                 closedir(dir);
489                 return (FALSE);
490             }
491         }
492         status = closedir(dir);
493         if (status < 0) {
494             perror(fileName);
495             return (FALSE);
496         }
497         if (dirAction != NULL && delayDirAction == TRUE) {
498             status = dirAction(fileName, &statbuf);
499             if (status == FALSE) {
500                 perror(fileName);
501                 return (FALSE);
502             }
503         }
504     } else {
505         if (fileAction == NULL)
506             return (TRUE);
507         else
508             return (fileAction(fileName, &statbuf));
509     }
510     return (TRUE);
511 }
512
513 #endif
514
515
516
517 #if defined (BB_TAR) || defined (BB_MKDIR)
518 /*
519  * Attempt to create the directories along the specified path, except for
520  * the final component.  The mode is given for the final directory only,
521  * while all previous ones get default protections.  Errors are not reported
522  * here, as failures to restore files can be reported later.
523  */
524 extern void createPath (const char *name, int mode)
525 {
526     char *cp;
527     char *cpOld;
528     char buf[NAME_MAX];
529
530     strcpy (buf, name);
531
532     cp = strchr (buf, '/');
533
534     while (cp) {
535         cpOld = cp;
536         cp = strchr (cp + 1, '/');
537
538         *cpOld = '\0';
539
540         if (mkdir (buf, cp ? 0777 : mode) == 0)
541             printf ("Directory \"%s\" created\n", buf);
542
543         *cpOld = '/';
544     }
545 }
546 #endif
547
548
549
550 #if defined (BB_CHMOD_CHOWN_CHGRP) || defined (BB_MKDIR)
551 /* [ugoa]{+|-|=}[rwxstl] */
552 extern int parse_mode( const char* s, mode_t* theMode)
553 {
554         mode_t or;
555         mode_t and;
556         mode_t  mode = 0;
557         mode_t  groups = S_ISVTX;
558         char    type;
559         char    c;
560
561         do {
562                 for ( ; ; ) {
563                         switch ( c = *s++ ) {
564                         case '\0':
565                                 return (FALSE);
566                         case 'u':
567                                 groups |= S_ISUID|S_IRWXU;
568                                 continue;
569                         case 'g':
570                                 groups |= S_ISGID|S_IRWXG;
571                                 continue;
572                         case 'o':
573                                 groups |= S_IRWXO;
574                                 continue;
575                         case 'a':
576                                 groups |= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO;
577                                 continue;
578                         case '+':
579                         case '=':
580                         case '-':
581                                 type = c;
582                                 if ( groups == S_ISVTX ) /* The default is "all" */
583                                         groups |= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO;
584                                 break;
585                         default:
586                                 if ( c >= '0' && c <= '7' && mode == 0 && groups == S_ISVTX ) {
587                                         and = 0;
588                                         or = strtol(--s, 0, 010);
589                                         return (TRUE);
590                                 }
591                                 else
592                                         return (FALSE);
593                         }
594                         break;
595                 }
596
597                 while ( (c = *s++) != '\0' ) {
598                         switch ( c ) {
599                         case ',':
600                                 break;
601                         case 'r':
602                                 mode |= S_IRUSR|S_IRGRP|S_IROTH;
603                                 continue;
604                         case 'w':
605                                 mode |= S_IWUSR|S_IWGRP|S_IWOTH;
606                                 continue;
607                         case 'x':
608                                 mode |= S_IXUSR|S_IXGRP|S_IXOTH;
609                                 continue;
610                         case 's':
611                                 mode |= S_IXGRP|S_ISUID|S_ISGID;
612                                 continue;
613                         case 't':
614                                 mode |= S_ISVTX;
615                                 continue;
616                         default:
617                                 return (FALSE);
618                         }
619                         break;
620                 }
621                 switch ( type ) {
622                 case '=':
623                         and &= ~(groups);
624                         /* fall through */
625                 case '+':
626                         or |= mode & groups;
627                         break;
628                 case '-':
629                         and &= ~(mode & groups);
630                         or &= and;
631                         break;
632                 }
633         } while ( c == ',' );
634         return (TRUE);
635 }
636 #endif
637
638 /* END CODE */