]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/findlib/find_one.c
597d2575e9ea7fde8bcb3f92966bdc60c1a736b5
[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    if (ff_pkt->compute_MD5) {
81       ff_pkt->flags |= FO_MD5;
82    }
83    if (ff_pkt->GZIP_compression) {
84       ff_pkt->flags |= FO_GZIP;
85    }
86
87    if (lstat(fname, &ff_pkt->statp) != 0) {
88        /* Cannot stat file */
89        ff_pkt->type = FT_NOSTAT;
90        ff_pkt->ff_errno = errno;
91        return handle_file(ff_pkt, pkt);
92    }
93
94    Dmsg1(60, "File ----: %s\n", fname);
95 #ifdef DEBUG
96    if (S_ISLNK(ff_pkt->statp.st_mode))
97       Dmsg1(60, "Link-------------: %s \n", fname);
98 #endif
99
100    /* Save current times of this directory in case we need to
101     * reset them because the user doesn't want them changed.
102     */
103    restore_times.actime = ff_pkt->statp.st_atime;
104    restore_times.modtime = ff_pkt->statp.st_mtime;
105
106
107    /* 
108     * If this is an Incremental backup, see if file was modified
109     * since our last "save_time", presumably the last Full save
110     * or Incremental.
111     */
112    if (ff_pkt->incremental && !S_ISDIR(ff_pkt->statp.st_mode)) {
113       Dmsg1(100, "Non-directory incremental: %s\n", ff_pkt->fname);
114       /* Not a directory */
115       if (ff_pkt->statp.st_mtime < ff_pkt->save_time
116           && (ff_pkt->mtime_only || 
117               ff_pkt->statp.st_ctime < ff_pkt->save_time)) {
118          /* Incremental option, file not changed */
119          ff_pkt->type = FT_NOCHG;
120          Dmsg1(100, "File not changed: %s\n", ff_pkt->fname);
121          Dmsg4(200, "save_time=%d mtime=%d mtime_only=%d st_ctime=%d\n",
122             ff_pkt->save_time, ff_pkt->statp.st_mtime, 
123             ff_pkt->mtime_only, ff_pkt->statp.st_ctime);
124          return handle_file(ff_pkt, pkt);
125       }
126    }
127
128 #if xxxxxxx
129    /* See if we are trying to dump the archive.  */
130    if (ar_dev && ff_pkt->statp.st_dev == ar_dev && ff_pkt->statp.st_ino == ar_ino) {
131        ff_pkt->type = FT_ISARCH;
132        return handle_file(ff_pkt, pkt);
133    }
134 #endif
135
136    /* 
137     * Handle hard linked files
138     *
139     * Maintain a list of hard linked files already backed up. This
140     *  allows us to ensure that the data of each file gets backed 
141     *  up only once.
142     */
143    if (ff_pkt->statp.st_nlink > 1
144        && (S_ISREG(ff_pkt->statp.st_mode)
145            || S_ISCHR(ff_pkt->statp.st_mode)
146            || S_ISBLK(ff_pkt->statp.st_mode)
147            || S_ISFIFO(ff_pkt->statp.st_mode)
148            || S_ISSOCK(ff_pkt->statp.st_mode))) {
149
150        struct f_link *lp;
151
152        /* Search link list of hard linked files */
153        for (lp = ff_pkt->linklist; lp; lp = lp->next)
154           if (lp->ino == ff_pkt->statp.st_ino && lp->dev == ff_pkt->statp.st_dev) {
155               ff_pkt->link = lp->name;
156               ff_pkt->type = FT_LNKSAVED;       /* Handle link, file already saved */
157               return handle_file(ff_pkt, pkt);
158           }
159
160        /* File not previously dumped. Chain it into our list. */
161        lp = (struct f_link *)bmalloc(sizeof (struct f_link) + strlen(fname));
162        lp->ino = ff_pkt->statp.st_ino;
163        lp->dev = ff_pkt->statp.st_dev;
164        strcpy (lp->name, fname);
165        lp->next = ff_pkt->linklist;
166        ff_pkt->linklist = lp;
167    }
168
169    /* This is not a link to a previously dumped file, so dump it.  */
170    if (S_ISREG(ff_pkt->statp.st_mode)) {
171        off_t sizeleft;
172
173        sizeleft = ff_pkt->statp.st_size;
174
175        /* Don't bother opening empty, world readable files.  Also do not open
176           files when archive is meant for /dev/null.  */
177        if (ff_pkt->null_output_device || (sizeleft == 0
178                && MODE_RALL == (MODE_RALL & ff_pkt->statp.st_mode))) {
179           ff_pkt->type = FT_REGE;
180        } else {
181           ff_pkt->type = FT_REG;
182        }
183        return handle_file(ff_pkt, pkt);
184
185    } else if (S_ISLNK(ff_pkt->statp.st_mode)) {
186        int size;
187        char *buffer = (char *)alloca(PATH_MAX + 1);
188
189        size = readlink(fname, buffer, PATH_MAX + 1);
190        if (size < 0) {
191            /* Could not follow link */                             
192            ff_pkt->type = FT_NOFOLLOW;
193            ff_pkt->ff_errno = errno;
194            return handle_file(ff_pkt, pkt);
195        }
196        buffer[size] = 0;
197        ff_pkt->link = buffer;
198        ff_pkt->type = FT_LNK;           /* got a real link */
199        return handle_file(ff_pkt, pkt);
200
201    } else if (S_ISDIR(ff_pkt->statp.st_mode)) {
202        DIR *directory;
203        struct dirent *entry, *result;
204        char *namebuf;
205        size_t namebuf_len;
206        size_t len;
207        int status;
208        dev_t our_device = ff_pkt->statp.st_dev;
209
210        if (access(fname, R_OK) == -1 && geteuid() != 0) {
211            /* Could not access() directory */
212            ff_pkt->type = FT_NOACCESS;
213            ff_pkt->ff_errno = errno;
214            return handle_file(ff_pkt, pkt);
215        }
216
217        /* Build a canonical directory name with a trailing slash. */
218        len = strlen(fname);
219        namebuf_len = len + DEFAULT_NAMEBUF_LEN;
220        namebuf = (char *)bmalloc(namebuf_len + 2);
221        bstrncpy(namebuf, fname, namebuf_len);
222        while (len >= 1 && namebuf[len - 1] == '/')
223          len--;
224        namebuf[len++] = '/';
225        namebuf[len] = '\0';
226
227        ff_pkt->link = namebuf;
228        if (ff_pkt->incremental &&
229            (ff_pkt->statp.st_mtime < ff_pkt->save_time &&
230             ff_pkt->statp.st_ctime < ff_pkt->save_time)) {
231           /* Incremental option, directory entry not changed */
232           ff_pkt->type = FT_DIRNOCHG;
233        } else {
234           ff_pkt->type = FT_DIR;
235        }
236        handle_file(ff_pkt, pkt);       /* handle directory entry */
237
238        ff_pkt->link = ff_pkt->fname;     /* reset "link" */
239
240        /* 
241         * Do not decend into subdirectories (recurse) if the
242         * user has turned it off for this directory.
243         */
244        if (ff_pkt->no_recursion) {
245           free(namebuf);
246           /* No recursion into this directory */
247           ff_pkt->type = FT_NORECURSE;
248           return handle_file(ff_pkt, pkt);
249        }
250
251        /* 
252         * See if we are crossing file systems, and
253         * avoid doing so if the user only wants to dump one file system.
254         */
255        if (ff_pkt->one_file_system && !top_level
256            && parent_device != ff_pkt->statp.st_dev) {
257           free(namebuf);
258           ff_pkt->type = FT_NOFSCHG;
259           return handle_file(ff_pkt, pkt);
260        }
261
262        /* 
263         * Now process the files in this directory.
264         */
265        errno = 0;
266        if ((directory = opendir(fname)) == NULL) {
267           free(namebuf);
268           ff_pkt->type = FT_NOOPEN;
269           ff_pkt->ff_errno = errno;
270           return handle_file(ff_pkt, pkt);
271        }
272
273        /*
274         * This would possibly run faster if we chdir to the directory
275         * before traversing it.
276         */
277        rtn_stat = 1;
278        entry = (struct dirent *)malloc(sizeof(struct dirent) + name_max + 10);
279        for ( ;; ) {
280            char *p;
281
282            status  = readdir_r(directory, entry, &result);
283            Dmsg3(200, "readdir stat=%d result=%x name=%s\n", status, result,
284               entry->d_name);
285            if (status != 0 || result == NULL) {
286               break;
287            }
288            p = entry->d_name;
289            /* Skip `.', `..', and excluded file names.  */
290            if (p[0] == '\0' || (p[0] == '.' && (p[1] == '\0' ||
291                (p[1] == '.' && p[2] == '\0')))) {
292               continue;
293            }
294
295            if ((int)NAMELEN(entry) + len >= namebuf_len) {
296                namebuf_len = len + NAMELEN(entry);
297                namebuf = (char *)brealloc(namebuf, namebuf_len + 2);
298            }
299            strcpy(namebuf + len, entry->d_name);
300            if (!file_is_excluded(ff_pkt, namebuf)) {
301               rtn_stat = find_one_file(ff_pkt, handle_file, pkt, namebuf, our_device, 0);
302            }
303        }
304        closedir(directory);
305        free(namebuf);
306        free(entry);
307        if (ff_pkt->atime_preserve) {
308           utime(fname, &restore_times);
309        }
310        return rtn_stat;
311    } /* end check for directory */
312
313    /*
314     * If it is explicitly mentioned (i.e. top_level) and is
315     *  a block device, we do a raw backup of it.
316     */
317    if (top_level && S_ISBLK(ff_pkt->statp.st_mode)) {
318       ff_pkt->type = FT_RAW;          /* raw partition */
319    } else {
320       /* The only remaining types are special (character, ...) files */
321       ff_pkt->type = FT_SPEC;
322    }
323    return handle_file(ff_pkt, pkt);
324 }
325
326 void term_find_one(FF_PKT *ff)
327 {
328    struct f_link *lp, *lc;
329   
330    /* Free up list of hard linked files */
331    for (lp = ff->linklist; lp;) {
332       lc = lp;
333       lp = lp->next;
334       if (lc) {
335          free(lc);
336       }
337    }
338    return;
339 }