]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/findlib/find_one.c
dcb0381b90a86d6560b8f6803f71c41d14aad86d
[bacula/bacula] / bacula / src / findlib / find_one.c
1 /* 
2
3    Copyright 1988, 92,93,94,95,96,97, 1999 Free Software Foundation, Inc.
4    Written by John Gilmore, starting 1985-08-25.
5
6    This program is free software; you can redistribute it and/or modify it
7    under the terms of the GNU General Public License as published by the
8    Free Software Foundation; either version 2, or (at your option) any later
9    version.
10
11    This program is distributed in the hope that it will be useful, but
12    WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
14    Public License for more details.
15
16    You should have received a copy of the GNU General Public License along
17    with this program; if not, write to the Free Software Foundation, Inc.,
18    59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.    
19
20    Massively changed from GNU TAR source code to adapt to Bacula
21    by Kern Sibbald, MM
22
23  */
24
25 #include "bacula.h"
26 #include "find.h"
27 #include "system.h"
28
29
30 extern size_t name_max;               /* filename max length */
31 extern size_t path_max;               /* path name max length */
32
33 #ifndef HAVE_READDIR_R
34 int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result);
35 #endif
36
37
38 /* ****FIXME**** debug until stable */
39 #undef bmalloc
40 #define bmalloc(x) sm_malloc(__FILE__, __LINE__, x)
41
42
43 /*
44  * Structure for keeping track of hard linked files
45  */
46 struct f_link {
47     struct f_link *next;
48     dev_t dev;
49     ino_t ino;
50     short linkcount;
51     char name[1];
52 };
53
54
55 #if HAVE_UTIME_H
56 # include <utime.h>
57 #else
58 struct utimbuf
59   {
60     long actime;
61     long modtime;
62   };
63 #endif
64
65
66 /*
67  * Find a single file.                        
68  * handle_file is the callback for handling the file.
69  * p is the filename
70  * parent_device is the device we are currently on 
71  * top_level is 1 when not recursing.
72  */
73 int
74 find_one_file(FF_PKT *ff_pkt, int handle_file(FF_PKT *ff, void *hpkt), void *pkt, 
75                             char *p, dev_t parent_device, int top_level)
76 {
77   struct utimbuf restore_times;
78   int rtn_stat;
79
80   ff_pkt->fname = ff_pkt->link = p;
81   if (ff_pkt->compute_MD5) {
82      ff_pkt->flags |= FO_MD5;
83   }
84   if (ff_pkt->GZIP_compression) {
85      ff_pkt->flags |= FO_GZIP;
86   }
87
88   /* Use stat if following (rather than dumping) 4.2BSD's symbolic links.
89      Otherwise, use lstat (which falls back to stat if no symbolic links).  */
90
91   if (ff_pkt->dereference != 0
92 #if STX_HIDDEN && !_LARGE_FILES /* AIX */
93       ? statx(p, &ff_pkt->statp, STATSIZE, STX_HIDDEN)
94       : statx(p, &ff_pkt->statp, STATSIZE, STX_HIDDEN | STX_LINK)
95 #else
96       ? stat(p, &ff_pkt->statp) : lstat(p, &ff_pkt->statp)
97 #endif
98       )
99     {
100       /* Cannot stat file */
101       ff_pkt->type = FT_NOSTAT;
102       ff_pkt->ff_errno = errno;
103       return handle_file(ff_pkt, pkt);
104     }
105
106 Dmsg1(60, "File ----: %s\n", p);
107 if (S_ISLNK(ff_pkt->statp.st_mode))
108    Dmsg1(60, "Link-------------: %s \n", p);
109
110   /* Save current times of this directory in case we need to
111    * reset them because the user doesn't want them changed.
112    */
113   restore_times.actime = ff_pkt->statp.st_atime;
114   restore_times.modtime = ff_pkt->statp.st_mtime;
115
116
117 #ifdef S_ISHIDDEN
118   if (S_ISHIDDEN(ff_pkt->statp.st_mode)) {
119       char *new = alloca(strlen(p) + 2);
120       if (new) {
121           strcpy (new, p);
122           strcat (new, "@");
123           p = new;
124           ff_pkt->link = p;
125       }
126   }
127 #endif
128
129   /* See if we want only new files, and check if this one is too old to
130      put in the archive.  */
131
132   if (ff_pkt->incremental && !S_ISDIR(ff_pkt->statp.st_mode)) {
133      Dmsg1(100, "Non-directory incremental: %s\n", ff_pkt->fname);
134      /* Not a directory */
135      if (ff_pkt->statp.st_mtime < ff_pkt->save_time
136          && (ff_pkt->mtime_only || 
137              ff_pkt->statp.st_ctime < ff_pkt->save_time)) {
138         /* Incremental option, file not changed */
139         ff_pkt->type = FT_NOCHG;
140         Dmsg1(100, "File not changed: %s\n", ff_pkt->fname);
141         Dmsg4(200, "save_time=%d mtime=%d mtime_only=%d st_ctime=%d\n",
142            ff_pkt->save_time, ff_pkt->statp.st_mtime, 
143            ff_pkt->mtime_only, ff_pkt->statp.st_ctime);
144         return handle_file(ff_pkt, pkt);
145      }
146   }
147
148 #if xxxxxxx
149   /* See if we are trying to dump the archive.  */
150   if (ar_dev && ff_pkt->statp.st_dev == ar_dev && ff_pkt->statp.st_ino == ar_ino) {
151       ff_pkt->type = FT_ISARCH;
152       return handle_file(ff_pkt, pkt);
153   }
154 #endif
155
156   /* Check for multiple links.
157      NOTE: CTG is Masscomp contiguous files  
158
159      We maintain a list of all such files that we've written so far.  Any
160      time we see another, we check the list and avoid dumping the data
161      again if we've done it once already.  */
162
163   if (ff_pkt->statp.st_nlink > 1
164       && (S_ISREG(ff_pkt->statp.st_mode)
165           || S_ISCTG(ff_pkt->statp.st_mode)
166           || S_ISCHR(ff_pkt->statp.st_mode)
167           || S_ISBLK(ff_pkt->statp.st_mode)
168           || S_ISFIFO(ff_pkt->statp.st_mode)
169           || S_ISSOCK(ff_pkt->statp.st_mode))) {
170
171       struct f_link *lp;
172
173       /* FIXME: First quick and dirty.  Hashing, etc later.  */
174       for (lp = ff_pkt->linklist; lp; lp = lp->next)
175          if (lp->ino == ff_pkt->statp.st_ino && lp->dev == ff_pkt->statp.st_dev) {
176              ff_pkt->link = lp->name;
177              ff_pkt->type = FT_LNKSAVED;
178              return handle_file(ff_pkt, pkt);
179          }
180
181       /* Not found.  Add it to the list of possible links.  */
182
183       lp = (struct f_link *)bmalloc(sizeof (struct f_link) + strlen(p));
184       lp->ino = ff_pkt->statp.st_ino;
185       lp->dev = ff_pkt->statp.st_dev;
186       strcpy (lp->name, p);
187       lp->next = ff_pkt->linklist;
188       ff_pkt->linklist = lp;
189   }
190
191   /* This is not a link to a previously dumped file, so dump it.  */
192   if (S_ISREG(ff_pkt->statp.st_mode) || S_ISCTG(ff_pkt->statp.st_mode)) {
193       off_t sizeleft;
194       int header_moved;
195
196       header_moved = 0;
197
198       sizeleft = ff_pkt->statp.st_size;
199
200       /* Don't bother opening empty, world readable files.  Also do not open
201          files when archive is meant for /dev/null.  */
202       if (ff_pkt->null_output_device || (sizeleft == 0
203               && MODE_R == (MODE_R & ff_pkt->statp.st_mode))) {
204          ff_pkt->type = FT_REGE;
205       } else {
206          ff_pkt->type = FT_REG;
207       }
208       return handle_file(ff_pkt, pkt);
209
210   } else if (S_ISLNK(ff_pkt->statp.st_mode)) {
211       int size;
212       char *buffer = (char *)alloca(PATH_MAX + 1);
213
214       size = readlink(p, buffer, PATH_MAX + 1);
215       if (size < 0) {
216           /* Could not follow link */                             
217           ff_pkt->type = FT_NOFOLLOW;
218           ff_pkt->ff_errno = errno;
219           return handle_file(ff_pkt, pkt);
220       }
221       buffer[size] = '\0';
222       ff_pkt->link = buffer;
223       ff_pkt->type = FT_LNK;           /* got a real link */
224       return handle_file(ff_pkt, pkt);
225
226   } else if (S_ISDIR(ff_pkt->statp.st_mode)) {
227       DIR *directory;
228       struct dirent *entry, *result;
229       char *namebuf;
230       size_t buflen;
231       size_t len;
232       int status;
233       dev_t our_device = ff_pkt->statp.st_dev;
234
235       if (access(p, R_OK) == -1 && geteuid () != 0) {
236           /* Could not access() directory */
237           ff_pkt->type = FT_NOACCESS;
238           ff_pkt->ff_errno = errno;
239           return handle_file(ff_pkt, pkt);
240       }
241
242       /* Build new prototype name.  Ensure exactly one trailing slash.  */
243       len = strlen(p);
244       buflen = len + NAME_FIELD_SIZE;
245       namebuf = (char *)bmalloc(buflen + 2);
246       strncpy(namebuf, p, buflen);
247       while (len >= 1 && namebuf[len - 1] == '/')
248         len--;
249       namebuf[len++] = '/';
250       namebuf[len] = '\0';
251
252       ff_pkt->link = namebuf;
253       if (ff_pkt->incremental &&
254           (ff_pkt->statp.st_mtime < ff_pkt->save_time &&
255            ff_pkt->statp.st_ctime < ff_pkt->save_time)) {
256          /* Incremental option, directory entry not changed */
257          ff_pkt->type = FT_DIRNOCHG;
258       } else {
259          ff_pkt->type = FT_DIR;
260       }
261       handle_file(ff_pkt, pkt);       /* handle directory entry */
262
263       ff_pkt->link = ff_pkt->fname;     /* reset "link" */
264
265       /* See if we are about to recurse into a directory, and avoid doing
266          so if the user wants that we do not descend into directories.  */
267
268       if (ff_pkt->no_recursion) {
269          free(namebuf);
270          /* No recursion into this directory */
271          ff_pkt->type = FT_NORECURSE;
272          return handle_file(ff_pkt, pkt);
273       }
274
275       /* See if we are crossing from one file system to another, and
276          avoid doing so if the user only wants to dump one file system.  */
277
278       if (ff_pkt->one_file_system && !top_level
279           && parent_device != ff_pkt->statp.st_dev) {
280           free(namebuf);
281           ff_pkt->type = FT_NOFSCHG;
282           return handle_file(ff_pkt, pkt);
283       }
284
285       /* Now output all the files in the directory.  */
286
287       errno = 0;                /* FIXME: errno should be read-only */
288
289       if ((directory = opendir(p)) == NULL) {
290           free(namebuf);
291           ff_pkt->type = FT_NOOPEN;
292           ff_pkt->ff_errno = errno;
293           return handle_file(ff_pkt, pkt);
294       }
295
296       /* FIXME: Should speed this up by cd-ing into the dir.  */
297
298       rtn_stat = 1;
299       entry = (struct dirent *)malloc(sizeof(struct dirent) + name_max + 10);
300       for ( ;; ) {
301           char *p;
302
303           status  = readdir_r(directory, entry, &result);
304           Dmsg3(200, "readdir stat=%d result=%x name=%s\n", status, result,
305              entry->d_name);
306           if (status != 0 || result == NULL) {
307              break;
308           }
309           p = entry->d_name;
310           /* Skip `.', `..', and excluded file names.  */
311           if (p[0] == '\0' || (p[0] == '.' && (p[1] == '\0' ||
312               (p[1] == '.' && p[2] == '\0')))) {
313              continue;
314           }
315
316           if ((int)NAMLEN(entry) + len >= buflen) {
317               buflen = len + NAMLEN(entry);
318               namebuf = (char *)brealloc(namebuf, buflen + 2);
319           }
320           strcpy(namebuf + len, entry->d_name);
321           if (!file_is_excluded(ff_pkt, namebuf)) {
322              rtn_stat = find_one_file(ff_pkt, handle_file, pkt, namebuf, our_device, 0);
323           }
324       }
325       closedir(directory);
326       free(namebuf);
327       free(entry);
328       if (ff_pkt->atime_preserve) {
329          utime(p, &restore_times);
330       }
331       return rtn_stat;
332   } /* end check for directory */
333
334   /* The only remaining types are special (character, ...) files */
335   ff_pkt->type = FT_SPEC;
336   return handle_file(ff_pkt, pkt);
337 }
338
339 void term_find_one(FF_PKT *ff)
340 {
341   struct f_link *lp, *lc;
342   
343   /* Free up list of hard linked files */
344   for (lp = ff->linklist; lp;) {
345      lc = lp;
346      lp = lp->next;
347      if (lc)
348         free(lc);
349   }
350   return;
351 }