]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/findlib/find_one.c
Fix smartall to be thread safe
[bacula/bacula] / bacula / src / findlib / find_one.c
1 /* 
2    Copyright (C) 2000-2003 Kern Sibbald and John Walker
3
4    This program is free software; you can redistribute it and/or
5    modify it under the terms of the GNU General Public License as
6    published by the Free Software Foundation; either version 2 of
7    the License, or (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12    General Public License for more details.
13
14    You should have received a copy of the GNU General Public
15    License along with this program; if not, write to the Free
16    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
17    MA 02111-1307, USA.
18
19    This file is based on GNU TAR source code. Except for a few key
20    ideas and some key snippets of code, it has been rewritten for Bacula.
21
22       Kern Sibbald, MM
23
24    Many thanks to the TAR programmers.
25
26  */
27
28 #include "bacula.h"
29 #include "find.h"
30 /*#include "system.h" */
31
32
33 extern size_t name_max;               /* filename max length */
34 extern size_t path_max;               /* path name max length */
35
36 #ifndef HAVE_READDIR_R
37 int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result);
38 #endif
39
40
41 /*
42  * Structure for keeping track of hard linked files
43  */
44 struct f_link {
45     struct f_link *next;
46     dev_t dev;
47     ino_t ino;
48     short linkcount;
49     char name[1];
50 };
51
52
53 #if HAVE_UTIME_H
54 # include <utime.h>
55 #else
56 struct utimbuf
57   {
58     long actime;
59     long modtime;
60   };
61 #endif
62
63
64 /*
65  * Find a single file.                        
66  * handle_file is the callback for handling the file.
67  * p is the filename
68  * parent_device is the device we are currently on 
69  * top_level is 1 when not recursing or 0 when 
70  *  decending into a directory.
71  */
72 int
73 find_one_file(FF_PKT *ff_pkt, int handle_file(FF_PKT *ff, void *hpkt), void *pkt, 
74                char *fname, dev_t parent_device, int top_level)
75 {
76    struct utimbuf restore_times;
77    int rtn_stat;
78
79    ff_pkt->fname = ff_pkt->link = fname;
80
81    if (lstat(fname, &ff_pkt->statp) != 0) {
82        /* Cannot stat file */
83        ff_pkt->type = FT_NOSTAT;
84        ff_pkt->ff_errno = errno;
85        return handle_file(ff_pkt, pkt);
86    }
87
88    Dmsg1(60, "File ----: %s\n", fname);
89 #ifdef DEBUG
90    if (S_ISLNK(ff_pkt->statp.st_mode))
91       Dmsg1(60, "Link-------------: %s \n", fname);
92 #endif
93
94    /* Save current times of this directory in case we need to
95     * reset them because the user doesn't want them changed.
96     */
97    restore_times.actime = ff_pkt->statp.st_atime;
98    restore_times.modtime = ff_pkt->statp.st_mtime;
99
100
101    /* 
102     * If this is an Incremental backup, see if file was modified
103     * since our last "save_time", presumably the last Full save
104     * or Incremental.
105     */
106    if (ff_pkt->incremental && !S_ISDIR(ff_pkt->statp.st_mode)) {
107       Dmsg1(100, "Non-directory incremental: %s\n", ff_pkt->fname);
108       /* Not a directory */
109       if (ff_pkt->statp.st_mtime < ff_pkt->save_time
110           && (ff_pkt->mtime_only || 
111               ff_pkt->statp.st_ctime < ff_pkt->save_time)) {
112          /* Incremental option, file not changed */
113          ff_pkt->type = FT_NOCHG;
114          Dmsg1(100, "File not changed: %s\n", ff_pkt->fname);
115          Dmsg4(200, "save_time=%d mtime=%d mtime_only=%d st_ctime=%d\n",
116             ff_pkt->save_time, ff_pkt->statp.st_mtime, 
117             ff_pkt->mtime_only, ff_pkt->statp.st_ctime);
118          return handle_file(ff_pkt, pkt);
119       }
120    }
121
122 #if xxxxxxx
123    /* See if we are trying to dump the archive.  */
124    if (ar_dev && ff_pkt->statp.st_dev == ar_dev && ff_pkt->statp.st_ino == ar_ino) {
125        ff_pkt->type = FT_ISARCH;
126        return handle_file(ff_pkt, pkt);
127    }
128 #endif
129
130    /* 
131     * Handle hard linked files
132     *
133     * Maintain a list of hard linked files already backed up. This
134     *  allows us to ensure that the data of each file gets backed 
135     *  up only once.
136     */
137    if (ff_pkt->statp.st_nlink > 1
138        && (S_ISREG(ff_pkt->statp.st_mode)
139            || S_ISCHR(ff_pkt->statp.st_mode)
140            || S_ISBLK(ff_pkt->statp.st_mode)
141            || S_ISFIFO(ff_pkt->statp.st_mode)
142            || S_ISSOCK(ff_pkt->statp.st_mode))) {
143
144        struct f_link *lp;
145
146        /* Search link list of hard linked files */
147        for (lp = ff_pkt->linklist; lp; lp = lp->next)
148           if (lp->ino == ff_pkt->statp.st_ino && lp->dev == ff_pkt->statp.st_dev) {
149               ff_pkt->link = lp->name;
150               ff_pkt->type = FT_LNKSAVED;       /* Handle link, file already saved */
151               return handle_file(ff_pkt, pkt);
152           }
153
154        /* File not previously dumped. Chain it into our list. */
155        lp = (struct f_link *)bmalloc(sizeof(struct f_link) + strlen(fname) +1);
156        lp->ino = ff_pkt->statp.st_ino;
157        lp->dev = ff_pkt->statp.st_dev;
158        strcpy(lp->name, fname);
159        lp->next = ff_pkt->linklist;
160        ff_pkt->linklist = lp;
161    }
162
163    /* This is not a link to a previously dumped file, so dump it.  */
164    if (S_ISREG(ff_pkt->statp.st_mode)) {
165        off_t sizeleft;
166
167        sizeleft = ff_pkt->statp.st_size;
168
169        /* Don't bother opening empty, world readable files.  Also do not open
170           files when archive is meant for /dev/null.  */
171        if (ff_pkt->null_output_device || (sizeleft == 0
172                && MODE_RALL == (MODE_RALL & ff_pkt->statp.st_mode))) {
173           ff_pkt->type = FT_REGE;
174        } else {
175           ff_pkt->type = FT_REG;
176        }
177        return handle_file(ff_pkt, pkt);
178
179    } else if (S_ISLNK(ff_pkt->statp.st_mode)) {
180        int size;
181        char *buffer = (char *)alloca(path_max + name_max + 2);
182
183        size = readlink(fname, buffer, path_max + name_max + 1);
184        if (size < 0) {
185            /* Could not follow link */                             
186            ff_pkt->type = FT_NOFOLLOW;
187            ff_pkt->ff_errno = errno;
188            return handle_file(ff_pkt, pkt);
189        }
190        buffer[size] = 0;
191        ff_pkt->link = buffer;
192        ff_pkt->type = FT_LNK;           /* got a real link */
193        return handle_file(ff_pkt, pkt);
194
195    } else if (S_ISDIR(ff_pkt->statp.st_mode)) {
196        DIR *directory;
197        struct dirent *entry, *result;
198        char *link;
199        int link_len;
200        int len;
201        int status;
202        dev_t our_device = ff_pkt->statp.st_dev;
203
204        if (access(fname, R_OK) == -1 && geteuid() != 0) {
205            /* Could not access() directory */
206            ff_pkt->type = FT_NOACCESS;
207            ff_pkt->ff_errno = errno;
208            return handle_file(ff_pkt, pkt);
209        }
210
211        /* Build a canonical directory name with a trailing slash. */
212        len = strlen(fname);
213        link_len = len + 200;
214        link = (char *)bmalloc(link_len + 2);
215        bstrncpy(link, fname, link_len);
216        /* Strip all trailing slashes */
217        while (len >= 1 && link[len - 1] == '/')
218          len--;
219        link[len++] = '/';             /* add back one */
220        link[len] = 0;
221
222        ff_pkt->link = link;
223        if (ff_pkt->incremental &&
224            (ff_pkt->statp.st_mtime < ff_pkt->save_time &&
225             ff_pkt->statp.st_ctime < ff_pkt->save_time)) {
226           /* Incremental option, directory entry not changed */
227           ff_pkt->type = FT_DIRNOCHG;
228        } else {
229           ff_pkt->type = FT_DIR;
230        }
231        handle_file(ff_pkt, pkt);       /* handle directory entry */
232
233        ff_pkt->link = ff_pkt->fname;     /* reset "link" */
234
235        /* 
236         * Do not decend into subdirectories (recurse) if the
237         * user has turned it off for this directory.
238         */
239        if (ff_pkt->flags & FO_NO_RECURSION) {
240           free(link);
241           /* No recursion into this directory */
242           ff_pkt->type = FT_NORECURSE;
243           return handle_file(ff_pkt, pkt);
244        }
245
246        /* 
247         * See if we are crossing file systems, and
248         * avoid doing so if the user only wants to dump one file system.
249         */
250        if (!top_level && !(ff_pkt->flags & FO_MULTIFS) &&
251             parent_device != ff_pkt->statp.st_dev) {
252           free(link);
253           /* returning here means we do not handle this directory */
254           ff_pkt->type = FT_NOFSCHG;
255           return handle_file(ff_pkt, pkt);
256        }
257        /* 
258         * Now process the files in this directory.
259         */
260        errno = 0;
261        if ((directory = opendir(fname)) == NULL) {
262           free(link);
263           ff_pkt->type = FT_NOOPEN;
264           ff_pkt->ff_errno = errno;
265           return handle_file(ff_pkt, pkt);
266        }
267
268        /*
269         * This would possibly run faster if we chdir to the directory
270         * before traversing it.
271         */
272        rtn_stat = 1;
273        entry = (struct dirent *)malloc(sizeof(struct dirent) + name_max + 100);
274        for ( ;; ) {
275            char *p, *q;
276            int i;
277
278            status  = readdir_r(directory, entry, &result);
279            sm_check(__FILE__, __LINE__, False);
280            Dmsg3(200, "readdir stat=%d result=%x name=%s\n", status, result,
281               entry->d_name);
282            if (status != 0 || result == NULL) {
283               break;
284            }
285            ASSERT(name_max+1 > sizeof(struct dirent) + (int)NAMELEN(entry));
286            p = entry->d_name;
287            /* Skip `.', `..', and excluded file names.  */
288            if (p[0] == '\0' || (p[0] == '.' && (p[1] == '\0' ||
289                (p[1] == '.' && p[2] == '\0')))) {
290               continue;
291            }
292
293            if ((int)NAMELEN(entry) + len >= link_len) {
294                link_len = len + NAMELEN(entry) + 1;
295                link = (char *)brealloc(link, link_len + 1);
296            }
297            q = link + len;
298            for (i=0; i < (int)NAMELEN(entry); i++) {
299               *q++ = *p++;
300            }
301            *q = 0;
302            sm_check(__FILE__, __LINE__, False);
303            if (!file_is_excluded(ff_pkt, link)) {
304               rtn_stat = find_one_file(ff_pkt, handle_file, pkt, link, our_device, 0);
305            }
306        }
307        closedir(directory);
308        free(link);
309        free(entry);
310
311        if (ff_pkt->atime_preserve) {
312           utime(fname, &restore_times);
313        }
314        return rtn_stat;
315    } /* end check for directory */
316
317    /*
318     * If it is explicitly mentioned (i.e. top_level) and is
319     *  a block device, we do a raw backup of it or if it is
320     *  a fifo, we simply read it.
321     */
322    if (top_level && S_ISBLK(ff_pkt->statp.st_mode)) {
323       ff_pkt->type = FT_RAW;          /* raw partition */
324    } else if (top_level && S_ISFIFO(ff_pkt->statp.st_mode) &&
325               ff_pkt->flags & FO_READFIFO) {
326       ff_pkt->type = FT_FIFO;
327    } else {
328       /* The only remaining types are special (character, ...) files */
329       ff_pkt->type = FT_SPEC;
330    }
331    return handle_file(ff_pkt, pkt);
332 }
333
334 int term_find_one(FF_PKT *ff)
335 {
336    struct f_link *lp, *lc;
337    int count = 0;
338   
339    /* Free up list of hard linked files */
340    for (lp = ff->linklist; lp;) {
341       lc = lp;
342       lp = lp->next;
343       if (lc) {
344          free(lc);
345          count++;
346       }
347    }
348    return count;
349 }