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