]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/findlib/attribs.c
VERIFY + automatic labeling bug fix + cygwin cleanups
[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 /* 
57  * Encode a stat structure into a base64 character string   
58  *   All systems must create such a structure.
59  *   In addition, we tack on the LinkFI, which is non-zero in
60  *   the case of a hard linked file that has no data.  This
61  *   is a File Index pointing to the link that does have the
62  *   data (always the first one encountered in a save).
63  * You may piggyback attributes on this packet by encoding
64  *   them in the encode_attribsEx() subroutine, but this is
65  *   not recommended.
66  */
67 void encode_stat(char *buf, struct stat *statp, uint32_t LinkFI)
68 {
69    char *p = buf;
70    /*
71     * NOTE: we should use rdev as major and minor device if
72     * it is a block or char device (S_ISCHR(statp->st_mode)
73     * or S_ISBLK(statp->st_mode)).  In all other cases,
74     * it is not used.   
75     *
76     */
77    p += to_base64((int64_t)statp->st_dev, p);
78    *p++ = ' ';                        /* separate fields with a space */
79    p += to_base64((int64_t)statp->st_ino, p);
80    *p++ = ' ';
81    p += to_base64((int64_t)statp->st_mode, p);
82    *p++ = ' ';
83    p += to_base64((int64_t)statp->st_nlink, p);
84    *p++ = ' ';
85    p += to_base64((int64_t)statp->st_uid, p);
86    *p++ = ' ';
87    p += to_base64((int64_t)statp->st_gid, p);
88    *p++ = ' ';
89    p += to_base64((int64_t)statp->st_rdev, p);
90    *p++ = ' ';
91    p += to_base64((int64_t)statp->st_size, p);
92    *p++ = ' ';
93    p += to_base64((int64_t)statp->st_blksize, p);
94    *p++ = ' ';
95    p += to_base64((int64_t)statp->st_blocks, p);
96    *p++ = ' ';
97    p += to_base64((int64_t)statp->st_atime, p);
98    *p++ = ' ';
99    p += to_base64((int64_t)statp->st_mtime, p);
100    *p++ = ' ';
101    p += to_base64((int64_t)statp->st_ctime, p);
102    *p++ = ' ';
103    p += to_base64((int64_t)LinkFI, p);
104
105 /* FreeBSD function */
106 #ifdef HAVE_CHFLAGS
107    *p++ = ' ';
108    p += to_base64((int64_t)statp->st_flags, p);
109 #endif
110    *p = 0;
111    return;
112 }
113
114
115
116 /* Decode a stat packet from base64 characters */
117 void
118 decode_stat(char *buf, struct stat *statp, uint32_t *LinkFI) 
119 {
120    char *p = buf;
121    int64_t val;
122
123    p += from_base64(&val, p);
124    statp->st_dev = val;
125    p++;                               /* skip space */
126    p += from_base64(&val, p);
127    statp->st_ino = val;
128    p++;
129    p += from_base64(&val, p);
130    statp->st_mode = val;
131    p++;
132    p += from_base64(&val, p);
133    statp->st_nlink = val;
134    p++;
135    p += from_base64(&val, p);
136    statp->st_uid = val;
137    p++;
138    p += from_base64(&val, p);
139    statp->st_gid = val;
140    p++;
141    p += from_base64(&val, p);
142    statp->st_rdev = val;
143    p++;
144    p += from_base64(&val, p);
145    statp->st_size = val;
146    p++;
147    p += from_base64(&val, p);
148    statp->st_blksize = val;
149    p++;
150    p += from_base64(&val, p);
151    statp->st_blocks = val;
152    p++;
153    p += from_base64(&val, p);
154    statp->st_atime = val;
155    p++;
156    p += from_base64(&val, p);
157    statp->st_mtime = val;
158    p++;
159    p += from_base64(&val, p);
160    statp->st_ctime = val;
161    /* Optional FileIndex of hard linked file data */
162    if (*p == ' ' || (*p != 0 && *(p+1) == ' ')) {
163       p++;
164       p += from_base64(&val, p);
165       *LinkFI = (uint32_t)val;
166   } else {
167       *LinkFI = 0;
168   }
169
170 /* FreeBSD user flags */
171 #ifdef HAVE_CHFLAGS
172    if (*p == ' ' || (*p != 0 && *(p+1) == ' ')) {
173       p++;
174       p += from_base64(&val, p);
175       statp->st_flags = (uint32_t)val;
176   } else {
177       statp->st_flags  = 0;
178   }
179 #endif
180 }
181
182 /*
183  * Set file modes, permissions and times
184  *
185  *  fname is the original filename  
186  *  ofile is the output filename (may be in a different directory)
187  *
188  * Returns:  1 on success
189  *           0 on failure
190  */
191 int set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
192 {
193    struct utimbuf ut;    
194    mode_t old_mask;
195    int stat = 1;
196
197 #ifdef HAVE_CYGWIN
198    if (attr->stream == STREAM_UNIX_ATTRIBUTES_EX &&
199        set_win32_attributes(jcr, attr, ofd)) {
200       return 1;
201    }
202    /*
203     * If Windows stuff failed, e.g. attempt to restore Unix file
204     *  to Windows, simply fall through and we will do it the     
205     *  universal way.
206     */
207 #endif
208
209    old_mask = umask(0);
210    if (is_bopen(ofd)) {
211       bclose(ofd);                    /* first close file */
212    }
213
214    ut.actime = attr->statp.st_atime;
215    ut.modtime = attr->statp.st_mtime;
216
217    /* ***FIXME**** optimize -- don't do if already correct */
218    /* 
219     * For link, change owner of link using lchown, but don't
220     *   try to do a chmod as that will update the file behind it.
221     */
222    if (attr->type == FT_LNK) {
223       /* Change owner of link, not of real file */
224       if (lchown(attr->ofname, attr->statp.st_uid, attr->statp.st_gid) < 0) {
225          Jmsg2(jcr, M_WARNING, 0, _("Unable to set file owner %s: ERR=%s\n"),
226             attr->ofname, strerror(errno));
227          stat = 0;
228       }
229    } else {
230       if (chown(attr->ofname, attr->statp.st_uid, attr->statp.st_gid) < 0) {
231          Jmsg2(jcr, M_WARNING, 0, _("Unable to set file owner %s: ERR=%s\n"),
232             attr->ofname, strerror(errno));
233          stat = 0;
234       }
235       if (chmod(attr->ofname, attr->statp.st_mode) < 0) {
236          Jmsg2(jcr, M_WARNING, 0, _("Unable to set file modes %s: ERR=%s\n"),
237             attr->ofname, strerror(errno));
238          stat = 0;
239       }
240
241       /* FreeBSD user flags */
242 #ifdef HAVE_CHFLAGS
243       if (chflags(attr->ofname, attr->statp.st_flags) < 0) {
244          Jmsg2(jcr, M_WARNING, 0, _("Unable to set file flags %s: ERR=%s\n"),
245             attr->ofname, strerror(errno));
246          stat = 0;
247       }
248 #endif
249       /*
250        * Reset file times.
251        */
252       if (utime(attr->ofname, &ut) < 0) {
253          Jmsg2(jcr, M_ERROR, 0, _("Unable to set file times %s: ERR=%s\n"),
254             attr->ofname, strerror(errno));
255          stat = 0;
256       }
257    }
258    umask(old_mask);
259    return stat;
260 }
261
262
263 /*=============================================================*/
264 /*                                                             */
265 /*                 * * *  U n i x * * * *                      */
266 /*                                                             */
267 /*=============================================================*/
268
269 #ifndef HAVE_CYGWIN
270     
271 /*
272  * It is possible to piggyback additional data e.g. ACLs on
273  *   the encode_stat() data by returning the extended attributes
274  *   here.  They must be "self-contained" (i.e. you keep track
275  *   of your own length), and they must be in ASCII string
276  *   format. Using this feature is not recommended.
277  * The code below shows how to return nothing.  See the Win32
278  *   code below for returning something in the attributes.
279  */
280 int encode_attribsEx(JCR *jcr, char *attribsEx, FF_PKT *ff_pkt)
281 {
282    *attribsEx = 0;                    /* no extended attributes */
283    return STREAM_UNIX_ATTRIBUTES;
284 }
285
286 #endif
287
288
289
290 /*=============================================================*/
291 /*                                                             */
292 /*                 * * *  W i n 3 2 * * * *                    */
293 /*                                                             */
294 /*=============================================================*/
295
296 #ifdef HAVE_CYGWIN
297
298 int encode_attribsEx(JCR *jcr, char *attribsEx, FF_PKT *ff_pkt)
299 {
300    char *p = attribsEx;
301    WIN32_FILE_ATTRIBUTE_DATA atts;
302    ULARGE_INTEGER li;
303
304    attribsEx[0] = 0;                  /* no extended attributes */
305
306    if (!p_GetFileAttributesEx) {
307       return STREAM_UNIX_ATTRIBUTES;
308    }
309
310    unix_name_to_win32(&ff_pkt->sys_fname, ff_pkt->fname);
311    if (!p_GetFileAttributesEx(ff_pkt->sys_fname, GetFileExInfoStandard,
312                             (LPVOID)&atts)) {
313       win_error(jcr, "GetFileAttributesEx:", ff_pkt->sys_fname);
314       return STREAM_UNIX_ATTRIBUTES_EX;
315    }
316
317    p += to_base64((uint64_t)atts.dwFileAttributes, p);
318    *p++ = ' ';                        /* separate fields with a space */
319    li.LowPart = atts.ftCreationTime.dwLowDateTime;
320    li.HighPart = atts.ftCreationTime.dwHighDateTime;
321    p += to_base64((uint64_t)li.QuadPart, p);
322    *p++ = ' ';
323    li.LowPart = atts.ftLastAccessTime.dwLowDateTime;
324    li.HighPart = atts.ftLastAccessTime.dwHighDateTime;
325    p += to_base64((uint64_t)li.QuadPart, p);
326    *p++ = ' ';
327    li.LowPart = atts.ftLastWriteTime.dwLowDateTime;
328    li.HighPart = atts.ftLastWriteTime.dwHighDateTime;
329    p += to_base64((uint64_t)li.QuadPart, p);
330    *p++ = ' ';
331    p += to_base64((uint64_t)atts.nFileSizeHigh, p);
332    *p++ = ' ';
333    p += to_base64((uint64_t)atts.nFileSizeLow, p);
334    *p = 0;
335    return STREAM_UNIX_ATTRIBUTES_EX;
336 }
337
338 /* Define attributes that are legal to set with SetFileAttributes() */
339 #define SET_ATTRS ( \
340          FILE_ATTRIBUTE_ARCHIVE| \
341          FILE_ATTRIBUTE_HIDDEN| \
342          FILE_ATTRIBUTE_NORMAL| \
343          FILE_ATTRIBUTE_NOT_CONTENT_INDEXED| \
344          FILE_ATTRIBUTE_OFFLINE| \
345          FILE_ATTRIBUTE_READONLY| \
346          FILE_ATTRIBUTE_SYSTEM| \
347          FILE_ATTRIBUTE_TEMPORARY)
348
349
350 /*
351  * Set Extended File Attributes for Win32
352  *
353  *  fname is the original filename  
354  *  ofile is the output filename (may be in a different directory)
355  *
356  * Returns:  1 on success
357  *           0 on failure
358  */
359 static
360 int set_win32_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
361 {
362    char *p = attr->attrEx;
363    int64_t val;
364    WIN32_FILE_ATTRIBUTE_DATA atts;
365    ULARGE_INTEGER li;
366    POOLMEM *win32_ofile;
367
368    if (!p || !*p) {                   /* we should have attributes */
369       Dmsg2(100, "Attributes missing. of=%s ofd=%d\n", attr->ofname, ofd->fid);
370       if (is_bopen(ofd)) {
371          bclose(ofd);
372       }
373       return 0;
374    } else {
375       Dmsg2(100, "Attribs %s = %s\n", attr->ofname, attr->attrEx);
376    }
377
378    p += from_base64(&val, p);
379    atts.dwFileAttributes = val;
380    p++;                               /* skip space */
381    p += from_base64(&val, p);
382    li.QuadPart = val;
383    atts.ftCreationTime.dwLowDateTime = li.LowPart;
384    atts.ftCreationTime.dwHighDateTime = li.HighPart;
385    p++;                               /* skip space */
386    p += from_base64(&val, p);
387    li.QuadPart = val;
388    atts.ftLastAccessTime.dwLowDateTime = li.LowPart;
389    atts.ftLastAccessTime.dwHighDateTime = li.HighPart;
390    p++;                               /* skip space */
391    p += from_base64(&val, p);
392    li.QuadPart = val;
393    atts.ftLastWriteTime.dwLowDateTime = li.LowPart;
394    atts.ftLastWriteTime.dwHighDateTime = li.HighPart;
395    p++;   
396    p += from_base64(&val, p);
397    atts.nFileSizeHigh = val;
398    p++;
399    p += from_base64(&val, p);
400    atts.nFileSizeLow = val;
401
402    /* Convert to Windows path format */
403    win32_ofile = get_pool_memory(PM_FNAME);
404    unix_name_to_win32(&win32_ofile, attr->ofname);
405
406
407
408    /* At this point, we have reconstructed the WIN32_FILE_ATTRIBUTE_DATA pkt */
409
410
411    if (!is_bopen(ofd)) {
412       Dmsg1(100, "File not open: %s\n", attr->ofname);
413       bopen(ofd, attr->ofname, O_WRONLY|O_BINARY, 0);   /* attempt to open the file */
414    }
415
416    if (is_bopen(ofd)) {
417       Dmsg1(100, "SetFileTime %s\n", attr->ofname);
418       if (!SetFileTime(bget_handle(ofd),
419                          &atts.ftCreationTime,
420                          &atts.ftLastAccessTime,
421                          &atts.ftLastWriteTime)) {
422          win_error(jcr, "SetFileTime:", win32_ofile);
423       }
424       bclose(ofd);
425    }
426
427    Dmsg1(100, "SetFileAtts %s\n", attr->ofname);
428    if (!(atts.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
429       if (!SetFileAttributes(win32_ofile, atts.dwFileAttributes & SET_ATTRS)) {
430          win_error(jcr, "SetFileAttributes:", win32_ofile);
431       }
432    }
433    free_pool_memory(win32_ofile);
434    return 1;
435 }
436
437 void win_error(JCR *jcr, char *prefix, POOLMEM *win32_ofile)
438 {
439    DWORD lerror = GetLastError();
440    LPTSTR msg;
441    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
442                  FORMAT_MESSAGE_FROM_SYSTEM,
443                  NULL,
444                  lerror,
445                  0,
446                  (LPTSTR)&msg,
447                  0,
448                  NULL);
449    Dmsg3(100, "Error in %s on file %s: ERR=%s\n", prefix, win32_ofile, msg);
450    strip_trailing_junk(msg);
451    Jmsg(jcr, M_INFO, 0, _("Error in %s file %s: ERR=%s\n"), prefix, win32_ofile, msg);
452    LocalFree(msg);
453 }
454
455 void win_error(JCR *jcr, char *prefix, DWORD lerror)
456 {
457    LPTSTR msg;
458    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
459                  FORMAT_MESSAGE_FROM_SYSTEM,
460                  NULL,
461                  lerror,
462                  0,
463                  (LPTSTR)&msg,
464                  0,
465                  NULL);
466    strip_trailing_junk(msg);
467    if (jcr) {
468       Jmsg2(jcr, M_INFO, 0, _("Error in %s: ERR=%s\n"), prefix, msg);
469    } else {
470       MessageBox(NULL, msg, prefix, MB_OK);
471    }
472    LocalFree(msg);
473 }
474
475
476 /* Cygwin API definition */
477 extern "C" void cygwin_conv_to_win32_path(const char *path, char *win32_path);
478
479 void unix_name_to_win32(POOLMEM **win32_name, char *name)
480 {
481    /* One extra byte should suffice, but we double it */
482    *win32_name = check_pool_memory_size(*win32_name, 2*strlen(name)+1);
483    cygwin_conv_to_win32_path(name, *win32_name);
484 }
485
486 #endif  /* HAVE_CYGWIN */