]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/findlib/attribs.c
Implement Win32 data detection code
[bacula/bacula] / bacula / src / findlib / attribs.c
1 /*
2  *  Encode and decode standard Unix attributes and
3  *   Extended attributes for Win32 and
4  *   other non-Unix systems, or Unix systems with ACLs, ...
5  *
6  *    Kern Sibbald, October MMII
7  *
8  *   Version $Id$
9  *
10  */
11 /*
12    Copyright (C) 2000-2003 Kern Sibbald and John Walker
13
14    This program is free software; you can redistribute it and/or
15    modify it under the terms of the GNU General Public License as
16    published by the Free Software Foundation; either version 2 of
17    the License, or (at your option) any later version.
18
19    This program is distributed in the hope that it will be useful,
20    but WITHOUT ANY WARRANTY; without even the implied warranty of
21    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22    General Public License for more details.
23
24    You should have received a copy of the GNU General Public
25    License along with this program; if not, write to the Free
26    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
27    MA 02111-1307, USA.
28
29  */
30
31 #include "bacula.h"
32 #include "find.h"
33
34 #ifdef HAVE_CYGWIN
35
36 /* Forward referenced subroutines */
37 static
38 int set_win32_attributes(JCR *jcr, ATTR *attr, BFILE *ofd);
39 void unix_name_to_win32(POOLMEM **win32_name, char *name);
40 void win_error(JCR *jcr, char *prefix, POOLMEM *ofile);
41 HANDLE bget_handle(BFILE *bfd);
42 #endif
43
44 /* For old systems that don't have lchown() use chown() */
45 #ifndef HAVE_LCHOWN
46 #define lchown chown
47 #endif
48
49 /*=============================================================*/
50 /*                                                             */
51 /*             ***  A l l  S y s t e m s ***                   */
52 /*                                                             */
53 /*=============================================================*/
54
55 /*
56  * Return the data stream that will be used 
57  */
58 int select_data_stream(FF_PKT *ff_pkt)
59 {
60    int stream;
61
62    /* Note, no sparse option for win32_data */
63    if (is_win32_backup()) {
64       stream = STREAM_WIN32_DATA;
65       ff_pkt->flags &= ~FO_SPARSE;
66    } else if (ff_pkt->flags & FO_SPARSE) {
67       stream = STREAM_SPARSE_DATA;
68    } else {
69       stream = STREAM_FILE_DATA;
70    }
71 #ifdef HAVE_LIBZ
72    if (ff_pkt->flags & FO_GZIP) {
73       if (stream == STREAM_WIN32_DATA) {
74          stream = STREAM_WIN32_GZIP_DATA;
75       } else if (stream == STREAM_FILE_DATA) {
76          stream = STREAM_GZIP_DATA;
77       } else {
78          stream = STREAM_SPARSE_GZIP_DATA;
79       }
80    }
81 #endif
82    return stream;
83 }
84
85
86 /* 
87  * Encode a stat structure into a base64 character string   
88  *   All systems must create such a structure.
89  *   In addition, we tack on the LinkFI, which is non-zero in
90  *   the case of a hard linked file that has no data.  This
91  *   is a File Index pointing to the link that does have the
92  *   data (always the first one encountered in a save).
93  * You may piggyback attributes on this packet by encoding
94  *   them in the encode_attribsEx() subroutine, but this is
95  *   not recommended.
96  */
97 void encode_stat(char *buf, FF_PKT *ff_pkt, int data_stream)
98 {
99    char *p = buf;
100    struct stat *statp = &ff_pkt->statp;
101    /*
102     *  Encode a stat packet.  I should have done this more intelligently
103     *   with a length so that it could be easily expanded.
104     */
105    p += to_base64((int64_t)statp->st_dev, p);
106    *p++ = ' ';                        /* separate fields with a space */
107    p += to_base64((int64_t)statp->st_ino, p);
108    *p++ = ' ';
109    p += to_base64((int64_t)statp->st_mode, p);
110    *p++ = ' ';
111    p += to_base64((int64_t)statp->st_nlink, p);
112    *p++ = ' ';
113    p += to_base64((int64_t)statp->st_uid, p);
114    *p++ = ' ';
115    p += to_base64((int64_t)statp->st_gid, p);
116    *p++ = ' ';
117    p += to_base64((int64_t)statp->st_rdev, p);
118    *p++ = ' ';
119    p += to_base64((int64_t)statp->st_size, p);
120    *p++ = ' ';
121    p += to_base64((int64_t)statp->st_blksize, p);
122    *p++ = ' ';
123    p += to_base64((int64_t)statp->st_blocks, p);
124    *p++ = ' ';
125    p += to_base64((int64_t)statp->st_atime, p);
126    *p++ = ' ';
127    p += to_base64((int64_t)statp->st_mtime, p);
128    *p++ = ' ';
129    p += to_base64((int64_t)statp->st_ctime, p);
130    *p++ = ' ';
131    p += to_base64((int64_t)ff_pkt->LinkFI, p);
132    *p++ = ' ';
133
134 #ifdef HAVE_CHFLAGS
135    /* FreeBSD function */
136    p += to_base64((int64_t)statp->st_flags, p);  /* output st_flags */
137 #else
138    p += to_base64((int64_t)0, p);     /* output place holder */
139 #endif
140    *p++ = ' ';
141    p += to_base64((int64_t)data_stream, p);
142    *p = 0;
143    return;
144 }
145
146
147
148 /* Decode a stat packet from base64 characters */
149 int decode_stat(char *buf, struct stat *statp, uint32_t *LinkFI) 
150 {
151    char *p = buf;
152    int64_t val;
153
154    p += from_base64(&val, p);
155    statp->st_dev = val;
156    p++;                               /* skip space */
157    p += from_base64(&val, p);
158    statp->st_ino = val;
159    p++;
160    p += from_base64(&val, p);
161    statp->st_mode = val;
162    p++;
163    p += from_base64(&val, p);
164    statp->st_nlink = val;
165    p++;
166    p += from_base64(&val, p);
167    statp->st_uid = val;
168    p++;
169    p += from_base64(&val, p);
170    statp->st_gid = val;
171    p++;
172    p += from_base64(&val, p);
173    statp->st_rdev = val;
174    p++;
175    p += from_base64(&val, p);
176    statp->st_size = val;
177    p++;
178    p += from_base64(&val, p);
179    statp->st_blksize = val;
180    p++;
181    p += from_base64(&val, p);
182    statp->st_blocks = val;
183    p++;
184    p += from_base64(&val, p);
185    statp->st_atime = val;
186    p++;
187    p += from_base64(&val, p);
188    statp->st_mtime = val;
189    p++;
190    p += from_base64(&val, p);
191    statp->st_ctime = val;
192
193    /* Optional FileIndex of hard linked file data */
194    if (*p == ' ' || (*p != 0 && *(p+1) == ' ')) {
195       p++;
196       p += from_base64(&val, p);
197       *LinkFI = (uint32_t)val;
198    } else {
199       *LinkFI = 0;
200       return 0;
201    }
202
203    /* FreeBSD user flags */
204    if (*p == ' ' || (*p != 0 && *(p+1) == ' ')) {
205       p++;
206       p += from_base64(&val, p);
207 #ifdef HAVE_CHFLAGS
208       statp->st_flags = (uint32_t)val;
209    } else {
210       statp->st_flags  = 0;
211 #endif
212    }
213     
214    /* Look for data stream id */
215    if (*p == ' ' || (*p != 0 && *(p+1) == ' ')) {
216       p++;
217       p += from_base64(&val, p);
218    } else {
219       val = 0;
220    }
221    return (int)val;
222 }
223
224 /*
225  * Set file modes, permissions and times
226  *
227  *  fname is the original filename  
228  *  ofile is the output filename (may be in a different directory)
229  *
230  * Returns:  1 on success
231  *           0 on failure
232  */
233 int set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
234 {
235    struct utimbuf ut;    
236    mode_t old_mask;
237    int stat = 1;
238
239 #ifdef HAVE_CYGWIN
240    if (attr->stream == STREAM_UNIX_ATTRIBUTES_EX &&
241        set_win32_attributes(jcr, attr, ofd)) {
242       return 1;
243    }
244    /*
245     * If Windows stuff failed, e.g. attempt to restore Unix file
246     *  to Windows, simply fall through and we will do it the     
247     *  universal way.
248     */
249 #endif
250
251    old_mask = umask(0);
252    if (is_bopen(ofd)) {
253       bclose(ofd);                    /* first close file */
254    }
255
256    ut.actime = attr->statp.st_atime;
257    ut.modtime = attr->statp.st_mtime;
258
259    /* ***FIXME**** optimize -- don't do if already correct */
260    /* 
261     * For link, change owner of link using lchown, but don't
262     *   try to do a chmod as that will update the file behind it.
263     */
264    if (attr->type == FT_LNK) {
265       /* Change owner of link, not of real file */
266       if (lchown(attr->ofname, attr->statp.st_uid, attr->statp.st_gid) < 0) {
267          Jmsg2(jcr, M_WARNING, 0, _("Unable to set file owner %s: ERR=%s\n"),
268             attr->ofname, strerror(errno));
269          stat = 0;
270       }
271    } else {
272       if (chown(attr->ofname, attr->statp.st_uid, attr->statp.st_gid) < 0) {
273          Jmsg2(jcr, M_WARNING, 0, _("Unable to set file owner %s: ERR=%s\n"),
274             attr->ofname, strerror(errno));
275          stat = 0;
276       }
277       if (chmod(attr->ofname, attr->statp.st_mode) < 0) {
278          Jmsg2(jcr, M_WARNING, 0, _("Unable to set file modes %s: ERR=%s\n"),
279             attr->ofname, strerror(errno));
280          stat = 0;
281       }
282
283       /* FreeBSD user flags */
284 #ifdef HAVE_CHFLAGS
285       if (chflags(attr->ofname, attr->statp.st_flags) < 0) {
286          Jmsg2(jcr, M_WARNING, 0, _("Unable to set file flags %s: ERR=%s\n"),
287             attr->ofname, strerror(errno));
288          stat = 0;
289       }
290 #endif
291       /*
292        * Reset file times.
293        */
294       if (utime(attr->ofname, &ut) < 0) {
295          Jmsg2(jcr, M_ERROR, 0, _("Unable to set file times %s: ERR=%s\n"),
296             attr->ofname, strerror(errno));
297          stat = 0;
298       }
299    }
300    umask(old_mask);
301    return stat;
302 }
303
304
305 /*=============================================================*/
306 /*                                                             */
307 /*                 * * *  U n i x * * * *                      */
308 /*                                                             */
309 /*=============================================================*/
310
311 #ifndef HAVE_CYGWIN
312     
313 /*
314  * It is possible to piggyback additional data e.g. ACLs on
315  *   the encode_stat() data by returning the extended attributes
316  *   here.  They must be "self-contained" (i.e. you keep track
317  *   of your own length), and they must be in ASCII string
318  *   format. Using this feature is not recommended.
319  * The code below shows how to return nothing.  See the Win32
320  *   code below for returning something in the attributes.
321  */
322 int encode_attribsEx(JCR *jcr, char *attribsEx, FF_PKT *ff_pkt)
323 {
324    *attribsEx = 0;                    /* no extended attributes */
325    return STREAM_UNIX_ATTRIBUTES;
326 }
327
328 #endif
329
330
331
332 /*=============================================================*/
333 /*                                                             */
334 /*                 * * *  W i n 3 2 * * * *                    */
335 /*                                                             */
336 /*=============================================================*/
337
338 #ifdef HAVE_CYGWIN
339
340 int encode_attribsEx(JCR *jcr, char *attribsEx, FF_PKT *ff_pkt)
341 {
342    char *p = attribsEx;
343    WIN32_FILE_ATTRIBUTE_DATA atts;
344    ULARGE_INTEGER li;
345
346    attribsEx[0] = 0;                  /* no extended attributes */
347
348    if (!p_GetFileAttributesEx) {
349       return STREAM_UNIX_ATTRIBUTES;
350    }
351
352    unix_name_to_win32(&ff_pkt->sys_fname, ff_pkt->fname);
353    if (!p_GetFileAttributesEx(ff_pkt->sys_fname, GetFileExInfoStandard,
354                             (LPVOID)&atts)) {
355       win_error(jcr, "GetFileAttributesEx:", ff_pkt->sys_fname);
356       return STREAM_UNIX_ATTRIBUTES;
357    }
358
359    p += to_base64((uint64_t)atts.dwFileAttributes, p);
360    *p++ = ' ';                        /* separate fields with a space */
361    li.LowPart = atts.ftCreationTime.dwLowDateTime;
362    li.HighPart = atts.ftCreationTime.dwHighDateTime;
363    p += to_base64((uint64_t)li.QuadPart, p);
364    *p++ = ' ';
365    li.LowPart = atts.ftLastAccessTime.dwLowDateTime;
366    li.HighPart = atts.ftLastAccessTime.dwHighDateTime;
367    p += to_base64((uint64_t)li.QuadPart, p);
368    *p++ = ' ';
369    li.LowPart = atts.ftLastWriteTime.dwLowDateTime;
370    li.HighPart = atts.ftLastWriteTime.dwHighDateTime;
371    p += to_base64((uint64_t)li.QuadPart, p);
372    *p++ = ' ';
373    p += to_base64((uint64_t)atts.nFileSizeHigh, p);
374    *p++ = ' ';
375    p += to_base64((uint64_t)atts.nFileSizeLow, p);
376    *p = 0;
377    return STREAM_UNIX_ATTRIBUTES_EX;
378 }
379
380 /* Define attributes that are legal to set with SetFileAttributes() */
381 #define SET_ATTRS ( \
382          FILE_ATTRIBUTE_ARCHIVE| \
383          FILE_ATTRIBUTE_HIDDEN| \
384          FILE_ATTRIBUTE_NORMAL| \
385          FILE_ATTRIBUTE_NOT_CONTENT_INDEXED| \
386          FILE_ATTRIBUTE_OFFLINE| \
387          FILE_ATTRIBUTE_READONLY| \
388          FILE_ATTRIBUTE_SYSTEM| \
389          FILE_ATTRIBUTE_TEMPORARY)
390
391
392 /*
393  * Set Extended File Attributes for Win32
394  *
395  *  fname is the original filename  
396  *  ofile is the output filename (may be in a different directory)
397  *
398  * Returns:  1 on success
399  *           0 on failure
400  */
401 static
402 int set_win32_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
403 {
404    char *p = attr->attrEx;
405    int64_t val;
406    WIN32_FILE_ATTRIBUTE_DATA atts;
407    ULARGE_INTEGER li;
408    POOLMEM *win32_ofile;
409
410    if (!p || !*p) {                   /* we should have attributes */
411       Dmsg2(100, "Attributes missing. of=%s ofd=%d\n", attr->ofname, ofd->fid);
412       if (is_bopen(ofd)) {
413          bclose(ofd);
414       }
415       return 0;
416    } else {
417       Dmsg2(100, "Attribs %s = %s\n", attr->ofname, attr->attrEx);
418    }
419
420    p += from_base64(&val, p);
421    atts.dwFileAttributes = val;
422    p++;                               /* skip space */
423    p += from_base64(&val, p);
424    li.QuadPart = val;
425    atts.ftCreationTime.dwLowDateTime = li.LowPart;
426    atts.ftCreationTime.dwHighDateTime = li.HighPart;
427    p++;                               /* skip space */
428    p += from_base64(&val, p);
429    li.QuadPart = val;
430    atts.ftLastAccessTime.dwLowDateTime = li.LowPart;
431    atts.ftLastAccessTime.dwHighDateTime = li.HighPart;
432    p++;                               /* skip space */
433    p += from_base64(&val, p);
434    li.QuadPart = val;
435    atts.ftLastWriteTime.dwLowDateTime = li.LowPart;
436    atts.ftLastWriteTime.dwHighDateTime = li.HighPart;
437    p++;   
438    p += from_base64(&val, p);
439    atts.nFileSizeHigh = val;
440    p++;
441    p += from_base64(&val, p);
442    atts.nFileSizeLow = val;
443
444    /* Convert to Windows path format */
445    win32_ofile = get_pool_memory(PM_FNAME);
446    unix_name_to_win32(&win32_ofile, attr->ofname);
447
448
449
450    /* At this point, we have reconstructed the WIN32_FILE_ATTRIBUTE_DATA pkt */
451
452
453    if (!is_bopen(ofd)) {
454       Dmsg1(100, "File not open: %s\n", attr->ofname);
455       bopen(ofd, attr->ofname, O_WRONLY|O_BINARY, 0);   /* attempt to open the file */
456    }
457
458    if (is_bopen(ofd)) {
459       Dmsg1(100, "SetFileTime %s\n", attr->ofname);
460       if (!SetFileTime(bget_handle(ofd),
461                          &atts.ftCreationTime,
462                          &atts.ftLastAccessTime,
463                          &atts.ftLastWriteTime)) {
464          win_error(jcr, "SetFileTime:", win32_ofile);
465       }
466       bclose(ofd);
467    }
468
469    Dmsg1(100, "SetFileAtts %s\n", attr->ofname);
470    if (!(atts.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
471       if (!SetFileAttributes(win32_ofile, atts.dwFileAttributes & SET_ATTRS)) {
472          win_error(jcr, "SetFileAttributes:", win32_ofile);
473       }
474    }
475    free_pool_memory(win32_ofile);
476    return 1;
477 }
478
479 void win_error(JCR *jcr, char *prefix, POOLMEM *win32_ofile)
480 {
481    DWORD lerror = GetLastError();
482    LPTSTR msg;
483    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
484                  FORMAT_MESSAGE_FROM_SYSTEM,
485                  NULL,
486                  lerror,
487                  0,
488                  (LPTSTR)&msg,
489                  0,
490                  NULL);
491    Dmsg3(100, "Error in %s on file %s: ERR=%s\n", prefix, win32_ofile, msg);
492    strip_trailing_junk(msg);
493    Jmsg(jcr, M_INFO, 0, _("Error in %s file %s: ERR=%s\n"), prefix, win32_ofile, msg);
494    LocalFree(msg);
495 }
496
497 void win_error(JCR *jcr, char *prefix, DWORD lerror)
498 {
499    LPTSTR msg;
500    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
501                  FORMAT_MESSAGE_FROM_SYSTEM,
502                  NULL,
503                  lerror,
504                  0,
505                  (LPTSTR)&msg,
506                  0,
507                  NULL);
508    strip_trailing_junk(msg);
509    if (jcr) {
510       Jmsg2(jcr, M_INFO, 0, _("Error in %s: ERR=%s\n"), prefix, msg);
511    } else {
512       MessageBox(NULL, msg, prefix, MB_OK);
513    }
514    LocalFree(msg);
515 }
516
517
518 /* Cygwin API definition */
519 extern "C" void cygwin_conv_to_win32_path(const char *path, char *win32_path);
520
521 void unix_name_to_win32(POOLMEM **win32_name, char *name)
522 {
523    /* One extra byte should suffice, but we double it */
524    *win32_name = check_pool_memory_size(*win32_name, 2*strlen(name)+1);
525    cygwin_conv_to_win32_path(name, *win32_name);
526 }
527
528 #endif  /* HAVE_CYGWIN */