Line data Source code
1 : /*
2 : INI LIBRARY
3 :
4 : File context related functions
5 :
6 : Copyright (C) Dmitri Pal <dpal@redhat.com> 2010
7 :
8 : INI Library is free software: you can redistribute it and/or modify
9 : it under the terms of the GNU Lesser General Public License as published by
10 : the Free Software Foundation, either version 3 of the License, or
11 : (at your option) any later version.
12 :
13 : INI Library is distributed in the hope that it will be useful,
14 : but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 : GNU Lesser General Public License for more details.
17 :
18 : You should have received a copy of the GNU Lesser General Public License
19 : along with INI Library. If not, see <http://www.gnu.org/licenses/>.
20 : */
21 : #include "config.h"
22 : #include <errno.h>
23 : #include <sys/types.h>
24 : #include <sys/stat.h>
25 : #include <fcntl.h>
26 : #include <string.h>
27 : #include <stdlib.h>
28 : #include <iconv.h>
29 : #include "trace.h"
30 : #include "ini_defines.h"
31 : #include "ini_configobj.h"
32 : #include "ini_config_priv.h"
33 : #include "path_utils.h"
34 :
35 : #define ICONV_BUFFER 5000
36 :
37 : #define BOM4_SIZE 4
38 : #define BOM3_SIZE 3
39 : #define BOM2_SIZE 2
40 :
41 : enum index_utf_t {
42 : INDEX_UTF32BE = 0,
43 : INDEX_UTF32LE = 1,
44 : INDEX_UTF16BE = 2,
45 : INDEX_UTF16LE = 3,
46 : INDEX_UTF8 = 4
47 : };
48 :
49 : /* Close file but not destroy the object */
50 1 : void ini_config_file_close(struct ini_cfgfile *file_ctx)
51 : {
52 : TRACE_FLOW_ENTRY();
53 :
54 1 : if(file_ctx) {
55 1 : if(file_ctx->file) {
56 1 : fclose(file_ctx->file);
57 1 : file_ctx->file = NULL;
58 : }
59 : }
60 :
61 : TRACE_FLOW_EXIT();
62 1 : }
63 :
64 : /* Close file context and destroy the object */
65 250 : void ini_config_file_destroy(struct ini_cfgfile *file_ctx)
66 : {
67 : TRACE_FLOW_ENTRY();
68 :
69 250 : if(file_ctx) {
70 250 : free(file_ctx->filename);
71 250 : simplebuffer_free(file_ctx->file_data);
72 250 : if(file_ctx->file) fclose(file_ctx->file);
73 250 : free(file_ctx);
74 : }
75 :
76 : TRACE_FLOW_EXIT();
77 250 : }
78 :
79 : /* How much I plan to read? */
80 : static size_t how_much_to_read(size_t left, size_t increment)
81 : {
82 3945 : if(left > increment) return increment;
83 : else return left;
84 : }
85 :
86 245 : static enum index_utf_t check_bom(enum index_utf_t ind,
87 : unsigned char *buffer,
88 : size_t len,
89 : size_t *bom_shift)
90 : {
91 : TRACE_FLOW_ENTRY();
92 :
93 245 : if (len >= BOM4_SIZE) {
94 250 : if ((buffer[0] == 0x00) &&
95 18 : (buffer[1] == 0x00) &&
96 18 : (buffer[2] == 0xFE) &&
97 9 : (buffer[3] == 0xFF)) {
98 : TRACE_FLOW_RETURN(INDEX_UTF32BE);
99 9 : *bom_shift = BOM4_SIZE;
100 9 : return INDEX_UTF32BE;
101 : }
102 259 : else if ((buffer[0] == 0xFF) &&
103 54 : (buffer[1] == 0xFE) &&
104 36 : (buffer[2] == 0x00) &&
105 9 : (buffer[3] == 0x00)) {
106 : TRACE_FLOW_RETURN(INDEX_UTF32LE);
107 9 : *bom_shift = BOM4_SIZE;
108 9 : return INDEX_UTF32LE;
109 : }
110 : }
111 :
112 227 : if (len >= BOM3_SIZE) {
113 232 : if ((buffer[0] == 0xEF) &&
114 18 : (buffer[1] == 0xBB) &&
115 9 : (buffer[2] == 0xBF)) {
116 : TRACE_FLOW_RETURN(INDEX_UTF8);
117 9 : *bom_shift = BOM3_SIZE;
118 9 : return INDEX_UTF8;
119 : }
120 : }
121 :
122 218 : if (len >= BOM2_SIZE) {
123 223 : if ((buffer[0] == 0xFE) &&
124 9 : (buffer[1] == 0xFF)) {
125 : TRACE_FLOW_RETURN(INDEX_UTF16BE);
126 9 : *bom_shift = BOM2_SIZE;
127 9 : return INDEX_UTF16BE;
128 : }
129 223 : else if ((buffer[0] == 0xFF) &&
130 18 : (buffer[1] == 0xFE)) {
131 : TRACE_FLOW_RETURN(INDEX_UTF16LE);
132 18 : *bom_shift = BOM2_SIZE;
133 18 : return INDEX_UTF16LE;
134 : }
135 : }
136 :
137 : TRACE_FLOW_RETURN(ind);
138 : return ind;
139 : }
140 :
141 3945 : static int read_chunk(FILE *file, size_t left, size_t increment,
142 : char *position, size_t *read_num)
143 : {
144 3945 : int error = EOK;
145 3945 : size_t to_read = 0;
146 3945 : size_t read_cnt = 0;
147 :
148 : TRACE_FLOW_ENTRY();
149 :
150 3945 : to_read = how_much_to_read(left, increment);
151 :
152 : TRACE_INFO_NUMBER("About to read", to_read);
153 :
154 3945 : read_cnt = fread(position, to_read, 1, file);
155 :
156 : TRACE_INFO_NUMBER("Read", read_cnt * to_read);
157 :
158 3945 : if (read_cnt == 0) {
159 0 : error = ferror(file);
160 0 : if (error) {
161 : TRACE_ERROR_NUMBER("Failed to read data from file", error);
162 : return error;
163 : }
164 0 : error = feof(file);
165 0 : if(error) {
166 : TRACE_FLOW_EXIT();
167 : return EOK;
168 : }
169 : TRACE_ERROR_NUMBER("Failed to read data from file", EIO);
170 0 : return EIO;
171 : }
172 :
173 3945 : *read_num = to_read;
174 :
175 : TRACE_FLOW_EXIT();
176 3945 : return error;
177 : }
178 :
179 : /* Function useful for debugging */
180 : /*
181 : static void print_buffer(char *read_buffer, int len)
182 : {
183 : int i;
184 : for (i=0; i < len; i++) {
185 : printf("%02X ", (unsigned char)read_buffer[i]);
186 : }
187 : printf("\n");
188 : }
189 : */
190 :
191 : /* Internal initialization part */
192 3945 : static int initialize_conv(unsigned char *read_buf,
193 : size_t read_cnt,
194 : int *initialized,
195 : size_t *bom_shift,
196 : iconv_t *conv)
197 : {
198 3945 : int error = EOK;
199 3945 : enum index_utf_t ind = INDEX_UTF8;
200 3945 : const char *encodings[] = { "UTF-32BE",
201 : "UTF-32LE",
202 : "UTF-16BE",
203 : "UTF-16LE",
204 : "UTF-8" };
205 :
206 : TRACE_FLOW_ENTRY();
207 :
208 3945 : if (*initialized == 0) {
209 :
210 : TRACE_INFO_STRING("Reading first time.","Checking BOM");
211 :
212 245 : ind = check_bom(ind,
213 : (unsigned char *)read_buf,
214 : read_cnt,
215 : bom_shift);
216 :
217 : TRACE_INFO_STRING("Converting to", encodings[INDEX_UTF8]);
218 : TRACE_INFO_STRING("Converting from", encodings[ind]);
219 :
220 245 : errno = 0;
221 245 : *conv = iconv_open(encodings[INDEX_UTF8], encodings[ind]);
222 245 : if (*conv == (iconv_t) -1) {
223 0 : error = errno;
224 : TRACE_ERROR_NUMBER("Failed to create converter", error);
225 0 : return error;
226 : }
227 :
228 245 : *initialized = 1;
229 : }
230 3700 : else *bom_shift = 0;
231 :
232 : TRACE_FLOW_EXIT();
233 : return error;
234 :
235 : }
236 :
237 : /* Internal conversion part */
238 245 : static int common_file_convert(FILE *file,
239 : struct ini_cfgfile *file_ctx,
240 : uint32_t size)
241 : {
242 245 : int error = EOK;
243 245 : size_t read_cnt = 0;
244 245 : size_t total_read = 0;
245 245 : size_t in_buffer = 0;
246 245 : iconv_t conv = (iconv_t)-1;
247 245 : size_t conv_res = 0;
248 : char read_buf[ICONV_BUFFER+1];
249 : char result_buf[ICONV_BUFFER];
250 : char *src, *dest;
251 245 : size_t to_convert = 0;
252 245 : size_t room_left = 0;
253 245 : size_t bom_shift = 0;
254 245 : int initialized = 0;
255 :
256 : TRACE_FLOW_ENTRY();
257 :
258 : do {
259 : /* print_buffer(read_buf, ICONV_BUFFER); */
260 3945 : error = read_chunk(file,
261 : size - total_read,
262 : ICONV_BUFFER - in_buffer,
263 : read_buf + in_buffer,
264 : &read_cnt);
265 : /* print_buffer(read_buf, ICONV_BUFFER); */
266 3945 : if (error) {
267 0 : if (conv != (iconv_t) -1) iconv_close(conv);
268 : TRACE_ERROR_NUMBER("Failed to read chunk", error);
269 : return error;
270 : }
271 :
272 : /* Prepare source buffer for conversion */
273 3945 : src = read_buf;
274 3945 : to_convert = read_cnt + in_buffer;
275 3945 : in_buffer = 0;
276 :
277 : /* Do initialization if needed */
278 3945 : error = initialize_conv((unsigned char *)read_buf,
279 : read_cnt,
280 : &initialized,
281 : &bom_shift,
282 : &conv);
283 3945 : if (error) {
284 : TRACE_ERROR_NUMBER("Failed to initialize",
285 : error);
286 : return error;
287 : }
288 :
289 3945 : src += bom_shift;
290 3945 : to_convert -= bom_shift;
291 3945 : total_read += read_cnt;
292 : TRACE_INFO_NUMBER("Total read", total_read);
293 :
294 : do {
295 : /* Do conversion */
296 3999 : dest = result_buf;
297 3999 : room_left = ICONV_BUFFER;
298 :
299 : TRACE_INFO_NUMBER("To convert", to_convert);
300 : TRACE_INFO_NUMBER("Room left", room_left);
301 : TRACE_INFO_NUMBER("Total read", total_read);
302 :
303 3999 : errno = 0;
304 3999 : conv_res = iconv(conv, &src, &to_convert, &dest, &room_left);
305 3999 : if (conv_res == (size_t) -1) {
306 238 : error = errno;
307 238 : switch(error) {
308 : case EILSEQ:
309 : TRACE_ERROR_NUMBER("Invalid multibyte encoding", error);
310 0 : iconv_close(conv);
311 : return error;
312 : case EINVAL:
313 : /* We need to just read more if we can */
314 : TRACE_INFO_NUMBER("Incomplete sequence len",
315 : src - read_buf);
316 : TRACE_INFO_NUMBER("File size.", size);
317 184 : if (total_read == size) {
318 : /* Or return error if we can't */
319 : TRACE_ERROR_NUMBER("Incomplete sequence", error);
320 0 : iconv_close(conv);
321 : return error;
322 : }
323 184 : memmove(read_buf, src, to_convert);
324 184 : in_buffer = to_convert;
325 : break;
326 :
327 : case E2BIG:
328 : TRACE_INFO_STRING("No room in the output buffer.", "");
329 54 : error = simplebuffer_add_raw(file_ctx->file_data,
330 : result_buf,
331 : ICONV_BUFFER - room_left,
332 : ICONV_BUFFER);
333 54 : if (error) {
334 : TRACE_ERROR_NUMBER("Failed to store converted bytes",
335 : error);
336 0 : iconv_close(conv);
337 : return error;
338 : }
339 54 : continue;
340 : default:
341 : TRACE_ERROR_NUMBER("Unexpected internal error",
342 : error);
343 0 : iconv_close(conv);
344 : return ENOTSUP;
345 : }
346 : }
347 : /* The whole buffer was sucessfully converted */
348 3945 : error = simplebuffer_add_raw(file_ctx->file_data,
349 : result_buf,
350 : ICONV_BUFFER - room_left,
351 : ICONV_BUFFER);
352 3945 : if (error) {
353 : TRACE_ERROR_NUMBER("Failed to store converted bytes",
354 : error);
355 0 : iconv_close(conv);
356 : return error;
357 : }
358 : /*
359 : TRACE_INFO_STRING("Saved procesed portion.",
360 : (char *)simplebuffer_get_vbuf(file_ctx->file_data));
361 : */
362 : break;
363 : }
364 : while (1);
365 : }
366 3945 : while (total_read < size);
367 :
368 245 : iconv_close(conv);
369 :
370 : /* Open file */
371 : TRACE_INFO_STRING("File data",
372 : (char *)simplebuffer_get_vbuf(file_ctx->file_data));
373 : TRACE_INFO_NUMBER("File len",
374 : simplebuffer_get_len(file_ctx->file_data));
375 : TRACE_INFO_NUMBER("Size", size);
376 245 : errno = 0;
377 245 : file_ctx->file = fmemopen(simplebuffer_get_vbuf(file_ctx->file_data),
378 245 : simplebuffer_get_len(file_ctx->file_data),
379 : "r");
380 245 : if (!(file_ctx->file)) {
381 0 : error = errno;
382 : TRACE_ERROR_NUMBER("Failed to open file", error);
383 : return error;
384 : }
385 :
386 : TRACE_FLOW_EXIT();
387 : return EOK;
388 : }
389 :
390 :
391 : /* Internal common initialization part */
392 250 : static int common_file_init(struct ini_cfgfile *file_ctx,
393 : void *data_buf,
394 : uint32_t data_len)
395 : {
396 250 : int error = EOK;
397 250 : FILE *file = NULL;
398 250 : int stat_ret = 0;
399 250 : uint32_t size = 0;
400 250 : void *internal_data = NULL;
401 250 : uint32_t internal_len = 0;
402 250 : unsigned char alt_buffer[2] = {0, 0};
403 250 : uint32_t alt_buffer_len = 1;
404 :
405 : TRACE_FLOW_ENTRY();
406 :
407 250 : if (data_buf) {
408 :
409 48 : if(data_len) {
410 44 : internal_data = data_buf;
411 44 : internal_len = data_len;
412 : }
413 : else {
414 : /* If buffer is empty fmemopen will return an error.
415 : * This will prevent creation of adefault config object.
416 : * Instead we will use buffer that has at least one character. */
417 : internal_data = alt_buffer;
418 : internal_len = alt_buffer_len;
419 : }
420 :
421 : TRACE_INFO_NUMBER("Inside file_init len", internal_len);
422 : TRACE_INFO_STRING("Inside file_init data:", (char *)internal_data);
423 :
424 48 : file = fmemopen(internal_data, internal_len, "r");
425 48 : if (!file) {
426 0 : error = errno;
427 : TRACE_ERROR_NUMBER("Failed to memmap file", error);
428 0 : return error;
429 : }
430 : size = internal_len;
431 : }
432 : else {
433 :
434 : TRACE_INFO_STRING("File", file_ctx->filename);
435 :
436 : /* Open file to get its size */
437 202 : errno = 0;
438 202 : file = fopen(file_ctx->filename, "r");
439 202 : if (!file) {
440 0 : error = errno;
441 : TRACE_ERROR_NUMBER("Failed to open file", error);
442 0 : return error;
443 : }
444 :
445 : /* Get the size of the file */
446 202 : errno = 0;
447 404 : stat_ret = fstat(fileno(file), &(file_ctx->file_stats));
448 202 : if (stat_ret == -1) {
449 0 : error = errno;
450 0 : fclose(file);
451 : TRACE_ERROR_NUMBER("Failed to get file stats", error);
452 0 : return error;
453 : }
454 202 : size = file_ctx->file_stats.st_size;
455 : }
456 :
457 : /* Trick to overcome the fact that
458 : * fopen and fmemopen behave differently when file
459 : * is 0 length
460 : */
461 250 : if (size) {
462 245 : error = common_file_convert(file, file_ctx, size);
463 245 : if (error) {
464 : TRACE_ERROR_NUMBER("Failed to convert file",
465 : error);
466 0 : fclose(file);
467 0 : return error;
468 : }
469 : }
470 : else {
471 :
472 : TRACE_INFO_STRING("File is 0 length","");
473 5 : errno = 0;
474 :
475 5 : file_ctx->file = fdopen(fileno(file), "r");
476 5 : if (!(file_ctx->file)) {
477 0 : error = errno;
478 0 : fclose(file);
479 : TRACE_ERROR_NUMBER("Failed to fdopen file", error);
480 0 : return error;
481 : }
482 : }
483 :
484 250 : fclose(file);
485 :
486 : /* Collect stats */
487 250 : if (file_ctx->metadata_flags & INI_META_STATS) {
488 4 : file_ctx->stats_read = 1;
489 : }
490 : else {
491 246 : memset(&(file_ctx->file_stats), 0, sizeof(struct stat));
492 246 : file_ctx->stats_read = 0;
493 : }
494 :
495 : TRACE_FLOW_EXIT();
496 : return EOK;
497 : }
498 :
499 : /* Create a file object for parsing a config file */
500 200 : int ini_config_file_open(const char *filename,
501 : uint32_t metadata_flags,
502 : struct ini_cfgfile **file_ctx)
503 : {
504 200 : int error = EOK;
505 200 : struct ini_cfgfile *new_ctx = NULL;
506 :
507 : TRACE_FLOW_ENTRY();
508 :
509 200 : if ((!filename) || (!file_ctx)) {
510 : TRACE_ERROR_NUMBER("Invalid parameter.", EINVAL);
511 : return EINVAL;
512 : }
513 :
514 : /* Allocate structure */
515 200 : new_ctx = malloc(sizeof(struct ini_cfgfile));
516 200 : if (!new_ctx) {
517 : TRACE_ERROR_NUMBER("Failed to allocate file ctx.", ENOMEM);
518 : return ENOMEM;
519 : }
520 :
521 200 : new_ctx->filename = NULL;
522 200 : new_ctx->file = NULL;
523 200 : new_ctx->file_data = NULL;
524 :
525 200 : error = simplebuffer_alloc(&(new_ctx->file_data));
526 200 : if (error) {
527 : TRACE_ERROR_NUMBER("Failed to allocate buffer ctx.", error);
528 0 : ini_config_file_destroy(new_ctx);
529 0 : return error;
530 :
531 : }
532 :
533 : /* Store flags */
534 200 : new_ctx->metadata_flags = metadata_flags;
535 :
536 : /* Construct the full file path */
537 200 : new_ctx->filename = malloc(PATH_MAX + 1);
538 200 : if (!(new_ctx->filename)) {
539 0 : ini_config_file_destroy(new_ctx);
540 : TRACE_ERROR_NUMBER("Failed to allocate memory for file path.", ENOMEM);
541 0 : return ENOMEM;
542 : }
543 :
544 : /* Construct path */
545 200 : error = make_normalized_absolute_path(new_ctx->filename,
546 : PATH_MAX,
547 : filename);
548 200 : if(error) {
549 : TRACE_ERROR_NUMBER("Failed to resolve path", error);
550 0 : ini_config_file_destroy(new_ctx);
551 0 : return error;
552 : }
553 :
554 : /* Do common init */
555 200 : error = common_file_init(new_ctx, NULL, 0);
556 200 : if(error) {
557 : TRACE_ERROR_NUMBER("Failed to do common init", error);
558 0 : ini_config_file_destroy(new_ctx);
559 0 : return error;
560 : }
561 :
562 200 : *file_ctx = new_ctx;
563 : TRACE_FLOW_EXIT();
564 200 : return error;
565 : }
566 :
567 : /* Create a file object from a memory buffer */
568 48 : int ini_config_file_from_mem(void *data_buf,
569 : uint32_t data_len,
570 : struct ini_cfgfile **file_ctx)
571 : {
572 48 : int error = EOK;
573 48 : struct ini_cfgfile *new_ctx = NULL;
574 :
575 : TRACE_FLOW_ENTRY();
576 :
577 48 : if ((!data_buf) || (!file_ctx)) {
578 : TRACE_ERROR_NUMBER("Invalid parameter.", EINVAL);
579 : return EINVAL;
580 : }
581 :
582 : /* Allocate structure */
583 48 : new_ctx = malloc(sizeof(struct ini_cfgfile));
584 48 : if (!new_ctx) {
585 : TRACE_ERROR_NUMBER("Failed to allocate file ctx.", ENOMEM);
586 : return ENOMEM;
587 : }
588 :
589 48 : new_ctx->filename = NULL;
590 48 : new_ctx->file = NULL;
591 48 : new_ctx->file_data = NULL;
592 48 : new_ctx->metadata_flags = 0;
593 :
594 48 : error = simplebuffer_alloc(&(new_ctx->file_data));
595 48 : if (error) {
596 : TRACE_ERROR_NUMBER("Failed to allocate buffer ctx.", error);
597 0 : ini_config_file_destroy(new_ctx);
598 0 : return error;
599 : }
600 :
601 : /* Put an empty string into the file name */
602 48 : new_ctx->filename = strdup("");
603 48 : if (!(new_ctx->filename)) {
604 0 : ini_config_file_destroy(new_ctx);
605 : TRACE_ERROR_NUMBER("Failed to put empty string into filename.", ENOMEM);
606 0 : return ENOMEM;
607 : }
608 :
609 : /* Do common init */
610 48 : error = common_file_init(new_ctx, data_buf, data_len);
611 48 : if(error) {
612 : TRACE_ERROR_NUMBER("Failed to do common init", error);
613 0 : ini_config_file_destroy(new_ctx);
614 0 : return error;
615 : }
616 :
617 48 : *file_ctx = new_ctx;
618 : TRACE_FLOW_EXIT();
619 48 : return error;
620 : }
621 :
622 :
623 :
624 : /* Create a file object from existing one */
625 2 : int ini_config_file_reopen(struct ini_cfgfile *file_ctx_in,
626 : struct ini_cfgfile **file_ctx_out)
627 : {
628 2 : int error = EOK;
629 2 : struct ini_cfgfile *new_ctx = NULL;
630 :
631 : TRACE_FLOW_ENTRY();
632 :
633 2 : if ((!file_ctx_in) || (!file_ctx_out)) {
634 : TRACE_ERROR_NUMBER("Invalid parameter.", EINVAL);
635 : return EINVAL;
636 : }
637 :
638 : /* Allocate structure */
639 2 : new_ctx = malloc(sizeof(struct ini_cfgfile));
640 2 : if (!new_ctx) {
641 : TRACE_ERROR_NUMBER("Failed to allocate file ctx.", ENOMEM);
642 : return ENOMEM;
643 : }
644 :
645 2 : new_ctx->file = NULL;
646 2 : new_ctx->file_data = NULL;
647 2 : new_ctx->filename = NULL;
648 :
649 2 : error = simplebuffer_alloc(&(new_ctx->file_data));
650 2 : if (error) {
651 : TRACE_ERROR_NUMBER("Failed to allocate buffer ctx.", error);
652 0 : ini_config_file_destroy(new_ctx);
653 0 : return error;
654 :
655 : }
656 :
657 : /* Store flags */
658 2 : new_ctx->metadata_flags = file_ctx_in->metadata_flags;
659 :
660 : /* Copy full file path */
661 2 : errno = 0;
662 2 : new_ctx->filename = strndup(file_ctx_in->filename, PATH_MAX);
663 2 : if (!(new_ctx->filename)) {
664 0 : error = errno;
665 0 : ini_config_file_destroy(new_ctx);
666 : TRACE_ERROR_NUMBER("Failed to allocate memory for file path.", error);
667 0 : return error;
668 : }
669 :
670 : /* Do common init */
671 2 : error = common_file_init(new_ctx, NULL, 0);
672 2 : if(error) {
673 : TRACE_ERROR_NUMBER("Failed to do common init", error);
674 0 : ini_config_file_destroy(new_ctx);
675 0 : return error;
676 : }
677 :
678 2 : *file_ctx_out = new_ctx;
679 : TRACE_FLOW_EXIT();
680 2 : return error;
681 : }
682 :
683 : /* Get the fully resolved file name */
684 0 : const char *ini_config_get_filename(struct ini_cfgfile *file_ctx)
685 : {
686 : const char *ret;
687 : TRACE_FLOW_ENTRY();
688 :
689 0 : ret = file_ctx->filename;
690 :
691 : TRACE_FLOW_EXIT();
692 0 : return ret;
693 : }
694 :
695 : /* Get pointer to stat structure */
696 2 : const struct stat *ini_config_get_stat(struct ini_cfgfile *file_ctx)
697 : {
698 : const struct stat *ret;
699 : TRACE_FLOW_ENTRY();
700 :
701 2 : if (file_ctx->stats_read) ret = &(file_ctx->file_stats);
702 : else ret = NULL;
703 :
704 : TRACE_FLOW_EXIT();
705 2 : return ret;
706 : }
707 :
708 :
709 : /* Check access */
710 4 : int ini_config_access_check(struct ini_cfgfile *file_ctx,
711 : uint32_t flags,
712 : uid_t uid,
713 : gid_t gid,
714 : mode_t mode,
715 : mode_t mask)
716 : {
717 : mode_t st_mode;
718 :
719 : TRACE_FLOW_ENTRY();
720 :
721 4 : flags &= INI_ACCESS_CHECK_MODE |
722 : INI_ACCESS_CHECK_GID |
723 : INI_ACCESS_CHECK_UID;
724 :
725 4 : if ((file_ctx == NULL) || (flags == 0)) {
726 : TRACE_ERROR_NUMBER("Invalid parameter.", EINVAL);
727 : return EINVAL;
728 : }
729 :
730 4 : if (file_ctx->stats_read == 0) {
731 : TRACE_ERROR_NUMBER("Stats were not collected.", EINVAL);
732 : return EINVAL;
733 : }
734 :
735 : /* Check mode */
736 3 : if (flags & INI_ACCESS_CHECK_MODE) {
737 :
738 : TRACE_INFO_NUMBER("File mode as saved.",
739 : file_ctx->file_stats.st_mode);
740 :
741 3 : st_mode = file_ctx->file_stats.st_mode;
742 3 : st_mode &= S_IRWXU | S_IRWXG | S_IRWXO;
743 : TRACE_INFO_NUMBER("File mode adjusted.", st_mode);
744 :
745 : TRACE_INFO_NUMBER("Mode as provided.", mode);
746 3 : mode &= S_IRWXU | S_IRWXG | S_IRWXO;
747 : TRACE_INFO_NUMBER("Mode adjusted.", mode);
748 :
749 : /* Adjust mask */
750 3 : if (mask == 0) mask = S_IRWXU | S_IRWXG | S_IRWXO;
751 0 : else mask &= S_IRWXU | S_IRWXG | S_IRWXO;
752 :
753 3 : if ((mode & mask) != (st_mode & mask)) {
754 : TRACE_INFO_NUMBER("File mode:", (mode & mask));
755 : TRACE_INFO_NUMBER("Mode adjusted.",
756 : (st_mode & mask));
757 : TRACE_ERROR_NUMBER("Access denied.", EACCES);
758 : return EACCES;
759 : }
760 : }
761 :
762 : /* Check uid */
763 2 : if (flags & INI_ACCESS_CHECK_UID) {
764 0 : if (file_ctx->file_stats.st_uid != uid) {
765 : TRACE_ERROR_NUMBER("GID:", file_ctx->file_stats.st_uid);
766 : TRACE_ERROR_NUMBER("GID passed in.", uid);
767 : TRACE_ERROR_NUMBER("Access denied.", EACCES);
768 : return EACCES;
769 : }
770 : }
771 :
772 : /* Check gid */
773 2 : if (flags & INI_ACCESS_CHECK_GID) {
774 0 : if (file_ctx->file_stats.st_gid != gid) {
775 : TRACE_ERROR_NUMBER("GID:", file_ctx->file_stats.st_gid);
776 : TRACE_ERROR_NUMBER("GID passed in.", gid);
777 : TRACE_ERROR_NUMBER("Access denied.", EACCES);
778 : return EACCES;
779 : }
780 : }
781 :
782 : TRACE_FLOW_EXIT();
783 2 : return EOK;
784 :
785 : }
786 :
787 : /* Determines if two file contexts are different by comparing:
788 : * - time stamp
789 : * - device ID
790 : * - i-node
791 : */
792 2 : int ini_config_changed(struct ini_cfgfile *file_ctx1,
793 : struct ini_cfgfile *file_ctx2,
794 : int *changed)
795 : {
796 : TRACE_FLOW_ENTRY();
797 :
798 4 : if ((file_ctx1 == NULL) ||
799 4 : (file_ctx2 == NULL) ||
800 : (changed == NULL)) {
801 : TRACE_ERROR_NUMBER("Invalid parameter.", EINVAL);
802 : return EINVAL;
803 : }
804 :
805 4 : if ((file_ctx1->stats_read == 0) ||
806 2 : (file_ctx2->stats_read == 0)) {
807 : TRACE_ERROR_NUMBER("Stats were not collected.", EINVAL);
808 : return EINVAL;
809 : }
810 :
811 2 : *changed = 0;
812 :
813 : /* Unfortunately the time is not granular enough
814 : * to detect the change if run during the unit test.
815 : * In future when a more granular version of stat
816 : * is available we should switch to it and update
817 : * the unit test.
818 : */
819 :
820 4 : if((file_ctx1->file_stats.st_mtime !=
821 3 : file_ctx2->file_stats.st_mtime) ||
822 1 : (file_ctx1->file_stats.st_dev !=
823 2 : file_ctx2->file_stats.st_dev) ||
824 1 : (file_ctx1->file_stats.st_ino !=
825 1 : file_ctx2->file_stats.st_ino)) {
826 : TRACE_INFO_STRING("File changed!", "");
827 1 : *changed = 1;
828 : }
829 :
830 : TRACE_FLOW_EXIT();
831 : return EOK;
832 : }
833 :
834 : /* Print the file object contents */
835 0 : void ini_config_file_print(struct ini_cfgfile *file_ctx)
836 : {
837 : TRACE_FLOW_ENTRY();
838 0 : if (file_ctx == NULL) {
839 0 : printf("No file object\n.");
840 : }
841 : else {
842 0 : printf("File name: %s\n", (file_ctx->filename) ? file_ctx->filename : "NULL");
843 0 : printf("File is %s\n", (file_ctx->file) ? "open" : "closed");
844 0 : printf("Metadata flags %u\n", file_ctx->metadata_flags);
845 0 : printf("Stats flag st_dev %li\n", file_ctx->file_stats.st_dev);
846 0 : printf("Stats flag st_ino %li\n", file_ctx->file_stats.st_ino);
847 0 : printf("Stats flag st_mode %u\n", file_ctx->file_stats.st_mode);
848 0 : printf("Stats flag st_nlink %li\n", file_ctx->file_stats.st_nlink);
849 0 : printf("Stats flag st_uid %u\n", file_ctx->file_stats.st_uid);
850 0 : printf("Stats flag st_gid %u\n", file_ctx->file_stats.st_gid);
851 0 : printf("Stats flag st_rdev %li\n", file_ctx->file_stats.st_rdev);
852 0 : printf("Stats flag st_size %lu\n", file_ctx->file_stats.st_size);
853 0 : printf("Stats flag st_blocks %li\n", file_ctx->file_stats.st_blocks);
854 0 : printf("Stats flag st_atime %ld\n", file_ctx->file_stats.st_atime);
855 0 : printf("Stats flag st_mtime %ld\n", file_ctx->file_stats.st_mtime);
856 0 : printf("Stats flag st_ctime %ld\n", file_ctx->file_stats.st_ctime);
857 : }
858 : TRACE_FLOW_EXIT();
859 0 : }
|