1 /*
2    Bacula(R) - The Network Backup Solution
3 
4    Copyright (C) 2000-2020 Kern Sibbald
5 
6    The original author of Bacula is Kern Sibbald, with contributions
7    from many others, a complete list can be found in the file AUTHORS.
8 
9    You may use this file and others of this release according to the
10    license defined in the LICENSE file, which includes the Affero General
11    Public License, v3.0 ("AGPLv3") and some additional permissions and
12    terms pursuant to its AGPLv3 Section 7.
13 
14    This notice must be preserved when any source code is
15    conveyed and/or propagated.
16 
17    Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20  *   attr.c  Unpack an Attribute record returned from the tape
21  *
22  *    Kern Sibbald, June MMIII  (code pulled from filed/restore.c and updated)
23  *
24  */
25 
26 
27 #include "bacula.h"
28 #include "jcr.h"
29 #include "lib/breg.h"
30 
31 static const int dbglvl = 150;
32 
new_attr(JCR * jcr)33 ATTR *new_attr(JCR *jcr)
34 {
35    ATTR *attr = (ATTR *)malloc(sizeof(ATTR));
36    memset(attr, 0, sizeof(ATTR));
37    attr->ofname = get_pool_memory(PM_FNAME);
38    attr->olname = get_pool_memory(PM_FNAME);
39    attr->attrEx = get_pool_memory(PM_FNAME);
40    attr->jcr = jcr;
41    attr->uid = getuid();
42    return attr;
43 }
44 
free_attr(ATTR * attr)45 void free_attr(ATTR *attr)
46 {
47    free_pool_memory(attr->olname);
48    free_pool_memory(attr->ofname);
49    free_pool_memory(attr->attrEx);
50    free(attr);
51 }
52 
unpack_attributes_record(JCR * jcr,int32_t stream,char * rec,int32_t reclen,ATTR * attr)53 int unpack_attributes_record(JCR *jcr, int32_t stream, char *rec, int32_t reclen, ATTR *attr)
54 {
55    char *p;
56    int object_len;
57    /*
58     * An Attributes record consists of:
59     *    File_index
60     *    Type   (FT_types)
61     *    Filename
62     *    Attributes
63     *    Link name (if file linked i.e. FT_LNK)
64     *    Extended attributes (Win32)
65     *  plus optional values determined by AR_ flags in upper bits of Type
66     *    Data_stream
67     *
68     */
69    attr->stream = stream;
70    Dmsg1(dbglvl, "Attr: %s\n", rec);
71    if (sscanf(rec, "%d %d", &attr->file_index, &attr->type) != 2) {
72       Jmsg(jcr, M_FATAL, 0, _("Error scanning attributes: %s\n"), rec);
73       Dmsg1(dbglvl, "\nError scanning attributes. %s\n", rec);
74       return 0;
75    }
76    Dmsg2(dbglvl, "Got Attr: FilInx=%d type=%d\n", attr->file_index, attr->type);
77    /*
78     * Note AR_DATA_STREAM should never be set since it is encoded
79     *  at the end of the attributes.
80     */
81    if (attr->type & AR_DATA_STREAM) {
82       attr->data_stream = 1;
83    } else {
84       attr->data_stream = 0;
85    }
86    attr->type &= FT_MASK;             /* keep only type bits */
87    p = rec;
88    while (*p++ != ' ')               /* skip record file index */
89       { }
90    while (*p++ != ' ')               /* skip type */
91       { }
92 
93    attr->fname = p;                   /* set filename position */
94    while (*p++ != 0)                  /* skip filename */
95       { }
96    attr->attr = p;                    /* set attributes position */
97    while (*p++ != 0)                  /* skip attributes */
98       { }
99    attr->lname = p;                   /* set link position */
100    while (*p++ != 0)                  /* skip link */
101       { }
102    attr->delta_seq = 0;
103    if (attr->type == FT_RESTORE_FIRST) {
104       /* We have an object, so do a binary copy */
105       object_len = reclen + rec - p;
106       attr->attrEx = check_pool_memory_size(attr->attrEx, object_len + 1);
107       memcpy(attr->attrEx, p, object_len);
108       /* Add a EOS for those who attempt to print the object */
109       p = attr->attrEx + object_len;
110       *p = 0;
111    } else {
112       pm_strcpy(attr->attrEx, p);     /* copy extended attributes, if any */
113       if (attr->data_stream) {
114          int64_t val;
115          while (*p++ != 0)            /* skip extended attributes */
116             { }
117          from_base64(&val, p);
118          attr->data_stream = (int32_t)val;
119       } else {
120          while (*p++ != 0)            /* skip extended attributes */
121             { }
122          if (p - rec < reclen) {
123             attr->delta_seq = str_to_int32(p); /* delta_seq */
124          }
125       }
126    }
127    Dmsg8(dbglvl, "unpack_attr FI=%d Type=%d fname=%s attr=%s lname=%s attrEx=%s datastr=%d delta_seq=%d\n",
128       attr->file_index, attr->type, attr->fname, attr->attr, attr->lname,
129       attr->attrEx, attr->data_stream, attr->delta_seq);
130    *attr->ofname = 0;
131    *attr->olname = 0;
132    return 1;
133 }
134 
135 #if defined(HAVE_WIN32)
strip_double_slashes(char * fname)136 static void strip_double_slashes(char *fname)
137 {
138    char *p = fname;
139    while (p && *p) {
140       p = strpbrk(p, "/\\");
141       if (p != NULL) {
142          if (IsPathSeparator(p[1])) {
143             strcpy(p, p+1);
144          }
145          p++;
146       }
147    }
148 }
149 #endif
150 
151 /*
152  * Build attr->ofname from attr->fname and
153  *       attr->olname from attr->olname
154  */
build_attr_output_fnames(JCR * jcr,ATTR * attr)155 void build_attr_output_fnames(JCR *jcr, ATTR *attr)
156 {
157    /*
158     * Prepend the where directory so that the
159     * files are put where the user wants.
160     *
161     * We do a little jig here to handle Win32 files with
162     *   a drive letter -- we simply change the drive
163     *   from, for example, c: to c/ for
164     *   every filename if a prefix is supplied.
165     *
166     */
167 
168    if (jcr->where_bregexp) {
169       char *ret;
170       apply_bregexps(attr->fname, &attr->statp, jcr->where_bregexp, &ret);
171       pm_strcpy(attr->ofname, ret);
172 
173       if (attr->type == FT_LNKSAVED || attr->type == FT_LNK) {
174          /* Always add prefix to hard links (FT_LNKSAVED) and
175           *  on user request to soft links
176           */
177 
178          if ((attr->type == FT_LNKSAVED || jcr->prefix_links)) {
179             apply_bregexps(attr->lname, &attr->statp, jcr->where_bregexp, &ret);
180             pm_strcpy(attr->olname, ret);
181 
182          } else {
183             pm_strcpy(attr->olname, attr->lname);
184          }
185       }
186 
187    } else if (jcr->where[0] == 0) {
188       pm_strcpy(attr->ofname, attr->fname);
189       pm_strcpy(attr->olname, attr->lname);
190 
191    } else {
192       const char *fn;
193       int wherelen = strlen(jcr->where);
194       pm_strcpy(attr->ofname, jcr->where);  /* copy prefix */
195 #if defined(HAVE_WIN32)
196       if (attr->fname[1] == ':') {
197          attr->fname[1] = '/';     /* convert : to / */
198       }
199 #endif
200       fn = attr->fname;            /* take whole name */
201       /* Ensure where is terminated with a slash */
202       if (!IsPathSeparator(jcr->where[wherelen-1]) && !IsPathSeparator(fn[0])) {
203          pm_strcat(attr->ofname, "/");
204       }
205       pm_strcat(attr->ofname, fn); /* copy rest of name */
206       /*
207        * Fixup link name -- if it is an absolute path
208        */
209       if (attr->type == FT_LNKSAVED || attr->type == FT_LNK) {
210          bool add_link;
211          /* Always add prefix to hard links (FT_LNKSAVED) and
212           *  on user request to soft links
213           */
214          if (IsPathSeparator(attr->lname[0]) &&
215              (attr->type == FT_LNKSAVED || jcr->prefix_links)) {
216             pm_strcpy(attr->olname, jcr->where);
217             add_link = true;
218          } else {
219             attr->olname[0] = 0;
220             add_link = false;
221          }
222 
223 #if defined(HAVE_WIN32)
224          if (attr->lname[1] == ':') {
225             attr->lname[1] = '/';    /* turn : into / */
226          }
227 #endif
228          fn = attr->lname;       /* take whole name */
229          /* Ensure where is terminated with a slash */
230          if (add_link &&
231             !IsPathSeparator(jcr->where[wherelen-1]) &&
232             !IsPathSeparator(fn[0])) {
233             pm_strcat(attr->olname, "/");
234          }
235          pm_strcat(attr->olname, fn);     /* copy rest of link */
236       }
237    }
238 #if defined(HAVE_WIN32)
239    strip_double_slashes(attr->ofname);
240    strip_double_slashes(attr->olname);
241 #endif
242 }
243 
244 extern char *getuser(uid_t uid, char *name, int len);
245 extern char *getgroup(gid_t gid, char *name, int len);
246 
247 /*
248  * Print an ls style message, also send M_RESTORED/M_SAVED
249  */
print_ls_output(JCR * jcr,ATTR * attr,int message_type)250 void print_ls_output(JCR *jcr, ATTR *attr, int message_type /* M_RESTORED */)
251 {
252    char buf[5000];
253    char ec1[30];
254    char en1[30], en2[30];
255    char *p, *f;
256    guid_list *guid;
257 
258    /* No need to compute everything if it's not required */
259    if (!chk_dbglvl(dbglvl) && !is_message_type_set(jcr, message_type)) {
260       return;
261    }
262 
263    if (attr->type == FT_DELETED) { /* TODO: change this to get last seen values */
264       bsnprintf(buf, sizeof(buf),
265                 "-*DELETED-   - -        -                  - ---------- --------  %s\n", attr->ofname);
266       Dmsg1(dbglvl, "%s", buf);
267       Jmsg(jcr, message_type, 1, "%s", buf);
268       return;
269    }
270 
271    if (!jcr->id_list) {
272       jcr->id_list = new_guid_list();
273    }
274    guid = jcr->id_list;
275    p = encode_mode(attr->statp.st_mode, buf);
276    p += sprintf(p, "  %2d ", (uint32_t)attr->statp.st_nlink);
277    p += sprintf(p, "%-8.8s %-8.8s",
278                 guid->uid_to_name(attr->statp.st_uid, en1, sizeof(en1)),
279                 guid->gid_to_name(attr->statp.st_gid, en2, sizeof(en2)));
280    p += sprintf(p, " %18.18s ", edit_int64(attr->statp.st_size, ec1));
281    p = encode_time(attr->statp.st_ctime, p);
282    *p++ = ' ';
283    *p++ = ' ';
284    for (f=attr->ofname; *f && (p-buf) < (int)sizeof(buf)-10; ) {
285       *p++ = *f++;
286    }
287    if (attr->type == FT_LNK) {
288       *p++ = ' ';
289       *p++ = '-';
290       *p++ = '>';
291       *p++ = ' ';
292       /* Copy link name */
293       for (f=attr->olname; *f && (p-buf) < (int)sizeof(buf)-10; ) {
294          *p++ = *f++;
295       }
296    }
297    *p++ = '\n';
298    *p = 0;
299    Dmsg1(dbglvl, "%s", buf);
300    Jmsg(jcr, message_type, 1, "%s", buf);
301 }
302