]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/bacl_freebsd.c
Update ACL/XATTR code and define new ACL/XATTR API for Plugins.
[bacula/bacula] / bacula / src / filed / bacl_freebsd.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2016 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  * Major refactoring of ACL code written by:
21  *
22  *  RadosÅ‚aw Korzeniewski, MMXVI
23  *  radoslaw@korzeniewski.net, radekk@inteos.pl
24  *  Inteos Sp. z o.o. http://www.inteos.pl/
25  *
26  */
27
28 #include "bacula.h"
29 #include "filed.h"
30 #include "bacl_freebsd.h"
31
32 #if defined(HAVE_FREEBSD_OS)
33
34 /* check if ACL support is enabled */
35 #if defined(HAVE_ACL)
36
37 /*
38  * Define the supported ACL streams for this OS
39  */
40 static const int os_acl_streams[] = {
41    STREAM_XACL_FREEBSD_ACCESS,
42    STREAM_XACL_FREEBSD_NFS4,
43    0
44 };
45
46 static const int os_default_acl_streams[] = {
47    STREAM_XACL_FREEBSD_DEFAULT,
48    0
49 };
50
51 /*
52  * OS specific constructor
53  */
54 BACL_FreeBSD::BACL_FreeBSD(){
55
56    set_acl_streams(os_acl_streams, os_default_acl_streams);
57 };
58
59 /*
60  * Translates Bacula internal acl representation into acl type
61  *
62  * in:
63  *    bacltype - internal Bacula acl type (BACL_type)
64  * out:
65  *    acl_type_t - os dependent acl type
66  *    when failed - ACL_TYPE_NONE is returned
67  */
68 acl_type_t BACL_FreeBSD::get_acltype(BACL_type bacltype){
69
70    acl_type_t acltype;
71
72    switch (bacltype){
73 #ifdef HAVE_ACL_TYPE_NFS4
74       case BACL_TYPE_NFS4:
75          acltype = ACL_TYPE_NFS4;
76          break;
77 #endif
78       case BACL_TYPE_ACCESS:
79          acltype = ACL_TYPE_ACCESS;
80          break;
81       case BACL_TYPE_DEFAULT:
82          acltype = ACL_TYPE_DEFAULT;
83          break;
84       default:
85          /*
86           * sanity check for acl's not supported by OS
87           */
88          acltype = (acl_type_t)ACL_TYPE_NONE;
89          break;
90    }
91    return acltype;
92 };
93
94 /*
95  * Counts a number of acl entries
96  *
97  * in:
98  *    acl - acl object
99  * out:
100  *    int - number of entries in acl object
101  *    when no acl entry available or any error then return zero '0'
102  */
103 int BACL_FreeBSD::acl_nrentries(acl_t acl){
104
105    int nr = 0;
106    acl_entry_t aclentry;
107    int rc;
108
109    rc = acl_get_entry(acl, ACL_FIRST_ENTRY, &aclentry);
110    while (rc == 1){
111       nr++;
112       rc = acl_get_entry(acl, ACL_NEXT_ENTRY, &aclentry);
113    }
114
115    return nr;
116 };
117
118 /*
119  * Checks if acl is simple.
120  *
121  * acl is simple if it has only the following entries:
122  * "user::",
123  * "group::",
124  * "other::"
125  *
126  * in:
127  *    acl - acl object
128  * out:
129  *    true - when acl object is simple
130  *    false - when acl object is not simple
131  */
132 bool BACL_FreeBSD::acl_issimple(acl_t acl){
133
134    acl_entry_t aclentry;
135    acl_tag_t acltag;
136    int rc;
137
138    rc = acl_get_entry(acl, ACL_FIRST_ENTRY, &aclentry);
139    while (rc == 1){
140       if (acl_get_tag_type(aclentry, &acltag) < 0){
141          return true;
142       }
143       /*
144        * Check for ACL_USER_OBJ, ACL_GROUP_OBJ or ACL_OTHER to find out.
145        */
146       if (acltag != ACL_USER_OBJ &&
147           acltag != ACL_GROUP_OBJ &&
148           acltag != ACL_OTHER){
149          return false;
150       }
151       rc = acl_get_entry(acl, ACL_NEXT_ENTRY, &aclentry);
152    }
153    return true;
154 };
155
156 /*
157  * Checks if ACL's are available for a specified file
158  *
159  * in:
160  *    jcr - Job Control Record
161  *    name - specifies the system variable to be queried
162  * out:
163  *    bRC_BACL_ok - check successful, lets setup bacltype variable
164  *    bRC_BACL_error -  in case of error
165  *    bRC_BACL_skip - you should skip all other routine
166  *    bRC_BACL_cont - you need to continue your routine
167  */
168 bRC_BACL BACL_FreeBSD::check_bacltype (JCR *jcr, int name){
169
170    int aclrc = 0;
171
172    aclrc = pathconf(jcr->last_fname, name);
173    switch (aclrc){
174       case -1: {
175          /* some error check why */
176          berrno be;
177          if (errno == ENOENT){
178             /* file does not exist skip it */
179             return bRC_BACL_skip;
180          } else {
181             Mmsg2(jcr->errmsg, _("pathconf error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
182             Dmsg2(100, "pathconf error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
183             return bRC_BACL_error;
184          }
185       }
186       case 0:
187          /* continue the routine */
188          return bRC_BACL_cont;
189       default:
190          break;
191    }
192    return bRC_BACL_ok;
193 };
194
195 /*
196  * Perform OS specific ACL backup
197  *
198  * in/out - check API at bacl.h
199  */
200 bRC_BACL BACL_FreeBSD::os_backup_acl (JCR *jcr, FF_PKT *ff_pkt){
201
202    bRC_BACL rc;
203    BACL_type bacltype = BACL_TYPE_NONE;
204
205 #if defined(_PC_ACL_NFS4)
206    /*
207     * Check if filesystem supports NFS4 acls.
208     */
209    rc = check_bacltype(jcr, _PC_ACL_NFS4);
210    switch (rc){
211       case bRC_BACL_ok:
212          bacltype = BACL_TYPE_NFS4;
213          break;
214       case bRC_BACL_skip:
215          return bRC_BACL_ok;
216       case bRC_BACL_cont:
217          break;
218       default:
219          /* errors */
220          return rc;
221    }
222 #endif
223    if (bacltype == BACL_TYPE_NONE){
224       /*
225        * Check if filesystem supports POSIX acls.
226        */
227       rc = check_bacltype(jcr, _PC_ACL_EXTENDED);
228       switch (rc){
229          case bRC_BACL_ok:
230             bacltype = BACL_TYPE_ACCESS;
231             break;
232          case bRC_BACL_skip:
233             return bRC_BACL_ok;
234          case bRC_BACL_cont:
235             break;
236          default:
237             /* errors */
238             return rc;
239       }
240    }
241
242    /* no ACL's available for file, so skip this filesystem */
243    if (bacltype == BACL_TYPE_NONE){
244       clear_flag(BACL_FLAG_NATIVE);
245       /*
246        * it is a bit of hardcore to clear a poolmemory with a NULL pointer,
247        * but it is working, hehe :)
248        * you may ask why it is working? it is simple, a pm_strcpy function is handling
249        * a null pointer with a substitiution of empty string.
250        */
251       set_content(NULL);
252       return bRC_BACL_ok;
253    }
254
255    switch (bacltype){
256       case BACL_TYPE_NFS4:
257          /*
258           * Read NFS4 ACLs
259           */
260          if (os_get_acl(jcr, BACL_TYPE_NFS4) == bRC_BACL_fatal)
261             return bRC_BACL_fatal;
262
263          if (get_content_len() > 0){
264             if (send_acl_stream(jcr, STREAM_XACL_FREEBSD_NFS4) == bRC_BACL_fatal)
265                return bRC_BACL_fatal;
266          }
267          break;
268       case BACL_TYPE_ACCESS:
269          /*
270           * Read access ACLs
271           */
272          if (os_get_acl(jcr, BACL_TYPE_ACCESS) == bRC_BACL_fatal)
273             return bRC_BACL_fatal;
274
275          if (get_content_len() > 0){
276             if (send_acl_stream(jcr, STREAM_XACL_FREEBSD_ACCESS) == bRC_BACL_fatal)
277                return bRC_BACL_fatal;
278          }
279
280          /*
281           * Directories can have default ACLs too
282           */
283          if (ff_pkt->type == FT_DIREND){
284             if (os_get_acl(jcr, BACL_TYPE_DEFAULT) == bRC_BACL_fatal)
285                return bRC_BACL_fatal;
286             if (get_content_len() > 0){
287                if (send_acl_stream(jcr, STREAM_XACL_FREEBSD_DEFAULT) == bRC_BACL_fatal)
288                   return bRC_BACL_fatal;
289             }
290          }
291          break;
292       default:
293          break;
294    }
295
296    return bRC_BACL_ok;
297 };
298
299 /*
300  * Perform OS specific ACL restore
301  *
302  * in/out - check API at bacl.h
303  */
304 bRC_BACL BACL_FreeBSD::os_restore_acl (JCR *jcr, int stream, char *content, uint32_t length){
305
306    int aclrc = 0;
307    const char *acl_type_name;
308
309    switch (stream){
310       case STREAM_UNIX_ACCESS_ACL:
311       case STREAM_XACL_FREEBSD_ACCESS:
312       case STREAM_UNIX_DEFAULT_ACL:
313       case STREAM_XACL_FREEBSD_DEFAULT:
314          aclrc = pathconf(jcr->last_fname, _PC_ACL_EXTENDED);
315          acl_type_name = "POSIX";
316          break;
317       case STREAM_XACL_FREEBSD_NFS4:
318 #if defined(_PC_ACL_NFS4)
319          aclrc = pathconf(jcr->last_fname, _PC_ACL_NFS4);
320 #endif
321          acl_type_name = "NFS4";
322          break;
323       default:
324          acl_type_name = "unknown";
325          break;
326    }
327
328    switch (aclrc){
329       case -1: {
330          berrno be;
331
332          switch (errno){
333             case ENOENT:
334                return bRC_BACL_ok;
335             default:
336                Mmsg2(jcr->errmsg, _("pathconf error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
337                Dmsg3(100, "pathconf error acl=%s file=%s ERR=%s\n", content, jcr->last_fname, be.bstrerror());
338                return bRC_BACL_error;
339          }
340       }
341       case 0:
342          clear_flag(BACL_FLAG_NATIVE);
343          Mmsg2(jcr->errmsg, _("Trying to restore acl on file \"%s\" on filesystem without %s acl support\n"), jcr->last_fname, acl_type_name);
344          return bRC_BACL_error;
345       default:
346          break;
347    }
348
349    switch (stream){
350       case STREAM_UNIX_ACCESS_ACL:
351       case STREAM_XACL_FREEBSD_ACCESS:
352          return os_set_acl(jcr, BACL_TYPE_ACCESS, content, length);
353       case STREAM_UNIX_DEFAULT_ACL:
354       case STREAM_XACL_FREEBSD_DEFAULT:
355          return os_set_acl(jcr, BACL_TYPE_DEFAULT, content, length);
356       case STREAM_XACL_FREEBSD_NFS4:
357          return os_set_acl(jcr, BACL_TYPE_NFS4, content, length);
358       default:
359          break;
360    }
361    return bRC_BACL_error;
362 };
363
364 /*
365  * Low level OS specific runtime to get ACL data from file.
366  * The ACL data is set in internal content buffer
367  *
368  * in/out - check API at bacl.h
369  */
370 bRC_BACL BACL_FreeBSD::os_get_acl(JCR *jcr, BACL_type bacltype){
371
372    acl_t acl;
373    acl_type_t acltype;
374    char *acltext;
375    bRC_BACL rc = bRC_BACL_ok;
376
377    acltype = get_acltype(bacltype);
378    acl = acl_get_file(jcr->last_fname, acltype);
379
380    if (acl){
381       Dmsg1(400, "OS_ACL read from file: %s\n",jcr->last_fname);
382       if (acl_nrentries(acl) == 0){
383          goto bail_out;
384       }
385
386       /* check for simple ACL which correspond to standard permissions only */
387       if (bacltype == BACL_TYPE_ACCESS && acl_issimple(acl)){
388          goto bail_out;
389       }
390
391 #if defined(_PC_ACL_NFS4)
392       if (bacltype == BACL_TYPE_NFS4){
393          int trivial;
394          if (acl_is_trivial_np(acl, &trivial) == 0){
395             if (trivial == 1){
396                goto bail_out;
397             }
398          }
399       }
400 #endif
401
402       if ((acltext = acl_to_text(acl, NULL)) != NULL){
403          set_content(acltext);
404          acl_free(acl);
405          acl_free(acltext);
406          return bRC_BACL_ok;
407       }
408
409       berrno be;
410
411       Mmsg2(jcr->errmsg, _("acl_to_text error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
412       Dmsg2(100, "acl_to_text error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
413
414       rc = bRC_BACL_error;
415
416    } else {
417       berrno be;
418
419       switch (errno){
420       case EOPNOTSUPP:
421          /* fs does not support acl, skip it */
422          Dmsg0(400, "Wow, ACL is not supported on this filesystem\n");
423          clear_flag(BACL_FLAG_NATIVE);
424          break;
425       case ENOENT:
426          break;
427       default:
428          /* Some real error */
429          Mmsg2(jcr->errmsg, _("acl_get_file error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
430          Dmsg2(100, "acl_get_file error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
431          rc = bRC_BACL_error;
432          break;
433       }
434    }
435
436 bail_out:
437    if (acl){
438       acl_free(acl);
439    }
440    /*
441     * it is a bit of hardcore to clear a pool memory with a NULL pointer,
442     * but it is working, hehe :)
443     * you may ask why it is working? it is simple, a pm_strcpy function is handling
444     * a null pointer with a substitution of empty string.
445     */
446    set_content(NULL);
447    return rc;
448 };
449
450 /*
451  * Low level OS specific runtime to set ACL data on file
452  *
453  * in/out - check API at bacl.h
454  */
455 bRC_BACL BACL_FreeBSD::os_set_acl(JCR *jcr, BACL_type bacltype, char *content, uint32_t length){
456
457    acl_t acl;
458    acl_type_t acltype;
459
460    acltype = get_acltype(bacltype);
461    if (acltype == ACL_TYPE_DEFAULT && length == 0){
462       /* delete ACl from file when no acl data available for default acl's */
463       if (acl_delete_def_file(jcr->last_fname) == 0){
464          return bRC_BACL_ok;
465       }
466
467       berrno be;
468       switch (errno){
469          case ENOENT:
470             return bRC_BACL_ok;
471          case ENOTSUP:
472             /*
473              * If the filesystem reports it doesn't support acl's we clear the
474              * BACL_FLAG_NATIVE flag so we skip ACL restores on all other files
475              * on the same filesystem. The BACL_FLAG_NATIVE flag gets set again
476              * when we change from one filesystem to an other.
477              */
478             clear_flag(BACL_FLAG_NATIVE);
479             Mmsg(jcr->errmsg, _("acl_delete_def_file error on file \"%s\": filesystem doesn't support ACLs\n"), jcr->last_fname);
480             return bRC_BACL_error;
481          default:
482             Mmsg2(jcr->errmsg, _("acl_delete_def_file error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
483             return bRC_BACL_error;
484       }
485    }
486
487    acl = acl_from_text(content);
488    if (acl == NULL){
489       berrno be;
490
491       Mmsg2(jcr->errmsg, _("acl_from_text error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
492       Dmsg3(100, "acl_from_text error acl=%s file=%s ERR=%s\n", content, jcr->last_fname, be.bstrerror());
493       return bRC_BACL_error;
494    }
495
496    /*
497     * Restore the ACLs, but don't complain about links which really should
498     * not have attributes, and the file it is linked to may not yet be restored.
499     * This is only true for the old acl streams as in the new implementation we
500     * don't save acls of symlinks (which cannot have acls anyhow)
501     */
502    if (acl_set_file(jcr->last_fname, acltype, acl) != 0 && jcr->last_type != FT_LNK){
503       berrno be;
504       switch (errno){
505       case ENOENT:
506          acl_free(acl);
507          return bRC_BACL_ok;
508       case ENOTSUP:
509          /*
510           * If the filesystem reports it doesn't support ACLs we clear the
511           * BACL_FLAG_NATIVE flag so we skip ACL restores on all other files
512           * on the same filesystem. The BACL_FLAG_NATIVE flag gets set again
513           * when we change from one filesystem to an other.
514           */
515          clear_flag(BACL_FLAG_NATIVE);
516          Mmsg(jcr->errmsg, _("acl_set_file error on file \"%s\": filesystem doesn't support ACLs\n"), jcr->last_fname);
517          Dmsg2(100, "acl_set_file error acl=%s file=%s filesystem doesn't support ACLs\n", content, jcr->last_fname);
518          acl_free(acl);
519          return bRC_BACL_error;
520       default:
521          Mmsg2(jcr->errmsg, _("acl_set_file error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
522          Dmsg3(100, "acl_set_file error acl=%s file=%s ERR=%s\n", content, jcr->last_fname, be.bstrerror());
523          acl_free(acl);
524          return bRC_BACL_error;
525       }
526    }
527    acl_free(acl);
528    return bRC_BACL_ok;
529 };
530
531 #endif /* HAVE_ACL */
532
533 #endif /* HAVE_FREEBSD_OS */