wma: Trivial whitespace fix.
[paraslash.git] / attribute.c
1 /*
2  * Copyright (C) 1997-2012 Andre Noll <maan@systemlinux.org>
3  *
4  * Licensed under the GPL v2. For licencing details see COPYING.
5  */
6
7 /** \file attribute.c Attribute handling functions. */
8
9 #include <regex.h>
10 #include <osl.h>
11
12 #include "para.h"
13 #include "error.h"
14 #include "crypt.h"
15 #include "string.h"
16 #include "afh.h"
17 #include "afs.h"
18 #include "ipc.h"
19 #include "command.h"
20
21 static struct osl_table *attribute_table;
22 static int greatest_att_bitnum;
23
24 /** The columns of the attribute table. */
25 enum attribute_table_columns {
26         /** The bit number (0-63). */
27         ATTCOL_BITNUM,
28         /** The name of the attribute. */
29         ATTCOL_NAME,
30         /** Number of columns in this table. */
31         NUM_ATT_COLUMNS
32 };
33
34 static int char_compare(const struct osl_object *obj1, const struct osl_object *obj2)
35 {
36         const unsigned char *c1 = (const unsigned char*)obj1->data;
37         const unsigned char *c2 = (const unsigned char*)obj2->data;
38         if (*c1 > *c2)
39                 return 1;
40         if (*c1 < *c2)
41                 return -1;
42         return 0;
43 }
44
45 static struct osl_column_description att_cols[] = {
46         [ATTCOL_BITNUM] = {
47                 .storage_type = OSL_MAPPED_STORAGE,
48                 .storage_flags = OSL_RBTREE | OSL_FIXED_SIZE | OSL_UNIQUE,
49                 .name = "bitnum",
50                 .compare_function = char_compare,
51                 .data_size = 1
52         },
53         [ATTCOL_NAME] = {
54                 .storage_type = OSL_MAPPED_STORAGE,
55                 .storage_flags = OSL_RBTREE | OSL_UNIQUE,
56                 .name = "name",
57                 .compare_function = string_compare,
58         }
59 };
60
61 static struct osl_table_description attribute_table_desc = {
62         .name = "attributes",
63         .num_columns = NUM_ATT_COLUMNS,
64         .flags = 0,
65         .column_descriptions = att_cols
66 };
67
68 static void find_greatest_att_bitnum(void)
69 {
70         unsigned char c = 63;
71         do {
72                 struct osl_row *row;
73                 struct osl_object obj = {.data = &c, .size = 1};
74                 if (osl_get_row(attribute_table, ATTCOL_BITNUM, &obj,
75                                 &row) >= 0) {
76                         greatest_att_bitnum = c;
77                         return;
78                 }
79         } while (c--);
80         PARA_INFO_LOG("no attributes\n");
81         greatest_att_bitnum = -E_NO_ATTRIBUTES;
82 }
83
84 /**
85  * Retrieve the identifier (number) of an attribute.
86  *
87  * \param att_name The name of the attribute.
88  * \param bitnum Result pointer.
89  *
90  * \return Positive on success, negative on errors.
91  */
92 int get_attribute_bitnum_by_name(const char *att_name, unsigned char *bitnum)
93 {
94         struct osl_object obj = {.data = (char *)att_name,
95                 .size = strlen(att_name) + 1};
96         struct osl_row *row;
97         int ret = osl(osl_get_row(attribute_table, ATTCOL_NAME, &obj, &row));
98
99         if (ret < 0)
100                 return ret;
101         ret = osl(osl_get_object(attribute_table, row, ATTCOL_BITNUM, &obj));
102         if (ret < 0)
103                 return ret;
104         *bitnum = *(unsigned char *)obj.data;
105         return 1;
106 }
107
108 /**
109  * Flags used by the lsatt command.
110  *
111  * \param \sa com_lsatt().
112  */
113 enum lsatt_flags {
114         /** Whether "-a" was given for the lsatt command. */
115         LSATT_FLAG_SORT_BY_ID = 1,
116         /** Whether "-l" was given for the lsatt command. */
117         LSATT_FLAG_LONG = 2,
118         /** Reverse sort order. */
119         LSATT_FLAG_REVERSE = 4
120 };
121
122 /** Data passed to the action function of lsatt */
123 struct lsatt_action_data {
124         /** The result buffer. */
125         struct para_buffer pb;
126         /** The given flags for the lsatt command. */
127         unsigned flags;
128 };
129
130 static int print_attribute(struct osl_table *table, struct osl_row *row,
131                 const char *name, void *data)
132 {
133         struct lsatt_action_data *laad = data;
134         struct osl_object bitnum_obj;
135         int ret;
136
137         if (!(laad->flags & LSATT_FLAG_LONG))
138                 return para_printf(&laad->pb, "%s\n", name);
139         ret = osl(osl_get_object(table, row, ATTCOL_BITNUM, &bitnum_obj));
140         if (ret < 0) {
141                 para_printf(&laad->pb, "%s: %s\n", name, para_strerror(-ret));
142                 return ret;
143         }
144         return para_printf(&laad->pb, "%u\t%s\n", *(unsigned char*)bitnum_obj.data,
145                 name);
146 }
147
148 static void com_lsatt_callback(int fd, const struct osl_object *query)
149 {
150         struct lsatt_action_data laad = {
151                 .flags = *(unsigned *) query->data,
152                 .pb = {
153                         .max_size = shm_get_shmmax(),
154                         .private_data = &fd,
155                         .max_size_handler = pass_buffer_as_shm
156                 }
157
158         };
159         struct pattern_match_data pmd = {
160                 .table = attribute_table,
161                 .loop_col_num = ATTCOL_BITNUM,
162                 .match_col_num = ATTCOL_NAME,
163                 .patterns = {.data = (char *)query->data + sizeof(laad.flags),
164                         .size = query->size - sizeof(laad.flags)},
165                 .pm_flags = PM_NO_PATTERN_MATCHES_EVERYTHING,
166                 .data = &laad,
167                 .action = print_attribute
168         };
169         if (laad.flags & LSATT_FLAG_SORT_BY_ID)
170                 pmd.loop_col_num = ATTCOL_NAME;
171         if (laad.flags & LSATT_FLAG_REVERSE)
172                 pmd.pm_flags |= PM_REVERSE_LOOP;
173         for_each_matching_row(&pmd);
174         if (laad.pb.offset)
175                 pass_buffer_as_shm(laad.pb.buf, laad.pb.offset, &fd);
176         free(laad.pb.buf);
177 }
178
179 int com_lsatt(struct command_context *cc)
180 {
181         unsigned flags = 0;
182         struct osl_object options = {.data = &flags, .size = sizeof(flags)};
183         int ret, i;
184
185         for (i = 1; i < cc->argc; i++) {
186                 const char *arg = cc->argv[i];
187                 if (arg[0] != '-')
188                         break;
189                 if (!strcmp(arg, "--")) {
190                         i++;
191                         break;
192                 }
193                 if (!strcmp(arg, "-i")) {
194                         flags |= LSATT_FLAG_SORT_BY_ID;
195                         continue;
196                 }
197                 if (!strcmp(arg, "-l")) {
198                         flags |= LSATT_FLAG_LONG;
199                         continue;
200                 }
201                 if (!strcmp(arg, "-r")) {
202                         flags |= LSATT_FLAG_REVERSE;
203                         continue;
204                 }
205         }
206         ret = send_option_arg_callback_request(&options, cc->argc - i, cc->argv + i,
207                 com_lsatt_callback, sc_send_result, cc);
208         if (!ret) {
209                 if (cc->argc > 1)
210                         ret = sc_send_va_buffer(&cc->scc, "no matches\n");
211         } else if (ret < 0)
212                 sc_send_va_buffer(&cc->scc, "%s\n", para_strerror(-ret));
213         return ret;
214 }
215
216 static void com_setatt_callback(__a_unused int fd, const struct osl_object *query)
217 {
218         char *p;
219         uint64_t add_mask = 0, del_mask = 0;
220         int ret;
221         size_t len;
222         struct osl_object obj;
223         struct osl_row *row;
224
225         for (p = query->data; p < (char *)query->data + query->size; p += len + 1) {
226                 char c;
227
228                 len = strlen(p);
229                 ret = -E_ATTR_SYNTAX;
230                 if (!*p)
231                         goto out;
232                 c = p[len - 1];
233                 if (c != '+' && c != '-')
234                         break;
235                 p[len - 1] = '\0';
236                 obj.data = p;
237                 obj.size = len + 1;
238                 ret = osl(osl_get_row(attribute_table, ATTCOL_NAME, &obj, &row));
239                 if (ret < 0)
240                         goto out;
241                 ret = osl(osl_get_object(attribute_table, row, ATTCOL_BITNUM,
242                         &obj));
243                 if (ret < 0)
244                         goto out;
245                 if (c == '+')
246                         add_mask |= (1UL << *(unsigned char *)obj.data);
247                 else
248                         del_mask |= (1UL << *(unsigned char *)obj.data);
249         }
250         ret = -E_ATTR_SYNTAX;
251         if (!add_mask && !del_mask)
252                 goto out;
253         PARA_DEBUG_LOG("masks: %llx:%llx\n",(long long unsigned)add_mask,
254                 (long long unsigned)del_mask);
255         for (; p < (char *)query->data + query->size; p += len + 1) { /* TODO: fnmatch */
256                 struct afs_info old_afsi, new_afsi;
257                 struct afsi_change_event_data aced = {.old_afsi = &old_afsi};
258
259                 len = strlen(p);
260                 ret = aft_get_row_of_path(p, &aced.aft_row);
261                 if (ret < 0)
262                         goto out;
263                 ret = get_afsi_object_of_row(aced.aft_row, &obj);
264                 if (ret < 0)
265                         goto out;
266                 ret = load_afsi(&old_afsi, &obj);
267                 if (ret < 0)
268                         goto out;
269                 new_afsi = old_afsi;
270                 new_afsi.attributes |= add_mask;
271                 new_afsi.attributes &= ~del_mask;
272                 save_afsi(&new_afsi, &obj); /* in-place update */
273                 afs_event(AFSI_CHANGE, NULL, &aced);
274         }
275 out:
276         if (ret < 0)
277                 PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
278 }
279
280 int com_setatt(struct command_context *cc)
281 {
282         if (cc->argc < 3)
283                 return -E_ATTR_SYNTAX;
284         return send_standard_callback_request(cc->argc - 1, cc->argv + 1,
285                 com_setatt_callback, NULL, NULL);
286 }
287
288 struct addatt_event_data {
289         const char *name;
290         unsigned char bitnum;
291 };
292
293
294 static void com_addatt_callback(int fd, const struct osl_object *query)
295 {
296         char *p;
297         int ret = 1, ret2 = 0;
298         struct para_buffer pb = {
299                 .max_size = shm_get_shmmax(),
300                 .private_data = &fd,
301                 .max_size_handler = pass_buffer_as_shm
302         };
303         size_t len;
304
305         for (p = query->data; p < (char *)query->data + query->size; p += len + 1) {
306                 struct osl_object objs[NUM_ATT_COLUMNS];
307                 struct osl_row *row;
308                 unsigned char bitnum;
309                 struct addatt_event_data aed;
310
311                 len = strlen(p);
312                 if (!len || p[len - 1] == '-' || p[len - 1] == '+') {
313                         ret2 = para_printf(&pb, "invalid attribute name: %s\n", p);
314                         if (ret2 < 0)
315                                 goto out;
316                         continue;
317                 }
318                 ret = get_attribute_bitnum_by_name(p, &bitnum);
319                 if (ret >= 0) {
320                         ret2 = para_printf(&pb, "attribute \"%s\" already exists\n", p);
321                         if (ret2 < 0)
322                                 goto out;
323                         continue;
324                 }
325                 if (ret != -OSL_ERRNO_TO_PARA_ERROR(E_OSL_RB_KEY_NOT_FOUND)) /* error */
326                         goto out;
327                 objs[ATTCOL_BITNUM].size = 1;
328                 /* find smallest unused attribute */
329                 for (bitnum = 0; bitnum < 64; bitnum++) {
330                         objs[ATTCOL_BITNUM].data = &bitnum;
331                         ret = osl(osl_get_row(attribute_table, ATTCOL_BITNUM,
332                                 &objs[ATTCOL_BITNUM], &row));
333                         if (ret == -OSL_ERRNO_TO_PARA_ERROR(E_OSL_RB_KEY_NOT_FOUND))
334                                 break; /* this bitnum is unused, use it */
335                         if (ret < 0) /* error */
336                                 goto out;
337                         /* this bit is already in use, try next bit */
338                 }
339                 if (bitnum == 64) {
340                         ret = -E_ATT_TABLE_FULL;
341                         goto out;
342                 }
343                 objs[ATTCOL_NAME].data = p;
344                 objs[ATTCOL_NAME].size = len + 1;
345                 ret = osl(osl_add_row(attribute_table, objs));
346                 if (ret < 0)
347                         goto out;
348                 aed.name = p;
349                 aed.bitnum = bitnum;
350                 afs_event(ATTRIBUTE_ADD, &pb, &aed);
351                 greatest_att_bitnum = PARA_MAX(greatest_att_bitnum, (int)bitnum);
352         }
353 out:
354         if (ret < 0 && ret2 >= 0)
355                 para_printf(&pb, "%s: %s\n", p, para_strerror(-ret));
356         if (pb.offset)
357                 pass_buffer_as_shm(pb.buf, pb.offset, &fd);
358         free(pb.buf);
359 }
360
361 int com_addatt(struct command_context *cc)
362 {
363         int ret;
364
365         if (cc->argc < 2)
366                 return -E_ATTR_SYNTAX;
367         ret = send_standard_callback_request(cc->argc - 1, cc->argv + 1,
368                 com_addatt_callback, sc_send_result, cc);
369         if (ret < 0)
370                 sc_send_va_buffer(&cc->scc, "%s\n", para_strerror(-ret));
371         return ret;
372 }
373
374 static void com_mvatt_callback(int fd, const struct osl_object *query)
375 {
376         char *old = query->data;
377         size_t size = strlen(old) + 1;
378         char *new = old + size;
379         struct osl_object obj = {.data = old, .size = size};
380         struct osl_row *row;
381         struct para_buffer pb = {
382                 .max_size = shm_get_shmmax(),
383                 .private_data = &fd,
384                 .max_size_handler = pass_buffer_as_shm
385         };
386         int ret;
387
388         ret = osl(osl_get_row(attribute_table, ATTCOL_NAME, &obj, &row));
389         if (ret < 0)
390                 goto out;
391         obj.data = new;
392         obj.size = strlen(new) + 1;
393         ret = osl(osl_update_object(attribute_table, row, ATTCOL_NAME, &obj));
394 out:
395         if (ret < 0)
396                 para_printf(&pb, "%s\n", para_strerror(-ret));
397         else
398                 afs_event(ATTRIBUTE_RENAME, &pb, NULL);
399         if (pb.offset)
400                 pass_buffer_as_shm(pb.buf, pb.offset, &fd);
401         free(pb.buf);
402 }
403
404 int com_mvatt(struct command_context *cc)
405 {
406         int ret;
407
408         if (cc->argc != 3)
409                 return -E_ATTR_SYNTAX;
410         ret = send_standard_callback_request(cc->argc - 1, cc->argv + 1,
411                 com_mvatt_callback, sc_send_result, cc);
412         if (ret < 0)
413                 sc_send_va_buffer(&cc->scc, "%s\n", para_strerror(-ret));
414         return ret;
415 }
416
417 /** Data passed to the action handler of com_rmatt(). */
418 struct remove_attribute_action_data {
419         /** Message buffer. */
420         struct para_buffer pb;
421         /** Numver of attributes removed. */
422         int num_removed;
423         /** Bitwise "or" of the removed attributes. */
424         uint64_t mask_of_removed_atts;
425 };
426
427 static int remove_attribute(struct osl_table *table, struct osl_row *row,
428                 const char *name, void *data)
429 {
430         struct remove_attribute_action_data *raad = data;
431         int ret;
432         struct rmatt_event_data red = {.name = name};
433
434         ret = get_attribute_bitnum_by_name(name, &red.bitnum);
435         if (ret < 0)
436                 return para_printf(&raad->pb, "%s: %s\n", name, para_strerror(-ret));
437         ret = osl(osl_del_row(table, row));
438         if (ret < 0)
439                 return para_printf(&raad->pb, "%s: %s\n", name, para_strerror(-ret));
440         ret = para_printf(&raad->pb, "removed attribute %s\n", name);
441         raad->num_removed++;
442         raad->mask_of_removed_atts |= (1 << red.bitnum);
443         afs_event(ATTRIBUTE_REMOVE, &raad->pb, &red);
444         return ret;
445 }
446
447 static void com_rmatt_callback(int fd, const struct osl_object *query)
448 {
449         struct remove_attribute_action_data raad = {
450                 .num_removed = 0,
451                 .pb = {
452                         .max_size = shm_get_shmmax(),
453                         .private_data = &fd,
454                         .max_size_handler = pass_buffer_as_shm
455                 }
456         };
457         int ret, ret2 = 0;
458         struct pattern_match_data pmd = {
459                 .table = attribute_table,
460                 .patterns = *query,
461                 .loop_col_num = ATTCOL_BITNUM,
462                 .match_col_num = ATTCOL_NAME,
463                 .data = &raad,
464                 .action = remove_attribute
465         };
466         ret = for_each_matching_row(&pmd);
467         if (ret < 0)
468                 ret2 = para_printf(&raad.pb, "%s\n", para_strerror(-ret));
469         else if (!raad.num_removed)
470                 ret2 = para_printf(&raad.pb, "no match -- nothing removed\n");
471         if (ret2 >= 0 && raad.pb.offset)
472                 pass_buffer_as_shm(raad.pb.buf, raad.pb.offset, &fd);
473         free(raad.pb.buf);
474 }
475
476 int com_rmatt(struct command_context *cc)
477 {
478         int ret;
479
480         if (cc->argc < 2)
481                 return -E_ATTR_SYNTAX;
482         ret = send_standard_callback_request(cc->argc - 1, cc->argv + 1,
483                 com_rmatt_callback, sc_send_result, cc);
484         if (ret < 0)
485                 sc_send_va_buffer(&cc->scc, "%s\n", para_strerror(-ret));
486         return ret;
487 }
488
489 /**
490  * Return a binary representation of the given attribute value.
491  *
492  * \param atts Pointer to the attribute value.
493  * \param buf Result.
494  *
495  * This function prints a string of at most 64 characters plus the terminating
496  * \p NULL character into \a buf which must be provided by the caller and at
497  * least 65 bytes long. The "x" character is used for set attributes and "-" is
498  * used for unset attributes.
499  *
500  * In practice, not all 64 attributes are defined. In this case, the function
501  * only prints \a N + 1 charaters where \a N is the greatest id of a defined
502  * attribute.
503  */
504 void get_attribute_bitmap(const uint64_t *atts, char *buf)
505 {
506         int i;
507         const uint64_t one = 1;
508
509         for (i = 0; i <= greatest_att_bitnum; i++)
510                 buf[greatest_att_bitnum - i] = (*atts & (one << i))? 'x' : '-';
511         buf[i] = '\0';
512 }
513
514 /**
515  * Get a string containing the set attributes in text form.
516  *
517  * \param atts The attribute bitmap.
518  * \param delim The delimiter to separate matching attribute names.
519  * \param text Result pointer.
520  *
521  * \return Positive on success, negative on errors. If no attributes have
522  * been defined, \a *text is NULL.
523  */
524 int get_attribute_text(uint64_t *atts, const char *delim, char **text)
525 {
526         int i, ret;
527         const uint64_t one = 1;
528
529         *text = NULL;
530         if (greatest_att_bitnum < 0) { /* no attributes available */
531                 *text = para_strdup("(no attributes available)");
532                 return 1;
533         }
534         for (i = 0; i <= greatest_att_bitnum; i++) {
535                 unsigned char bn = i;
536                 struct osl_object obj = {.data = &bn, .size = 1};
537                 struct osl_row *row;
538
539                 if (!(*atts & (one << i)))
540                         continue;
541                 ret = osl(osl_get_row(attribute_table, ATTCOL_BITNUM, &obj, &row));
542                 if (ret < 0)
543                         goto err;
544                 ret = osl(osl_get_object(attribute_table, row, ATTCOL_NAME, &obj));
545                 if (ret < 0)
546                         goto err;
547                 if (*text) {
548                         char *tmp = make_message("%s%s%s", *text, delim, (char *)obj.data);
549                         free(*text);
550                         *text = tmp;
551                 } else
552                         *text = para_strdup(obj.data);
553         }
554         if (!*text) /* no attributes set */
555                 *text = para_strdup("");
556         return 1;
557 err:
558         free(*text);
559         return ret;
560 }
561
562 /**
563  * Close the attribute table.
564  *
565  * \sa osl_close_table().
566  */
567 static void attribute_close(void)
568 {
569         osl_close_table(attribute_table, OSL_MARK_CLEAN);
570         attribute_table = NULL;
571 }
572
573 /**
574  * Open the attribute table.
575  *
576  * \param dir The database directory.
577  *
578  * \return Positive on success, negative on errors.
579  *
580  * \sa osl_open_table().
581  */
582 static int attribute_open(const char *dir)
583 {
584         int ret;
585
586         attribute_table_desc.dir = dir;
587         ret = osl(osl_open_table(&attribute_table_desc, &attribute_table));
588         greatest_att_bitnum = -1; /* no atts available */
589         if (ret >= 0) {
590                 find_greatest_att_bitnum();
591                 return ret;
592         }
593         attribute_table = NULL;
594         if (ret >= 0 || ret == -OSL_ERRNO_TO_PARA_ERROR(E_OSL_NOENT))
595                 return 1;
596         return ret;
597 }
598
599 static int attribute_create(const char *dir)
600 {
601         attribute_table_desc.dir = dir;
602         return osl(osl_create_table(&attribute_table_desc));
603 }
604
605 /**
606  * Initialize the attribute table structure.
607  *
608  * \param t The table structure to initialize.
609  */
610 void attribute_init(struct afs_table *t)
611 {
612         t->open = attribute_open;
613         t->close = attribute_close;
614         t->create = attribute_create;
615 }