]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/findlib/find_one.c
5f2f3468ea04e894170e96eda4072035b5f534b9
[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 + 1);
182
183        size = readlink(fname, buffer, PATH_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 *namebuf;
199        size_t namebuf_len;
200        size_t 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        namebuf_len = len + DEFAULT_NAMEBUF_LEN;
214        namebuf = (char *)bmalloc(namebuf_len + 2);
215        bstrncpy(namebuf, fname, namebuf_len);
216        while (len >= 1 && namebuf[len - 1] == '/')
217          len--;
218        namebuf[len++] = '/';
219        namebuf[len] = '\0';
220
221        ff_pkt->link = namebuf;
222        if (ff_pkt->incremental &&
223            (ff_pkt->statp.st_mtime < ff_pkt->save_time &&
224             ff_pkt->statp.st_ctime < ff_pkt->save_time)) {
225           /* Incremental option, directory entry not changed */
226           ff_pkt->type = FT_DIRNOCHG;
227        } else {
228           ff_pkt->type = FT_DIR;
229        }
230        handle_file(ff_pkt, pkt);       /* handle directory entry */
231
232        ff_pkt->link = ff_pkt->fname;     /* reset "link" */
233
234        /* 
235         * Do not decend into subdirectories (recurse) if the
236         * user has turned it off for this directory.
237         */
238        if (ff_pkt->flags & FO_NO_RECURSION) {
239           free(namebuf);
240           /* No recursion into this directory */
241           ff_pkt->type = FT_NORECURSE;
242           return handle_file(ff_pkt, pkt);
243        }
244
245        /* 
246         * See if we are crossing file systems, and
247         * avoid doing so if the user only wants to dump one file system.
248         */
249        if (!top_level && !(ff_pkt->flags & FO_MULTIFS) &&
250             parent_device != ff_pkt->statp.st_dev) {
251           free(namebuf);
252           /* returning here means we do not handle this directory */
253           ff_pkt->type = FT_NOFSCHG;
254           return handle_file(ff_pkt, pkt);
255        }
256        /* 
257         * Now process the files in this directory.
258         */
259        errno = 0;
260        if ((directory = opendir(fname)) == NULL) {
261           free(namebuf);
262           ff_pkt->type = FT_NOOPEN;
263           ff_pkt->ff_errno = errno;
264           return handle_file(ff_pkt, pkt);
265        }
266
267        /*
268         * This would possibly run faster if we chdir to the directory
269         * before traversing it.
270         */
271        rtn_stat = 1;
272        entry = (struct dirent *)malloc(sizeof(struct dirent) + name_max + 10);
273        for ( ;; ) {
274            char *p;
275
276            status  = readdir_r(directory, entry, &result);
277            Dmsg3(200, "readdir stat=%d result=%x name=%s\n", status, result,
278               entry->d_name);
279            if (status != 0 || result == NULL) {
280               break;
281            }
282            p = entry->d_name;
283            /* Skip `.', `..', and excluded file names.  */
284            if (p[0] == '\0' || (p[0] == '.' && (p[1] == '\0' ||
285                (p[1] == '.' && p[2] == '\0')))) {
286               continue;
287            }
288
289            if ((int)NAMELEN(entry) + len >= namebuf_len) {
290                namebuf_len = len + NAMELEN(entry);
291                namebuf = (char *)brealloc(namebuf, namebuf_len + 2);
292            }
293            strcpy(namebuf + len, entry->d_name);
294            if (!file_is_excluded(ff_pkt, namebuf)) {
295               rtn_stat = find_one_file(ff_pkt, handle_file, pkt, namebuf, our_device, 0);
296            }
297        }
298        closedir(directory);
299        free(namebuf);
300        free(entry);
301        if (ff_pkt->atime_preserve) {
302           utime(fname, &restore_times);
303        }
304        return rtn_stat;
305    } /* end check for directory */
306
307    /*
308     * If it is explicitly mentioned (i.e. top_level) and is
309     *  a block device, we do a raw backup of it or if it is
310     *  a fifo, we simply read it.
311     */
312    if (top_level && S_ISBLK(ff_pkt->statp.st_mode)) {
313       ff_pkt->type = FT_RAW;          /* raw partition */
314    } else if (top_level && S_ISFIFO(ff_pkt->statp.st_mode) &&
315               ff_pkt->flags & FO_READFIFO) {
316       ff_pkt->type = FT_FIFO;
317    } else {
318       /* The only remaining types are special (character, ...) files */
319       ff_pkt->type = FT_SPEC;
320    }
321    return handle_file(ff_pkt, pkt);
322 }
323
324 int term_find_one(FF_PKT *ff)
325 {
326    struct f_link *lp, *lc;
327    int count = 0;
328   
329    /* Free up list of hard linked files */
330    for (lp = ff->linklist; lp;) {
331       lc = lp;
332       lp = lp->next;
333       if (lc) {
334          free(lc);
335          count++;
336       }
337    }
338    return count;
339 }