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