Some fixes and such
[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 #ifdef BB_MV
192 /*
193  * Build a path name from the specified directory name and file name.
194  * If the directory name is NULL, then the original fileName is returned.
195  * The built path is in a static area, and is overwritten for each call.
196  */
197 char *buildName(const char *dirName, const char *fileName)
198 {
199     const char *cp;
200     static char buf[PATH_LEN];
201
202     if ((dirName == NULL) || (*dirName == '\0')) {
203         strcpy(buf, fileName);
204         return buf;
205     }
206
207     cp = strrchr(fileName, '/');
208
209     if (cp)
210         fileName = cp + 1;
211
212     strcpy(buf, dirName);
213     strcat(buf, "/");
214
215     return buf;
216 }
217 #endif
218
219
220
221 /*
222  * Return the standard ls-like mode string from a file mode.
223  * This is static and so is overwritten on each call.
224  */
225 const char *modeString(int mode)
226 {
227     static char buf[12];
228
229     strcpy(buf, "----------");
230
231     /*
232      * Fill in the file type.
233      */
234     if (S_ISDIR(mode))
235         buf[0] = 'd';
236     if (S_ISCHR(mode))
237         buf[0] = 'c';
238     if (S_ISBLK(mode))
239         buf[0] = 'b';
240     if (S_ISFIFO(mode))
241         buf[0] = 'p';
242 #ifdef  S_ISLNK
243     if (S_ISLNK(mode))
244         buf[0] = 'l';
245 #endif
246 #ifdef  S_ISSOCK
247     if (S_ISSOCK(mode))
248         buf[0] = 's';
249 #endif
250
251     /*
252      * Now fill in the normal file permissions.
253      */
254     if (mode & S_IRUSR)
255         buf[1] = 'r';
256     if (mode & S_IWUSR)
257         buf[2] = 'w';
258     if (mode & S_IXUSR)
259         buf[3] = 'x';
260     if (mode & S_IRGRP)
261         buf[4] = 'r';
262     if (mode & S_IWGRP)
263         buf[5] = 'w';
264     if (mode & S_IXGRP)
265         buf[6] = 'x';
266     if (mode & S_IROTH)
267         buf[7] = 'r';
268     if (mode & S_IWOTH)
269         buf[8] = 'w';
270     if (mode & S_IXOTH)
271         buf[9] = 'x';
272
273     /*
274      * Finally fill in magic stuff like suid and sticky text.
275      */
276     if (mode & S_ISUID)
277         buf[3] = ((mode & S_IXUSR) ? 's' : 'S');
278     if (mode & S_ISGID)
279         buf[6] = ((mode & S_IXGRP) ? 's' : 'S');
280     if (mode & S_ISVTX)
281         buf[9] = ((mode & S_IXOTH) ? 't' : 'T');
282
283     return buf;
284 }
285
286
287 #ifdef BB_TAR
288 /*
289  * Get the time string to be used for a file.
290  * This is down to the minute for new files, but only the date for old files.
291  * The string is returned from a static buffer, and so is overwritten for
292  * each call.
293  */
294 const char *timeString(time_t timeVal)
295 {
296     time_t now;
297     char *str;
298     static char buf[26];
299
300     time(&now);
301
302     str = ctime(&timeVal);
303
304     strcpy(buf, &str[4]);
305     buf[12] = '\0';
306
307     if ((timeVal > now) || (timeVal < now - 365 * 24 * 60 * 60L)) {
308         strcpy(&buf[7], &str[20]);
309         buf[11] = '\0';
310     }
311
312     return buf;
313 }
314
315
316 /*
317  * Routine to see if a text string is matched by a wildcard pattern.
318  * Returns TRUE if the text is matched, or FALSE if it is not matched
319  * or if the pattern is invalid.
320  *  *           matches zero or more characters
321  *  ?           matches a single character
322  *  [abc]       matches 'a', 'b' or 'c'
323  *  \c          quotes character c
324  *  Adapted from code written by Ingo Wilken.
325  */
326 int match(const char *text, const char *pattern)
327 {
328     const char *retryPat;
329     const char *retryText;
330     int ch;
331     int found;
332
333     retryPat = NULL;
334     retryText = NULL;
335
336     while (*text || *pattern) {
337         ch = *pattern++;
338
339         switch (ch) {
340         case '*':
341             retryPat = pattern;
342             retryText = text;
343             break;
344
345         case '[':
346             found = FALSE;
347
348             while ((ch = *pattern++) != ']') {
349                 if (ch == '\\')
350                     ch = *pattern++;
351
352                 if (ch == '\0')
353                     return FALSE;
354
355                 if (*text == ch)
356                     found = TRUE;
357             }
358
359             if (!found) {
360                 pattern = retryPat;
361                 text = ++retryText;
362             }
363
364             /* fall into next case */
365
366         case '?':
367             if (*text++ == '\0')
368                 return FALSE;
369
370             break;
371
372         case '\\':
373             ch = *pattern++;
374
375             if (ch == '\0')
376                 return FALSE;
377
378             /* fall into next case */
379
380         default:
381             if (*text == ch) {
382                 if (*text)
383                     text++;
384                 break;
385             }
386
387             if (*text) {
388                 pattern = retryPat;
389                 text = ++retryText;
390                 break;
391             }
392
393             return FALSE;
394         }
395
396         if (pattern == NULL)
397             return FALSE;
398     }
399
400     return TRUE;
401 }
402
403
404 /*
405  * Write all of the supplied buffer out to a file.
406  * This does multiple writes as necessary.
407  * Returns the amount written, or -1 on an error.
408  */
409 int fullWrite(int fd, const char *buf, int len)
410 {
411     int cc;
412     int total;
413
414     total = 0;
415
416     while (len > 0) {
417         cc = write(fd, buf, len);
418
419         if (cc < 0)
420             return -1;
421
422         buf += cc;
423         total += cc;
424         len -= cc;
425     }
426
427     return total;
428 }
429
430
431 /*
432  * Read all of the supplied buffer from a file.
433  * This does multiple reads as necessary.
434  * Returns the amount read, or -1 on an error.
435  * A short read is returned on an end of file.
436  */
437 int fullRead(int fd, char *buf, int len)
438 {
439     int cc;
440     int total;
441
442     total = 0;
443
444     while (len > 0) {
445         cc = read(fd, buf, len);
446
447         if (cc < 0)
448             return -1;
449
450         if (cc == 0)
451             break;
452
453         buf += cc;
454         total += cc;
455         len -= cc;
456     }
457
458     return total;
459 }
460 #endif
461
462
463 #if defined (BB_CHOWN) || defined (BB_CP) || defined (BB_FIND) || defined (BB_LS)
464 /*
465  * Walk down all the directories under the specified 
466  * location, and do something (something specified
467  * by the fileAction and dirAction function pointers).
468  */
469 int
470 recursiveAction(const char *fileName, int recurse, int followLinks,
471                 int (*fileAction) (const char *fileName, struct stat* statbuf),
472                 int (*dirAction) (const char *fileName, struct stat* statbuf))
473 {
474     int status;
475     struct stat statbuf;
476     struct dirent *next;
477
478     if (followLinks == FALSE)
479         status = stat(fileName, &statbuf);
480     else
481         status = lstat(fileName, &statbuf);
482
483     if (status < 0) {
484         perror(fileName);
485         return (FALSE);
486     }
487
488     if (recurse == FALSE) {
489         if (S_ISDIR(statbuf.st_mode)) {
490             if (dirAction != NULL)
491                 return (dirAction(fileName, &statbuf));
492             else
493                 return (TRUE);
494         } 
495     }
496
497     if (S_ISDIR(statbuf.st_mode)) {
498         DIR *dir;
499         dir = opendir(fileName);
500         if (!dir) {
501             perror(fileName);
502             return (FALSE);
503         }
504         if (dirAction != NULL) {
505             status = dirAction(fileName, &statbuf);
506             if (status == FALSE) {
507                 perror(fileName);
508                 return (FALSE);
509             }
510         }
511         while ((next = readdir(dir)) != NULL) {
512             char nextFile[NAME_MAX];
513             if ((strcmp(next->d_name, "..") == 0)
514                 || (strcmp(next->d_name, ".") == 0)) {
515                 continue;
516             }
517             sprintf(nextFile, "%s/%s", fileName, next->d_name);
518             status =
519                 recursiveAction(nextFile, TRUE, followLinks, fileAction,
520                                 dirAction);
521             if (status < 0) {
522                 closedir(dir);
523                 return (FALSE);
524             }
525         }
526         status = closedir(dir);
527         if (status < 0) {
528             perror(fileName);
529             return (FALSE);
530         }
531     } else {
532         if (fileAction == NULL)
533             return (TRUE);
534         else
535             return (fileAction(fileName, &statbuf));
536     }
537     return (TRUE);
538 }
539
540 #endif
541
542
543
544 #if defined (BB_TAR) || defined (BB_MKDIR)
545 /*
546  * Attempt to create the directories along the specified path, except for
547  * the final component.  The mode is given for the final directory only,
548  * while all previous ones get default protections.  Errors are not reported
549  * here, as failures to restore files can be reported later.
550  */
551 extern void createPath (const char *name, int mode)
552 {
553     char *cp;
554     char *cpOld;
555     char buf[NAME_MAX];
556
557     strcpy (buf, name);
558
559     cp = strchr (buf, '/');
560
561     while (cp) {
562         cpOld = cp;
563         cp = strchr (cp + 1, '/');
564
565         *cpOld = '\0';
566
567         if (mkdir (buf, cp ? 0777 : mode) == 0)
568             printf ("Directory \"%s\" created\n", buf);
569
570         *cpOld = '/';
571     }
572 }
573 #endif
574
575
576
577 #if defined (BB_CHMOD_CHOWN_CHGRP) || defined (BB_MKDIR)
578 /* [ugoa]{+|-|=}[rwxstl] */
579 extern int parse_mode( const char* s, mode_t* theMode)
580 {
581         mode_t or;
582         mode_t and;
583         mode_t  mode = 0;
584         mode_t  groups = S_ISVTX;
585         char    type;
586         char    c;
587
588         do {
589                 for ( ; ; ) {
590                         switch ( c = *s++ ) {
591                         case '\0':
592                                 return (FALSE);
593                         case 'u':
594                                 groups |= S_ISUID|S_IRWXU;
595                                 continue;
596                         case 'g':
597                                 groups |= S_ISGID|S_IRWXG;
598                                 continue;
599                         case 'o':
600                                 groups |= S_IRWXO;
601                                 continue;
602                         case 'a':
603                                 groups |= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO;
604                                 continue;
605                         case '+':
606                         case '=':
607                         case '-':
608                                 type = c;
609                                 if ( groups == S_ISVTX ) /* The default is "all" */
610                                         groups |= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO;
611                                 break;
612                         default:
613                                 if ( c >= '0' && c <= '7' && mode == 0 && groups == S_ISVTX ) {
614                                         and = 0;
615                                         or = strtol(--s, 0, 010);
616                                         return (TRUE);
617                                 }
618                                 else
619                                         return (FALSE);
620                         }
621                         break;
622                 }
623
624                 while ( (c = *s++) != '\0' ) {
625                         switch ( c ) {
626                         case ',':
627                                 break;
628                         case 'r':
629                                 mode |= S_IRUSR|S_IRGRP|S_IROTH;
630                                 continue;
631                         case 'w':
632                                 mode |= S_IWUSR|S_IWGRP|S_IWOTH;
633                                 continue;
634                         case 'x':
635                                 mode |= S_IXUSR|S_IXGRP|S_IXOTH;
636                                 continue;
637                         case 's':
638                                 mode |= S_IXGRP|S_ISUID|S_ISGID;
639                                 continue;
640                         case 't':
641                                 mode |= S_ISVTX;
642                                 continue;
643                         default:
644                                 return (FALSE);
645                         }
646                         break;
647                 }
648                 switch ( type ) {
649                 case '=':
650                         and &= ~(groups);
651                         /* fall through */
652                 case '+':
653                         or |= mode & groups;
654                         break;
655                 case '-':
656                         and &= ~(mode & groups);
657                         or &= and;
658                         break;
659                 }
660         } while ( c == ',' );
661         return (TRUE);
662 }
663 #endif
664
665 /* END CODE */