]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/findlib/attribs.c
Win32 tweaks -- kes25Nov02
[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, 2001, 2002 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 #include "jcr.h"
34
35 #ifdef HAVE_CYGWIN
36 #include <windows.h>
37
38 /* Forward referenced subroutines */
39 static
40 int set_win32_attributes(void *jcr, char *fname, char *ofile, char *lname, 
41                          int type, int stream, struct stat *statp,
42                          char *attribsEx, int *ofd);
43 void unix_name_to_win32(POOLMEM **win32_name, char *name);
44 extern "C" HANDLE get_osfhandle(int fd);
45 void win_error(void *jcr, char *prefix, POOLMEM *ofile);
46 #endif
47
48
49 /*=============================================================*/
50 /*                                                             */
51 /*             ***  A l l  S y s t e m s ***                   */
52 /*                                                             */
53 /*=============================================================*/
54
55
56 /* Encode a stat structure into a base64 character string */
57 void encode_stat(char *buf, struct stat *statp)
58 {
59    char *p = buf;
60    /*
61     * NOTE: we should use rdev as major and minor device if
62     * it is a block or char device (S_ISCHR(statp->st_mode)
63     * or S_ISBLK(statp->st_mode)).  In all other cases,
64     * it is not used.   
65     *
66     */
67    p += to_base64((int64_t)statp->st_dev, p);
68    *p++ = ' ';                        /* separate fields with a space */
69    p += to_base64((int64_t)statp->st_ino, p);
70    *p++ = ' ';
71    p += to_base64((int64_t)statp->st_mode, p);
72    *p++ = ' ';
73    p += to_base64((int64_t)statp->st_nlink, p);
74    *p++ = ' ';
75    p += to_base64((int64_t)statp->st_uid, p);
76    *p++ = ' ';
77    p += to_base64((int64_t)statp->st_gid, p);
78    *p++ = ' ';
79    p += to_base64((int64_t)statp->st_rdev, p);
80    *p++ = ' ';
81    p += to_base64((int64_t)statp->st_size, p);
82    *p++ = ' ';
83    p += to_base64((int64_t)statp->st_blksize, p);
84    *p++ = ' ';
85    p += to_base64((int64_t)statp->st_blocks, p);
86    *p++ = ' ';
87    p += to_base64((int64_t)statp->st_atime, p);
88    *p++ = ' ';
89    p += to_base64((int64_t)statp->st_mtime, p);
90    *p++ = ' ';
91    p += to_base64((int64_t)statp->st_ctime, p);
92    *p = 0;
93    return;
94 }
95
96
97
98 /* Decode a stat packet from base64 characters */
99 void
100 decode_stat(char *buf, struct stat *statp)
101 {
102    char *p = buf;
103    int64_t val;
104
105    p += from_base64(&val, p);
106    statp->st_dev = val;
107    p++;                               /* skip space */
108    p += from_base64(&val, p);
109    statp->st_ino = val;
110    p++;
111    p += from_base64(&val, p);
112    statp->st_mode = val;
113    p++;
114    p += from_base64(&val, p);
115    statp->st_nlink = val;
116    p++;
117    p += from_base64(&val, p);
118    statp->st_uid = val;
119    p++;
120    p += from_base64(&val, p);
121    statp->st_gid = val;
122    p++;
123    p += from_base64(&val, p);
124    statp->st_rdev = val;
125    p++;
126    p += from_base64(&val, p);
127    statp->st_size = val;
128    p++;
129    p += from_base64(&val, p);
130    statp->st_blksize = val;
131    p++;
132    p += from_base64(&val, p);
133    statp->st_blocks = val;
134    p++;
135    p += from_base64(&val, p);
136    statp->st_atime = val;
137    p++;
138    p += from_base64(&val, p);
139    statp->st_mtime = val;
140    p++;
141    p += from_base64(&val, p);
142    statp->st_ctime = val;
143 }
144
145 /*
146  * Set file modes, permissions and times
147  *
148  *  fname is the original filename  
149  *  ofile is the output filename (may be in a different directory)
150  *
151  * Returns:  1 on success
152  *           0 on failure
153  */
154 int set_attributes(void *jcr, char *fname, char *ofile, char *lname, 
155                    int type, int stream, struct stat *statp,
156                    char *attribsEx, int *ofd)
157 {
158    struct utimbuf ut;    
159
160 #ifdef HAVE_CYGWIN
161    if (set_win32_attributes(jcr, fname, ofile, lname, type, stream,
162                             statp, attribsEx, ofd)) {
163       return 1;
164    }
165    /*
166     * If Windows stuff failed, e.g. attempt to restore Unix file
167     *  to Windows, simply fall through and we will do it the     
168     *  universal way.
169     */
170 #endif
171
172    if (*ofd != -1) {
173       close(*ofd);                    /* first close file */
174       *ofd = -1;
175    }
176
177    ut.actime = statp->st_atime;
178    ut.modtime = statp->st_mtime;
179
180    /* ***FIXME**** optimize -- don't do if already correct */
181    if (type == FT_LNK) {
182       if (lchown(ofile, statp->st_uid, statp->st_gid) < 0) {
183          Jmsg2(jcr, M_ERROR, 0, "Unable to set file owner %s: ERR=%s\n",
184             ofile, strerror(errno));
185          return 0;
186       }
187    } else {
188       if (chown(ofile, statp->st_uid, statp->st_gid) < 0) {
189          Jmsg2(jcr, M_ERROR, 0, "Unable to set file owner %s: ERR=%s\n",
190             ofile, strerror(errno));
191          return 0;
192       }
193    }
194    if (chmod(ofile, statp->st_mode) < 0) {
195       Jmsg2(jcr, M_ERROR, 0, "Unable to set file modes %s: ERR=%s\n",
196          ofile, strerror(errno));
197       return 0;
198    }
199
200    /*
201     * Update file times.
202     */
203    if (utime(ofile, &ut) < 0) {
204       Jmsg2(jcr, M_ERROR, 0, "Unable to set file times %s: ERR=%s\n",
205          ofile, strerror(errno));
206       return 0;
207    }
208    return 1;
209 }
210
211
212 /*=============================================================*/
213 /*                                                             */
214 /*                 * * *  U n i x * * * *                      */
215 /*                                                             */
216 /*=============================================================*/
217
218 #ifndef HAVE_CYGWIN
219     
220 /*
221  * If you have a Unix system with extended attributes (e.g.
222  *  ACLs for Solaris, do it here.
223  */
224 int encode_attribsEx(void *jcr, char *attribsEx, FF_PKT *ff_pkt)
225 {
226    *attribsEx = 0;                    /* no extended attributes */
227    return STREAM_UNIX_ATTRIBUTES;
228 }
229
230 #endif
231
232
233
234 /*=============================================================*/
235 /*                                                             */
236 /*                 * * *  W i n 3 2 * * * *                    */
237 /*                                                             */
238 /*=============================================================*/
239
240 #ifdef HAVE_CYGWIN
241
242 int encode_attribsEx(void *jcr, char *attribsEx, FF_PKT *ff_pkt)
243 {
244    char *p = attribsEx;
245    WIN32_FILE_ATTRIBUTE_DATA atts;
246    ULARGE_INTEGER li;
247
248    attribsEx[0] = 0;                  /* no extended attributes */
249
250    unix_name_to_win32(&ff_pkt->sys_fname, ff_pkt->fname);
251    if (!GetFileAttributesEx(ff_pkt->sys_fname, GetFileExInfoStandard,
252                             (LPVOID)&atts)) {
253       win_error(jcr, "GetFileAttributesEx:", ff_pkt->sys_fname);
254       return STREAM_WIN32_ATTRIBUTES;
255    }
256
257    p += to_base64((uint64_t)atts.dwFileAttributes, p);
258    *p++ = ' ';                        /* separate fields with a space */
259    li.LowPart = atts.ftCreationTime.dwLowDateTime;
260    li.HighPart = atts.ftCreationTime.dwHighDateTime;
261    p += to_base64((uint64_t)li.QuadPart, p);
262    *p++ = ' ';
263    li.LowPart = atts.ftLastAccessTime.dwLowDateTime;
264    li.HighPart = atts.ftLastAccessTime.dwHighDateTime;
265    p += to_base64((uint64_t)li.QuadPart, p);
266    *p++ = ' ';
267    li.LowPart = atts.ftLastWriteTime.dwLowDateTime;
268    li.HighPart = atts.ftLastWriteTime.dwHighDateTime;
269    p += to_base64((uint64_t)li.QuadPart, p);
270    *p++ = ' ';
271    p += to_base64((uint64_t)atts.nFileSizeHigh, p);
272    *p++ = ' ';
273    p += to_base64((uint64_t)atts.nFileSizeLow, p);
274    *p = 0;
275    return STREAM_WIN32_ATTRIBUTES;
276 }
277
278 /* Define attributes that are legal to set with SetFileAttributes() */
279 #define SET_ATTRS ( \
280          FILE_ATTRIBUTE_ARCHIVE| \
281          FILE_ATTRIBUTE_HIDDEN| \
282          FILE_ATTRIBUTE_NORMAL| \
283          FILE_ATTRIBUTE_NOT_CONTENT_INDEXED| \
284          FILE_ATTRIBUTE_OFFLINE| \
285          FILE_ATTRIBUTE_READONLY| \
286          FILE_ATTRIBUTE_SYSTEM| \
287          FILE_ATTRIBUTE_TEMPORARY)
288
289
290 /*
291  * Set Extended File Attributes for Win32
292  *
293  *  fname is the original filename  
294  *  ofile is the output filename (may be in a different directory)
295  *
296  * Returns:  1 on success
297  *           0 on failure
298  */
299 static
300 int set_win32_attributes(void *jcr, char *fname, char *ofile, char *lname, 
301                          int type, int stream, struct stat *statp,
302                          char *attribsEx, int *ofd)
303 {
304    char *p = attribsEx;
305    int64_t val;
306    WIN32_FILE_ATTRIBUTE_DATA atts;
307    ULARGE_INTEGER li;
308    int fid, stat;
309    POOLMEM *win32_ofile;
310
311    if (!p || !*p) {                   /* we should have attributes */
312       Dmsg2(100, "Attributes missing. of=%s ofd=%d\n", ofile, *ofd);
313       if (*ofd != -1) {
314          close(*ofd);
315          *ofd = -1;
316       }
317       return 0;
318    } else {
319       Dmsg2(100, "Attribs %s = %s\n", ofile, attribsEx);
320    }
321
322    p += from_base64(&val, p);
323    atts.dwFileAttributes = val;
324    p++;                               /* skip space */
325    p += from_base64(&val, p);
326    li.QuadPart = val;
327    atts.ftCreationTime.dwLowDateTime = li.LowPart;
328    atts.ftCreationTime.dwHighDateTime = li.HighPart;
329    p++;                               /* skip space */
330    p += from_base64(&val, p);
331    li.QuadPart = val;
332    atts.ftLastAccessTime.dwLowDateTime = li.LowPart;
333    atts.ftLastAccessTime.dwHighDateTime = li.HighPart;
334    p++;                               /* skip space */
335    p += from_base64(&val, p);
336    li.QuadPart = val;
337    atts.ftLastWriteTime.dwLowDateTime = li.LowPart;
338    atts.ftLastWriteTime.dwHighDateTime = li.HighPart;
339    p++;   
340    p += from_base64(&val, p);
341    atts.nFileSizeHigh = val;
342    p++;
343    p += from_base64(&val, p);
344    atts.nFileSizeLow = val;
345
346    /* At this point, we have reconstructed the WIN32_FILE_ATTRIBUTE_DATA pkt */
347
348    /* Convert to Windows path format */
349    win32_ofile = get_pool_memory(PM_FNAME);
350    unix_name_to_win32(&win32_ofile, ofile);
351
352    if (*ofd == -1) {
353       Dmsg1(100, "File not open: %s\n", ofile);
354       fid = open(ofile, O_RDWR|O_BINARY);   /* attempt to open the file */
355       if (fid >= 0) {
356          *ofd = fid;
357       }
358    }
359
360    if (*ofd != -1) {
361       Dmsg1(100, "SetFileTime %s\n", ofile);
362       stat = SetFileTime(get_osfhandle(*ofd),
363                          &atts.ftCreationTime,
364                          &atts.ftLastAccessTime,
365                          &atts.ftLastWriteTime);
366       if (stat != 1) {
367          win_error(jcr, "SetFileTime:", win32_ofile);
368       }
369       close(*ofd);
370       *ofd = -1;
371    }
372
373    Dmsg1(100, "SetFileAtts %s\n", ofile);
374    stat = SetFileAttributes(win32_ofile, atts.dwFileAttributes & SET_ATTRS);
375    if (stat != 1) {
376       win_error(jcr, "SetFileAttributes:", win32_ofile);
377    }
378    free_pool_memory(win32_ofile);
379    return 1;
380 }
381
382 void win_error(void *vjcr, char *prefix, POOLMEM *win32_ofile)
383 {
384    JCR *jcr = (JCR *)vjcr; 
385    DWORD lerror = GetLastError();
386    LPTSTR msg;
387    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
388                  FORMAT_MESSAGE_FROM_SYSTEM,
389                  NULL,
390                  lerror,
391                  0,
392                  (LPTSTR)&msg,
393                  0,
394                  NULL);
395    Dmsg3(100, "Error in %s on file %s: ERR=%s\n", prefix, win32_ofile, msg);
396    Jmsg3(jcr, M_INFO, 0, _("Error in %s file %s: ERR=%s\n"), prefix, win32_ofile, msg);
397    LocalFree(msg);
398 }
399
400 /* Cygwin API definition */
401 extern "C" void cygwin_conv_to_win32_path(const char *path, char *win32_path);
402
403 void unix_name_to_win32(POOLMEM **win32_name, char *name)
404 {
405    /* One extra byte should suffice, but we take 10 */
406    *win32_name = check_pool_memory_size(*win32_name, strlen(name)+10);
407    cygwin_conv_to_win32_path(name, *win32_name);
408 }
409
410 #endif  /* HAVE_CYGWIN */