/[osn-commons]/trunk/uar/uar.c
ViewVC logotype

Contents of /trunk/uar/uar.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 36 - (show annotations)
Thu Aug 8 19:13:26 2024 UTC (7 months, 3 weeks ago) by rakinar2
File MIME type: text/x-c
File size: 44809 byte(s)
feat(uar): better content listing; including owner and group info
1 #define _XOPEN_SOURCE 500
2
3 #include "uar.h"
4 #include "malloc.h"
5
6 #include <assert.h>
7 #include <dirent.h>
8 #include <errno.h>
9 #include <fcntl.h>
10 #include <stdbool.h>
11 #include <stdint.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <strings.h>
16 #include <sys/stat.h>
17 #include <sys/types.h>
18 #include <time.h>
19 #include <unistd.h>
20
21 #if defined(__linux__)
22 # include <linux/limits.h>
23 #elif defined(__APPLE__)
24 # include <sys/syslimits.h>
25 #else
26 # include <limits.h>
27 #endif
28
29 const unsigned char UAR_MAGIC[] = { 0x99, 'U', 'A', 'R' };
30 const unsigned int UAR_MAX_SUPPORTED_VERSION = 0x01;
31
32 struct uar_header
33 {
34 uint8_t magic[4];
35 uint16_t version;
36 uint32_t flags;
37 uint64_t nfiles;
38 uint64_t size;
39 } __attribute__ ((packed));
40
41 /* TODO: Fix alignment */
42 struct uar_file
43 {
44 enum uar_file_type type;
45 char *name;
46 uint64_t namelen;
47 uint64_t offset;
48 union
49 {
50 uint64_t size;
51 struct
52 {
53 char *loc;
54 uint64_t loclen;
55 } link;
56 } data;
57 mode_t mode;
58 time_t mtime;
59 uid_t uid;
60 gid_t gid;
61 };
62
63 struct uar_archive
64 {
65 struct uar_header header;
66 struct uar_file **files;
67 struct uar_file *root;
68 enum uar_error ecode;
69 bool is_stream;
70 uint8_t *buffer; /* Deprecated */
71 FILE *stream;
72 uint64_t stream_size;
73 int last_errno;
74 char *err_file;
75 uar_create_callback_t create_callback;
76 };
77
78 void
79 uar_set_create_callback (struct uar_archive *uar,
80 uar_create_callback_t callback)
81 {
82 uar->create_callback = callback;
83 }
84
85 static void
86 uar_set_error (struct uar_archive *uar, enum uar_error ecode,
87 const char *err_file)
88 {
89 uar->ecode = ecode;
90 uar->last_errno = errno;
91 free (uar->err_file);
92 uar->err_file = err_file == NULL ? NULL : strdup (err_file);
93 }
94
95 const char *
96 uar_strerror (const struct uar_archive *restrict uar)
97 {
98 switch (uar->ecode)
99 {
100 case UAR_SUCCESS:
101 return "success";
102 case UAR_INVALID_MAGIC:
103 return "invalid archive magic";
104 case UAR_INVALID_ARCHIVE:
105 return "invalid archive";
106 case UAR_UNSUPPORTED_VERSION:
107 return "archive version is not supported";
108 case UAR_INVALID_FILE:
109 return "invalid file";
110 case UAR_INVALID_PATH:
111 return "invalid path string";
112 case UAR_IO_ERROR:
113 return "archive I/O error";
114 case UAR_OUT_OF_MEMORY:
115 return "out of memory";
116 case UAR_INVALID_ARGUMENT:
117 return "invalid argument";
118 case UAR_INVALID_OPERATION:
119 return "invalid operation";
120 case UAR_SYSTEM_ERROR:
121 return "system error";
122 case UAR_SYSCALL_ERROR:
123 return strerror (uar->last_errno);
124 default:
125 return "unknown error";
126 }
127 }
128
129 bool
130 uar_has_error (const struct uar_archive *restrict uar)
131 {
132 return uar->ecode != UAR_SUCCESS;
133 }
134
135 /* TODO: Use static storage for path */
136 static char *
137 path_concat (const char *p1, const char *p2, size_t len1, size_t len2)
138 {
139 char *path = malloc (len1 + len2 + 2);
140
141 if (path == NULL)
142 return NULL;
143
144 strncpy (path, p1, len1);
145 path[len1] = '/';
146 strncpy (path + len1 + 1, p2, len2);
147 path[len1 + len2 + 1] = 0;
148 return path;
149 }
150
151 struct uar_archive *
152 uar_create (void)
153 {
154 struct uar_archive *uar = malloc (sizeof (struct uar_archive));
155
156 if (uar == NULL)
157 return NULL;
158
159 uar->is_stream = false;
160 uar->buffer = NULL;
161 uar->ecode = UAR_SUCCESS;
162 uar->header.size = 0;
163 memcpy (uar->header.magic, UAR_MAGIC, 4);
164 uar->header.version = 1;
165 uar->header.flags = 0;
166 uar->header.nfiles = 0;
167 uar->files = NULL;
168 uar->stream = NULL;
169 uar->root = NULL;
170 uar->stream_size = 0;
171 uar->last_errno = 0;
172 uar->err_file = NULL;
173
174 return uar;
175 }
176
177 static struct uar_file *
178 uar_initialize_root (struct uar_archive *uar)
179 {
180 struct uar_file *root = uar_file_create ("/", 1, 0, 0);
181
182 if (root == NULL)
183 {
184 uar_set_error (uar, UAR_OUT_OF_MEMORY, "/");
185 return NULL;
186 }
187
188 root->type = UF_DIR;
189 root->mode = S_IFDIR | 0755;
190 root->mtime = time (NULL);
191 root->uid = getuid ();
192 root->gid = getgid ();
193
194 if (!uar_add_file_entry (uar, root))
195 {
196 uar_file_destroy (root);
197 return NULL;
198 }
199
200 uar->root = root;
201
202 return root;
203 }
204
205 static bool
206 uar_initialize (struct uar_archive *uar)
207 {
208 if (uar_initialize_root (uar) == NULL)
209 return false;
210
211 return true;
212 }
213
214 bool
215 uar_stream_write (struct uar_archive *uar, const char *filename)
216 {
217 if (uar == NULL || !uar->is_stream || uar->stream == NULL)
218 return false;
219
220 FILE *stream = fopen (filename, "wb");
221
222 if (fwrite (&uar->header, 1, sizeof (struct uar_header), stream)
223 != sizeof (struct uar_header))
224 {
225 uar_set_error (uar, UAR_SYSCALL_ERROR, NULL);
226 fclose (stream);
227 return false;
228 }
229
230 for (uint64_t i = 0; i < uar->header.nfiles; i++)
231 {
232 struct uar_file *file = uar->files[i];
233
234 if (fwrite (file, 1, sizeof (struct uar_file), stream)
235 != sizeof (struct uar_file))
236 {
237 uar_set_error (uar, UAR_SYSCALL_ERROR, file->name);
238 fclose (stream);
239 return false;
240 }
241
242 if (fwrite (file->name, 1, file->namelen, stream) != file->namelen)
243 {
244 uar_set_error (uar, UAR_SYSCALL_ERROR, file->name);
245 fclose (stream);
246 return false;
247 }
248
249 if (file->type == UF_LINK)
250 {
251 if (fwrite (file->data.link.loc, 1, file->data.link.loclen,
252 stream)
253 != file->data.link.loclen)
254 {
255 uar_set_error (uar, UAR_SYSCALL_ERROR, file->name);
256 fclose (stream);
257 return false;
258 }
259 }
260 }
261
262 uar->stream = freopen (NULL, "rb", uar->stream);
263
264 if (uar->stream == NULL)
265 {
266 uar_set_error (uar, UAR_SYSCALL_ERROR, NULL);
267 fclose (stream);
268 return false;
269 }
270
271 size_t buf_size
272 = uar->header.size >= (1024 * 1024) ? 1024 * 1024 : uar->header.size;
273 uint8_t *buf = malloc (buf_size);
274
275 if (buf == NULL)
276 {
277 uar_set_error (uar, UAR_OUT_OF_MEMORY, NULL);
278 fclose (stream);
279 return false;
280 }
281
282 uint64_t size = uar->header.size;
283
284 while (size > 0 && !feof (uar->stream))
285 {
286 if (size < buf_size)
287 buf_size = size;
288
289 if (fread (buf, 1, buf_size, uar->stream) != buf_size)
290 {
291 uar_set_error (uar, UAR_SYSCALL_ERROR, NULL);
292 fclose (stream);
293 free (buf);
294 return false;
295 }
296
297 if (fwrite (buf, 1, buf_size, stream) != buf_size)
298 {
299 uar_set_error (uar, UAR_SYSCALL_ERROR, NULL);
300 fclose (stream);
301 free (buf);
302 return false;
303 }
304
305 if (size < buf_size)
306 buf_size = size;
307 else
308 size -= buf_size;
309 }
310
311 free (buf);
312 fclose (stream);
313 return true;
314 }
315
316 struct uar_archive *
317 uar_stream_create (void)
318 {
319 struct uar_archive *uar = uar_create ();
320 int cerrno;
321
322 if (uar == NULL)
323 return NULL;
324
325 uar->is_stream = true;
326 uar->stream = tmpfile ();
327
328 if (uar->stream == NULL)
329 goto uar_create_stream_error;
330
331 if (!uar_initialize (uar))
332 goto uar_create_stream_error;
333
334 goto uar_create_stream_ret;
335
336 uar_create_stream_error:
337 cerrno = errno;
338 uar_close (uar);
339 errno = cerrno;
340 uar = NULL;
341 uar_create_stream_ret:
342 return uar;
343 }
344
345 struct uar_file *
346 uar_stream_add_file (struct uar_archive *uar, const char *uar_filename,
347 const char *fs_filename, struct stat *stinfo)
348 {
349 assert (uar != NULL && "uar is NULL");
350 assert (uar->is_stream && "uar is not in stream mode");
351 assert (uar_filename != NULL && "uar_filename is NULL");
352 assert (fs_filename != NULL && "fs_filename is NULL");
353
354 if (uar->root == NULL)
355 {
356 if (uar_initialize_root (uar) == NULL)
357 return NULL;
358 }
359
360 struct stat custom_stinfo = { 0 };
361 enum uar_error ecode = UAR_SUCCESS;
362 void *buffer = NULL;
363 struct uar_file *file = NULL;
364 uint64_t uar_file_namelen = strlen (uar_filename);
365
366 if (uar_file_namelen > PATH_MAX)
367 {
368 uar_set_error (uar, UAR_INVALID_PATH, fs_filename);
369 return NULL;
370 }
371
372 bool contains_dot_dot
373 = (uar_file_namelen > 3 && uar_filename[0] == '.'
374 && uar_filename[1] == '.' && uar_filename[2] == '/');
375
376 if ((uar_file_namelen > 2 && uar_filename[0] == '.'
377 && uar_filename[1] == '/')
378 || contains_dot_dot)
379 {
380 if (uar->create_callback != NULL)
381 uar->create_callback (
382 uar, NULL, uar_filename, fs_filename, UAR_ELEVEL_WARNING,
383 contains_dot_dot ? "removing leading '..'"
384 : "removing leading '.'");
385
386 uar_filename = uar_filename + 1 + (uar_file_namelen == 1);
387 uar_file_namelen -= 1 + (uar_file_namelen == 1);
388 }
389
390 if (stinfo == NULL)
391 {
392 if (lstat (fs_filename, &custom_stinfo) != 0)
393 {
394 uar_set_error (uar, UAR_SYSCALL_ERROR, fs_filename);
395
396 if (uar->create_callback != NULL)
397 uar->create_callback (uar, NULL, uar_filename,
398 fs_filename, UAR_ELEVEL_WARNING,
399 strerror (errno));
400
401 return NULL;
402 }
403 else
404 stinfo = &custom_stinfo;
405 }
406
407 FILE *stream = fopen (fs_filename, "rb");
408
409 if (stream == NULL)
410 {
411 uar_set_error (uar, UAR_SYSCALL_ERROR, fs_filename);
412
413 if (uar->create_callback != NULL)
414 uar->create_callback (uar, NULL, uar_filename, fs_filename,
415 UAR_ELEVEL_WARNING, strerror (errno));
416
417 return NULL;
418 }
419
420 fseek (stream, 0, SEEK_END);
421 long size = ftell (stream);
422
423 if (size < 0)
424 {
425 ecode = UAR_SYSCALL_ERROR;
426 goto uar_stream_add_file_end;
427 }
428
429 fseek (stream, 0, SEEK_SET);
430
431 file = uar_file_create (uar_filename, uar_file_namelen, size,
432 uar->header.size);
433
434 if (file == NULL)
435 {
436 ecode = UAR_SYSCALL_ERROR;
437 goto uar_stream_add_file_end;
438 }
439
440 file->mode = stinfo->st_mode;
441 file->data.size = size;
442 file->mtime = stinfo->st_mtime;
443 uar->header.size += size;
444 uar->root->data.size += size;
445
446 if (!uar_add_file_entry (uar, file))
447 {
448 uar_file_destroy (file);
449 fclose (stream);
450 return NULL;
451 }
452
453 buffer = malloc (size);
454
455 if (buffer == NULL)
456 {
457 ecode = UAR_OUT_OF_MEMORY;
458 goto uar_stream_add_file_end;
459 }
460
461 if (size != 0 && fread (buffer, 1, size, stream) != (size_t) size)
462 {
463 ecode = UAR_SYSCALL_ERROR;
464 goto uar_stream_add_file_end;
465 }
466
467 if (fwrite (buffer, 1, size, uar->stream) != (size_t) size)
468 {
469 ecode = UAR_SYSCALL_ERROR;
470 goto uar_stream_add_file_end;
471 }
472
473 if (ecode == UAR_SUCCESS && uar->create_callback != NULL)
474 {
475 uar->create_callback (uar, file, uar_filename, fs_filename,
476 UAR_ELEVEL_NONE, NULL);
477 }
478
479 uar_stream_add_file_end:
480 if (ecode != UAR_SUCCESS && file != NULL)
481 uar_file_destroy (file);
482
483 if (buffer != NULL)
484 free (buffer);
485
486 uar_set_error (uar, ecode, fs_filename);
487 fclose (stream);
488 return ecode == UAR_SUCCESS ? file : NULL;
489 }
490
491 struct uar_file *
492 uar_stream_add_dir (struct uar_archive *uar, const char *uar_dirname,
493 const char *fs_dirname, struct stat *stinfo)
494 {
495 struct stat custom_stinfo = { 0 };
496 enum uar_error ecode = UAR_SUCCESS;
497 struct uar_file *file = NULL;
498 uint64_t size = 0;
499 DIR *dir = NULL;
500
501 if (stinfo == NULL)
502 {
503 if (lstat (fs_dirname, &custom_stinfo) != 0)
504 {
505 uar_set_error (uar, UAR_SYSCALL_ERROR, fs_dirname);
506
507 if (uar->create_callback != NULL)
508 uar->create_callback (uar, NULL, uar_dirname,
509 fs_dirname, UAR_ELEVEL_WARNING,
510 strerror (errno));
511
512 return NULL;
513 }
514 else
515 stinfo = &custom_stinfo;
516 }
517
518 file = uar_file_create (uar_dirname, 0, 0, uar->header.size);
519
520 if (file == NULL)
521 {
522 ecode = UAR_OUT_OF_MEMORY;
523 goto uar_stream_add_dir_error;
524 }
525
526 file->type = UF_DIR;
527 file->mode = stinfo->st_mode;
528 file->mtime = stinfo->st_mtime;
529
530 if (!uar_add_file_entry (uar, file))
531 {
532 ecode = UAR_OUT_OF_MEMORY;
533 goto uar_stream_add_dir_error;
534 }
535
536 dir = opendir (fs_dirname);
537
538 if (dir == NULL)
539 {
540 if (uar->create_callback != NULL)
541 uar->create_callback (uar, NULL, uar_dirname, fs_dirname,
542 UAR_ELEVEL_WARNING, strerror (errno));
543
544 ecode = UAR_SYSCALL_ERROR;
545 goto uar_stream_add_dir_error;
546 }
547
548 struct dirent *entry = NULL;
549
550 while ((entry = readdir (dir)) != NULL)
551 {
552 if (strcmp (entry->d_name, ".") == 0
553 || strcmp (entry->d_name, "..") == 0)
554 continue;
555
556 size_t dname_len = strlen (entry->d_name);
557 char *fs_fullpath = path_concat (fs_dirname, entry->d_name,
558 strlen (fs_dirname), dname_len);
559 char *uar_fullpath = path_concat (uar_dirname, entry->d_name,
560 strlen (uar_dirname), dname_len);
561
562 struct uar_file *entry_file
563 = uar_stream_add_entry (uar, uar_fullpath, fs_fullpath, NULL);
564
565 if (entry_file != NULL && entry_file->type != UF_LINK)
566 size += entry_file->data.size;
567
568 free (fs_fullpath);
569 free (uar_fullpath);
570 }
571
572 file->data.size = size;
573
574 if (ecode == UAR_SUCCESS && uar->create_callback != NULL)
575 {
576 uar->create_callback (uar, file, uar_dirname, fs_dirname,
577 UAR_ELEVEL_NONE, NULL);
578 }
579
580 goto uar_stream_add_dir_ret;
581 uar_stream_add_dir_error:
582 uar_set_error (uar, ecode, fs_dirname);
583 uar_stream_add_dir_ret:
584 if (dir != NULL)
585 closedir (dir);
586
587 if (ecode != UAR_SUCCESS && file != NULL)
588 uar_file_destroy (file);
589
590 return ecode == UAR_SUCCESS ? file : NULL;
591 }
592
593 struct uar_file *
594 uar_stream_add_link (struct uar_archive *uar, const char *uar_name,
595 const char *fs_name, struct stat *stinfo)
596 {
597 struct stat custom_stinfo = { 0 };
598 struct uar_file *file = NULL;
599
600 if (stinfo == NULL)
601 {
602 if (lstat (fs_name, &custom_stinfo) != 0)
603 {
604 uar_set_error (uar, UAR_SYSCALL_ERROR, fs_name);
605
606 if (uar->create_callback != NULL)
607 uar->create_callback (uar, NULL, uar_name, fs_name,
608 UAR_ELEVEL_WARNING,
609 strerror (errno));
610
611 return NULL;
612 }
613 else
614 stinfo = &custom_stinfo;
615 }
616
617 uint64_t uar_file_namelen = strlen (uar_name);
618
619 bool contains_dot_dot = (uar_file_namelen > 3 && uar_name[0] == '.'
620 && uar_name[1] == '.' && uar_name[2] == '/');
621
622 if ((uar_file_namelen > 2 && uar_name[0] == '.' && uar_name[1] == '/')
623 || contains_dot_dot)
624 {
625 if (uar->create_callback != NULL)
626 uar->create_callback (
627 uar, NULL, uar_name, fs_name, UAR_ELEVEL_WARNING,
628 contains_dot_dot ? "removing leading '..'"
629 : "removing leading '.'");
630
631 uar_name = uar_name + 1 + (uar_file_namelen == 1);
632 uar_file_namelen -= 1 + (uar_file_namelen == 1);
633 }
634
635 file = uar_file_create (uar_name, 0, 0, uar->header.size);
636
637 if (file == NULL)
638 {
639 uar_set_error (uar, UAR_OUT_OF_MEMORY, fs_name);
640 return NULL;
641 }
642
643 file->type = UF_LINK;
644 file->mode = stinfo->st_mode;
645 file->mtime = stinfo->st_mtime;
646
647 char link_buf[PATH_MAX] = { 0 };
648
649 ssize_t link_len = readlink (fs_name, link_buf, PATH_MAX);
650
651 if (link_len == -1)
652 {
653 uar_set_error (uar, UAR_SYSCALL_ERROR, fs_name);
654 uar_file_destroy (file);
655
656 if (uar->create_callback != NULL)
657 uar->create_callback (uar, NULL, uar_name, fs_name,
658 UAR_ELEVEL_WARNING, strerror (errno));
659
660 return NULL;
661 }
662
663 file->data.link.loclen = link_len;
664 file->data.link.loc = malloc (link_len + 1);
665
666 if (file->data.link.loc == NULL)
667 {
668 uar_set_error (uar, UAR_OUT_OF_MEMORY, fs_name);
669 uar_file_destroy (file);
670 return NULL;
671 }
672
673 memcpy (file->data.link.loc, link_buf, link_len);
674 file->data.link.loc[link_len] = 0;
675
676 if (!uar_add_file_entry (uar, file))
677 {
678 uar_file_destroy (file);
679 return NULL;
680 }
681
682 if (uar->create_callback != NULL && file != NULL)
683 uar->create_callback (uar, file, uar_name, fs_name, UAR_ELEVEL_NONE,
684 NULL);
685
686 return file;
687 }
688
689 struct uar_file *
690 uar_stream_add_entry (struct uar_archive *uar, const char *uar_name,
691 const char *fs_name, struct stat *stinfo)
692 {
693 assert (uar != NULL && "uar is NULL");
694 assert (uar->is_stream && "uar is not in stream mode");
695 assert (uar_name != NULL && "uar_name is NULL");
696 assert (fs_name != NULL && "fs_name is NULL");
697
698 struct stat custom_stinfo = { 0 };
699 struct uar_file *file = NULL;
700
701 if (stinfo == NULL)
702 {
703 if (lstat (fs_name, &custom_stinfo) != 0)
704 {
705 uar_set_error (uar, UAR_SYSCALL_ERROR, fs_name);
706 return NULL;
707 }
708 else
709 stinfo = &custom_stinfo;
710 }
711
712 if (S_ISREG (stinfo->st_mode))
713 {
714 file = uar_stream_add_file (uar, uar_name, fs_name, stinfo);
715
716 if (file == NULL)
717 {
718 return NULL;
719 }
720 }
721 else if (S_ISDIR (stinfo->st_mode))
722 file = uar_stream_add_dir (uar, uar_name, fs_name, stinfo);
723
724 else if (S_ISLNK (stinfo->st_mode))
725 file = uar_stream_add_link (uar, uar_name, fs_name, stinfo);
726 else
727 {
728 uar_set_error (uar, UAR_INVALID_FILE, fs_name);
729 return NULL;
730 }
731
732 if (file != NULL)
733 {
734 file->mode = stinfo->st_mode;
735 file->mtime = stinfo->st_mtime;
736 file->uid = stinfo->st_uid;
737 file->gid = stinfo->st_gid;
738 }
739
740 return file;
741 }
742
743 /* Validate the UAR archive header. */
744 bool
745 uar_stream_header_validate (struct uar_archive *uar)
746 {
747 /* Compare magic to ensure it's a valid UAR archive. */
748 if (memcmp (uar->header.magic, UAR_MAGIC, sizeof (UAR_MAGIC)) != 0)
749 {
750 uar_set_error (uar, UAR_INVALID_MAGIC, NULL);
751 return false;
752 }
753
754 /* Check if the version is supported. */
755 if (uar->header.version > UAR_MAX_SUPPORTED_VERSION)
756 {
757 uar_set_error (uar, UAR_UNSUPPORTED_VERSION, NULL);
758 return false;
759 }
760
761 /* Check if the data block size is valid, to prevent buffer overflow. If
762 it's larger than the stream size, it's invalid. This could be because
763 the archive is corrupted, or it's a malicious archive. */
764 if (uar->header.size > (uar->stream_size - sizeof (struct uar_header)))
765 {
766 uar_set_error (uar, UAR_INVALID_ARCHIVE, NULL);
767 return false;
768 }
769
770 /* At the moment, UAR doesn't support any flags. */
771 if (uar->header.flags != 0)
772 {
773 uar_set_error (uar, UAR_INVALID_ARCHIVE, NULL);
774 return false;
775 }
776
777 /* Check if the file is big enough to hold n number of files. */
778 if (uar->header.nfiles * sizeof (struct uar_file) > uar->header.size)
779 {
780 uar_set_error (uar, UAR_INVALID_ARCHIVE, NULL);
781 return false;
782 }
783
784 return true;
785 }
786
787 struct uar_archive *
788 uar_stream_open (const char *filename)
789 {
790 struct uar_archive *uar;
791 FILE *stream = fopen (filename, "rb");
792
793 if (stream == NULL)
794 return NULL;
795
796 uar = uar_create ();
797
798 if (uar == NULL)
799 return NULL;
800
801 uar->is_stream = true;
802 uar->stream = stream;
803
804 fseek (stream, 0, SEEK_END);
805 long size = ftell (stream);
806
807 if (size < 0 || size > INT64_MAX)
808 {
809 uar_set_error (uar, UAR_SYSCALL_ERROR, NULL);
810 return uar;
811 }
812
813 fseek (stream, 0, SEEK_SET);
814
815 if (((size_t) size) < sizeof (struct uar_header))
816 {
817 uar_set_error (uar, UAR_INVALID_ARCHIVE, NULL);
818 return uar;
819 }
820
821 if (fread (&uar->header, 1, sizeof (struct uar_header), stream)
822 != sizeof (struct uar_header))
823 {
824 uar_set_error (uar, UAR_SYSCALL_ERROR, NULL);
825 return uar;
826 }
827
828 uar->stream_size = size;
829
830 if (!uar_stream_header_validate (uar))
831 return uar;
832
833 uar->files = calloc (uar->header.nfiles, sizeof (struct uar_file *));
834
835 if (uar->files == NULL)
836 {
837 uar_set_error (uar, UAR_OUT_OF_MEMORY, NULL);
838 return uar;
839 }
840
841 uint64_t read_size = sizeof (struct uar_header)
842 + (uar->header.nfiles * sizeof (struct uar_file));
843
844 for (uint64_t i = 0; i < uar->header.nfiles; i++)
845 {
846 struct uar_file *file = malloc (sizeof (struct uar_file));
847
848 if (file == NULL)
849 {
850 uar_set_error (uar, UAR_OUT_OF_MEMORY, NULL);
851 return uar;
852 }
853
854 if (fread (file, 1, sizeof (struct uar_file), stream)
855 != sizeof (struct uar_file))
856 {
857 uar_set_error (uar, UAR_SYSCALL_ERROR, NULL);
858 free (file);
859 return uar;
860 }
861
862 /* Right after the file structure, the name of the file is stored,
863 with the length of the name stored in the namelen field.
864 First, we need to check if the namelen is valid.
865 */
866
867 if (file->namelen > PATH_MAX || file->namelen == 0
868 || read_size + file->namelen > ((uint64_t) size))
869 {
870 /* At a later stage, we might want to rather call a callback
871 function instead. */
872 uar_set_error (uar, UAR_INVALID_ARCHIVE, NULL);
873 free (file);
874 return uar;
875 }
876
877 file->name = malloc (file->namelen + 1);
878
879 if (file->name == NULL)
880 {
881 uar_set_error (uar, UAR_OUT_OF_MEMORY, NULL);
882 free (file);
883 return uar;
884 }
885
886 if (fread (file->name, 1, file->namelen, stream) != file->namelen)
887 {
888 uar_set_error (uar, UAR_SYSCALL_ERROR, NULL);
889 free (file->name);
890 free (file);
891 return uar;
892 }
893
894 file->name[file->namelen] = 0;
895
896 /* Next, we need to check if the file is a link. If it is, we need
897 to read the link location. */
898
899 if (file->type == UF_LINK)
900 {
901 if (file->data.link.loclen > PATH_MAX
902 || read_size + file->data.link.loclen
903 > ((uint64_t) size))
904 {
905 uar_set_error (uar, UAR_INVALID_ARCHIVE, NULL);
906 free (file->name);
907 free (file);
908 return uar;
909 }
910
911 file->data.link.loc = malloc (file->data.link.loclen + 1);
912
913 if (file->data.link.loc == NULL)
914 {
915 uar_set_error (uar, UAR_OUT_OF_MEMORY, NULL);
916 free (file->name);
917 free (file);
918 return uar;
919 }
920
921 if (fread (file->data.link.loc, 1, file->data.link.loclen,
922 stream)
923 != file->data.link.loclen)
924 {
925 uar_set_error (uar, UAR_SYSCALL_ERROR, NULL);
926 free (file->name);
927 free (file->data.link.loc);
928 free (file);
929 return uar;
930 }
931
932 file->data.link.loc[file->data.link.loclen] = 0;
933 }
934
935 uar->files[i] = file;
936 }
937
938 return uar;
939 }
940
941 struct uar_archive *
942 uar_open (const char *filename)
943 {
944 struct uar_archive *uar = NULL;
945 FILE *stream = NULL;
946 int cerrno;
947
948 errno = 0;
949 uar = uar_create ();
950
951 if (uar == NULL)
952 return NULL;
953
954 stream = fopen (filename, "rb");
955
956 if (stream == NULL)
957 {
958 uar_set_error (uar, UAR_IO_ERROR, NULL);
959 goto uar_open_ret;
960 }
961
962 fseek (stream, 0, SEEK_END);
963 size_t size = ftell (stream);
964 fseek (stream, 0, SEEK_SET);
965
966 if (fread (&uar->header, sizeof (struct uar_header), 1, stream) != 1)
967 {
968 uar_set_error (uar, UAR_IO_ERROR, NULL);
969 goto uar_open_ret;
970 }
971
972 if (memcmp (uar->header.magic, UAR_MAGIC, 4) != 0)
973 {
974 uar_set_error (uar, UAR_INVALID_MAGIC, NULL);
975 goto uar_open_ret;
976 }
977
978 uint64_t filearr_size = uar->header.nfiles * sizeof (struct uar_file);
979
980 if (filearr_size > size)
981 {
982 uar_set_error (uar, UAR_IO_ERROR, NULL);
983 goto uar_open_ret;
984 }
985
986 if (uar->header.size > size)
987 {
988 uar_set_error (uar, UAR_IO_ERROR, NULL);
989 goto uar_open_ret;
990 }
991
992 uar->files = calloc (uar->header.nfiles, sizeof (struct uar_file *));
993
994 if (uar->files == NULL)
995 {
996 uar_set_error (uar, UAR_OUT_OF_MEMORY, NULL);
997 goto uar_open_ret;
998 }
999
1000 for (uint64_t i = 0; i < uar->header.nfiles; i++)
1001 {
1002 struct uar_file *file = malloc (sizeof (struct uar_file));
1003
1004 if (file == NULL)
1005 {
1006 uar_set_error (uar, UAR_OUT_OF_MEMORY, NULL);
1007 goto uar_open_ret;
1008 }
1009
1010 if (fread (file, sizeof (struct uar_file), 1, stream) != 1)
1011 {
1012 uar_set_error (uar, UAR_IO_ERROR, NULL);
1013 goto uar_open_ret;
1014 }
1015
1016 if (file->namelen > PATH_MAX)
1017 {
1018 uar_set_error (uar, UAR_INVALID_PATH, NULL);
1019 goto uar_open_ret;
1020 }
1021
1022 file->name = malloc (file->namelen + 1);
1023
1024 if (file->name == NULL)
1025 {
1026 uar_set_error (uar, UAR_OUT_OF_MEMORY, NULL);
1027 goto uar_open_ret;
1028 }
1029
1030 if (fread (file->name, 1, file->namelen, stream) != file->namelen)
1031 {
1032 uar_set_error (uar, UAR_IO_ERROR, file->name);
1033 goto uar_open_ret;
1034 }
1035
1036 file->name[file->namelen] = 0;
1037 uar->files[i] = file;
1038 }
1039
1040 uar->buffer = malloc (uar->header.size + 1);
1041
1042 if (uar->buffer == NULL)
1043 {
1044 uar_set_error (uar, UAR_OUT_OF_MEMORY, NULL);
1045 goto uar_open_ret;
1046 }
1047
1048 if (fread (uar->buffer, 1, uar->header.size, stream) != uar->header.size)
1049 {
1050 uar_set_error (uar, UAR_IO_ERROR, NULL);
1051 goto uar_open_ret;
1052 }
1053
1054 uar_open_ret:
1055 cerrno = errno;
1056 fclose (stream);
1057 errno = cerrno;
1058 return uar;
1059 }
1060
1061 void
1062 uar_file_destroy (struct uar_file *file)
1063 {
1064 if (file == NULL)
1065 return;
1066
1067 if (file->type == UF_LINK)
1068 free (file->data.link.loc);
1069
1070 free (file->name);
1071 free (file);
1072 }
1073
1074 void
1075 uar_close (struct uar_archive *uar)
1076 {
1077 if (uar == NULL)
1078 return;
1079
1080 if (uar->is_stream)
1081 fclose (uar->stream);
1082 else
1083 free (uar->buffer);
1084
1085 for (uint64_t i = 0; i < uar->header.nfiles; i++)
1086 {
1087 struct uar_file *file = uar->files[i];
1088 uar_file_destroy (file);
1089 }
1090
1091 free (uar->err_file);
1092 free (uar->files);
1093 free (uar);
1094 }
1095
1096 bool
1097 uar_add_file_entry (struct uar_archive *restrict uar, struct uar_file *file)
1098 {
1099 if (uar == NULL || file == NULL)
1100 {
1101 uar_set_error (uar, UAR_INVALID_ARGUMENT, NULL);
1102 return false;
1103 }
1104
1105 uar->files = realloc (uar->files,
1106 (uar->header.nfiles + 1) * sizeof (struct uar_file));
1107
1108 if (uar->files == NULL)
1109 {
1110 uar_set_error (uar, UAR_OUT_OF_MEMORY, NULL);
1111 return false;
1112 }
1113
1114 uar->files[uar->header.nfiles] = file;
1115 uar->header.nfiles++;
1116
1117 return true;
1118 }
1119
1120 struct uar_file *
1121 uar_file_create (const char *name, uint64_t namelen, uint64_t size,
1122 uint32_t offset)
1123 {
1124 struct uar_file *file;
1125 int cerrno;
1126 bool abs = false;
1127
1128 if (namelen == 0)
1129 namelen = strlen (name);
1130
1131 if (namelen >= PATH_MAX)
1132 {
1133 errno = ENAMETOOLONG;
1134 return NULL;
1135 }
1136
1137 abs = name[0] == '/';
1138 namelen += (abs ? 0 : 1);
1139
1140 file = malloc (sizeof (struct uar_file));
1141
1142 if (file == NULL)
1143 return NULL;
1144
1145 bzero (file, sizeof (struct uar_file));
1146
1147 file->type = UF_FILE;
1148 file->mode = 0644;
1149 file->mtime = 0;
1150 file->uid = 0;
1151 file->gid = 0;
1152 file->name = malloc (namelen + 1);
1153
1154 if (file->name == NULL)
1155 {
1156 cerrno = errno;
1157 free (file);
1158 errno = cerrno;
1159 return NULL;
1160 }
1161
1162 if (!abs)
1163 file->name[0] = '/';
1164
1165 strncpy (file->name + (abs ? 0 : 1), name, namelen);
1166 file->name[namelen] = 0;
1167 file->namelen = namelen;
1168 file->data.size = size;
1169 file->offset = offset;
1170
1171 return file;
1172 }
1173
1174 struct uar_file *
1175 uar_add_file (struct uar_archive *restrict uar, const char *name,
1176 const char *path, struct stat *stinfo)
1177 {
1178 assert (uar != NULL && "uar is NULL");
1179 assert (name != NULL && "name is NULL");
1180 assert (path != NULL && "path is NULL");
1181 assert (!uar->is_stream && "uar in non-stream mode is not supported yet");
1182
1183 uint64_t namelen = strlen (name);
1184 struct stat *file_stinfo = stinfo, st_stinfo = { 0 };
1185
1186 if (namelen >= PATH_MAX)
1187 {
1188 uar_set_error (uar, UAR_INVALID_PATH, path);
1189 return NULL;
1190 }
1191
1192 if (file_stinfo == NULL)
1193 {
1194 if (lstat (path, &st_stinfo) != 0)
1195 {
1196 uar_set_error (uar, UAR_IO_ERROR, path);
1197 return NULL;
1198 }
1199
1200 file_stinfo = &st_stinfo;
1201 }
1202
1203 FILE *stream = fopen (path, "rb");
1204
1205 if (stream == NULL)
1206 {
1207 uar_set_error (uar, UAR_IO_ERROR, path);
1208 return NULL;
1209 }
1210
1211 fseek (stream, 0, SEEK_END);
1212 long size = ftell (stream);
1213
1214 if (size < 0)
1215 {
1216 uar_set_error (uar, UAR_IO_ERROR, path);
1217 fclose (stream);
1218 return NULL;
1219 }
1220
1221 fseek (stream, 0, SEEK_SET);
1222
1223 struct uar_file *file
1224 = uar_file_create (name, namelen, size, uar->header.size);
1225
1226 if (file == NULL)
1227 {
1228 uar_set_error (uar, UAR_OUT_OF_MEMORY, path);
1229 fclose (stream);
1230 return NULL;
1231 }
1232
1233 file->mtime = file_stinfo->st_mtime;
1234 uar->header.size += size;
1235
1236 if (!uar_add_file_entry (uar, file))
1237 {
1238 uar_file_destroy (file);
1239 fclose (stream);
1240 return NULL;
1241 }
1242
1243 uar->buffer = realloc (uar->buffer, uar->header.size);
1244
1245 if (uar->buffer == NULL)
1246 {
1247 uar_set_error (uar, UAR_OUT_OF_MEMORY, path);
1248 fclose (stream);
1249 return NULL;
1250 }
1251
1252 if (size != 0 && fread (uar->buffer + file->offset, size, 1, stream) != 1)
1253 {
1254 uar_set_error (uar, UAR_IO_ERROR, path);
1255 fclose (stream);
1256 return NULL;
1257 }
1258
1259 fclose (stream);
1260 return file;
1261 }
1262
1263 struct uar_file *
1264 uar_add_dir (struct uar_archive *uar, const char *dname, const char *path,
1265 bool (*callback) (struct uar_file *file, const char *fullname,
1266 const char *fullpath))
1267 {
1268 assert (uar != NULL && "uar is NULL");
1269 assert (dname != NULL && "dname is NULL");
1270 assert (path != NULL && "path is NULL");
1271 assert (!uar->is_stream && "uar in non-stream mode is not supported yet");
1272
1273 char *name = (char *) dname;
1274 bool free_name = false;
1275 int cerrno;
1276 uint64_t namelen;
1277
1278 if (strcmp (name, ".") == 0)
1279 {
1280 name = strdup ("/");
1281 free_name = true;
1282 namelen = 1;
1283 }
1284 else
1285 namelen = strlen (name);
1286
1287 if (namelen >= PATH_MAX)
1288 {
1289 uar_set_error (uar, UAR_INVALID_PATH, path);
1290 return NULL;
1291 }
1292
1293 DIR *dir = opendir (path);
1294 struct dirent *entry = NULL;
1295
1296 if (dir == NULL)
1297 {
1298 uar_set_error (uar, UAR_INVALID_FILE, path);
1299 return NULL;
1300 }
1301
1302 struct uar_file *dir_file
1303 = uar_file_create (name, namelen, 0, uar->header.size);
1304 uint64_t dir_size = 0;
1305
1306 dir_file->type = UF_DIR;
1307
1308 if (callback != NULL && !callback (dir_file, name, path))
1309 {
1310 uar_set_error (uar, UAR_SUCCESS, NULL);
1311 uar_file_destroy (dir_file);
1312 return NULL;
1313 }
1314
1315 if (!uar_add_file_entry (uar, dir_file))
1316 {
1317 uar_file_destroy (dir_file);
1318 return NULL;
1319 }
1320
1321 while ((entry = readdir (dir)) != NULL)
1322 {
1323 if (strcmp (entry->d_name, ".") == 0
1324 || strcmp (entry->d_name, "..") == 0)
1325 continue;
1326
1327 struct stat stinfo = { 0 };
1328
1329 if (256 + namelen >= PATH_MAX)
1330 {
1331 uar_set_error (uar, UAR_INVALID_PATH, path);
1332 uar_file_destroy (dir_file);
1333 closedir (dir);
1334 return NULL;
1335 }
1336
1337 uint64_t dnamelen = strlen (entry->d_name);
1338
1339 char *fullpath
1340 = path_concat (path, entry->d_name, strlen (path), dnamelen);
1341 assert (fullpath != NULL);
1342
1343 char *fullname
1344 = path_concat (name, entry->d_name, namelen, dnamelen);
1345 assert (fullname != NULL);
1346
1347 if (lstat (fullpath, &stinfo) != 0)
1348 {
1349 uar_set_error (uar, UAR_IO_ERROR, fullpath);
1350 goto uar_add_dir_error;
1351 }
1352
1353 if (S_ISREG (stinfo.st_mode))
1354 {
1355 struct uar_file *file
1356 = uar_add_file (uar, fullname, fullpath, &stinfo);
1357
1358 if (file == NULL)
1359 {
1360 goto uar_add_dir_error;
1361 }
1362
1363 if (callback != NULL
1364 && !callback (file, fullname, fullpath))
1365 {
1366 uar_set_error (uar, UAR_SUCCESS, NULL);
1367 goto uar_add_dir_error;
1368 }
1369
1370 file->mode = stinfo.st_mode;
1371 dir_size += file->data.size;
1372 }
1373 else if (S_ISDIR (stinfo.st_mode))
1374 {
1375 struct uar_file *direntry
1376 = uar_add_dir (uar, fullname, fullpath, callback);
1377
1378 if (direntry == NULL)
1379 {
1380 goto uar_add_dir_error;
1381 }
1382
1383 direntry->mode = stinfo.st_mode;
1384 dir_size += direntry->data.size;
1385 }
1386 else
1387 assert (false && "Not supported");
1388
1389 free (fullpath);
1390 free (fullname);
1391
1392 continue;
1393
1394 uar_add_dir_error:
1395 cerrno = errno;
1396 uar_file_destroy (dir_file);
1397 free (fullpath);
1398 free (fullname);
1399 errno = cerrno;
1400 goto uar_add_dir_end;
1401 }
1402
1403 dir_file->data.size = dir_size;
1404
1405 uar_add_dir_end:
1406 cerrno = errno;
1407 closedir (dir);
1408
1409 if (free_name)
1410 free (name);
1411
1412 errno = cerrno;
1413 return dir_file;
1414 }
1415
1416 void
1417 uar_file_set_mode (struct uar_file *file, mode_t mode)
1418 {
1419 file->mode = mode;
1420 }
1421
1422 static void
1423 uar_debug_print_file_contents (const struct uar_archive *uar,
1424 struct uar_file *file)
1425 {
1426 printf (" contents:\n");
1427 printf ("==================\n");
1428 fflush (stdout);
1429
1430 ssize_t size
1431 = write (STDOUT_FILENO, uar->buffer + file->offset, file->data.size);
1432
1433 if (size == -1 || ((uint64_t) size) != file->data.size)
1434 {
1435 perror ("write");
1436 return;
1437 }
1438
1439 putchar ('\n');
1440 printf ("==================\n");
1441 }
1442
1443 void
1444 uar_debug_print (const struct uar_archive *uar, bool print_file_contents)
1445 {
1446 printf ("uar_archive:\n");
1447 printf (" magic: %02x %02x %02x %02x\n", uar->header.magic[0],
1448 uar->header.magic[1], uar->header.magic[2], uar->header.magic[3]);
1449 printf (" version: %u\n", uar->header.version);
1450 printf (" flags: %u\n", uar->header.flags);
1451 printf (" nfiles: %lu\n", uar->header.nfiles);
1452 printf (" size: %lu\n", uar->header.size);
1453 printf (" stream?: %i\n", uar->is_stream);
1454
1455 for (uint64_t i = 0; i < uar->header.nfiles; i++)
1456 {
1457 struct uar_file *file = uar->files[i];
1458
1459 printf (" %s[%lu]:\n",
1460 file->type == UF_FILE ? "file"
1461 : file->type == UF_DIR ? "directory"
1462 : "link",
1463 i);
1464 printf (" name: \033[1m%s%s\033[0m\n", uar_file_get_name (file),
1465 file->type == UF_DIR
1466 ? file->name[0] == '/' && file->namelen == 1 ? "" : "/"
1467 : file->type == UF_LINK ? "@"
1468 : "");
1469 printf (" offset: %lu\n", file->offset);
1470 printf (" mode: %04o\n", file->mode);
1471
1472 if (file->type == UF_LINK)
1473 printf (" points to: %s\n", file->data.link.loc);
1474 else
1475 printf (" size: %lu\n", file->data.size);
1476
1477 if (file->type == UF_FILE && print_file_contents)
1478 uar_debug_print_file_contents (uar, file);
1479 }
1480 }
1481
1482 bool
1483 uar_write (struct uar_archive *uar, const char *filename)
1484 {
1485 FILE *stream = fopen (filename, "wb");
1486
1487 if (stream == NULL)
1488 {
1489 uar_set_error (uar, UAR_IO_ERROR, filename);
1490 return false;
1491 }
1492
1493 if (fwrite (&uar->header, sizeof (struct uar_header), 1, stream) != 1)
1494 {
1495 uar_set_error (uar, UAR_IO_ERROR, filename);
1496 fclose (stream);
1497 return false;
1498 }
1499
1500 for (uint64_t i = 0; i < uar->header.nfiles; i++)
1501 {
1502 struct uar_file *file = uar->files[i];
1503
1504 if (fwrite (file, sizeof (struct uar_file), 1, stream) != 1)
1505 {
1506 uar_set_error (uar, UAR_IO_ERROR, file->name);
1507 fclose (stream);
1508 return false;
1509 }
1510
1511 if (fwrite (file->name, 1, file->namelen, stream) != file->namelen)
1512 {
1513 uar_set_error (uar, UAR_IO_ERROR, file->name);
1514 fclose (stream);
1515 return false;
1516 }
1517 }
1518
1519 if (fwrite (uar->buffer, 1, uar->header.size, stream) != uar->header.size)
1520 {
1521 uar_set_error (uar, UAR_IO_ERROR, NULL);
1522 fclose (stream);
1523 return false;
1524 }
1525
1526 fclose (stream);
1527 return true;
1528 }
1529
1530 bool
1531 uar_extract (struct uar_archive *uar, const char *cwd,
1532 bool (*callback) (struct uar_file *file))
1533 {
1534 if (cwd != NULL && chdir (cwd) != 0)
1535 {
1536 uar_set_error (uar, UAR_SYSTEM_ERROR, NULL);
1537 return false;
1538 }
1539
1540 for (uint64_t i = 0; i < uar->header.nfiles; i++)
1541 {
1542 struct uar_file *file = uar->files[i];
1543
1544 if (callback != NULL && !callback (file))
1545 return false;
1546
1547 char *name = file->name;
1548
1549 if (name[0] == '/')
1550 name += 2;
1551
1552 switch (file->type)
1553 {
1554 case UF_FILE:
1555 {
1556 FILE *stream = fopen (name, "wb");
1557
1558 if (stream == NULL)
1559 {
1560 uar_set_error (uar, UAR_IO_ERROR, name);
1561 return false;
1562 }
1563
1564 if (fwrite (uar->buffer + file->offset, 1,
1565 file->data.size, stream)
1566 != file->data.size)
1567 {
1568 uar_set_error (uar, UAR_IO_ERROR, name);
1569 return false;
1570 }
1571
1572 fchmod (fileno (stream), file->mode & 07777);
1573 fclose (stream);
1574 }
1575 break;
1576
1577 case UF_DIR:
1578 if (file->namelen == 1 && file->name[0] == '/')
1579 continue;
1580
1581 if (mkdir (name, file->mode) != 0)
1582 {
1583 uar_set_error (uar, UAR_SYSTEM_ERROR, name);
1584 return false;
1585 }
1586
1587 break;
1588
1589 default:
1590 assert (false && "unknown file type");
1591 return false;
1592 }
1593 }
1594
1595 return true;
1596 }
1597
1598 bool
1599 uar_iterate (struct uar_archive *uar,
1600 bool (*callback) (struct uar_file *file, void *data), void *data)
1601 {
1602 for (uint64_t i = 0; i < uar->header.nfiles; i++)
1603 {
1604 struct uar_file *file = uar->files[i];
1605
1606 if (!callback (file, data))
1607 return false;
1608 }
1609
1610 return true;
1611 }
1612
1613 const char *
1614 uar_file_get_name (const struct uar_file *file)
1615 {
1616 return file->name;
1617 }
1618
1619 enum uar_file_type
1620 uar_file_get_type (const struct uar_file *file)
1621 {
1622 return file->type;
1623 }
1624
1625 mode_t
1626 uar_file_get_mode (const struct uar_file *file)
1627 {
1628 return file->mode;
1629 }
1630
1631 uint64_t
1632 uar_file_get_size (const struct uar_file *file)
1633 {
1634 if (file->type == UF_LINK)
1635 return 0;
1636
1637 return file->data.size;
1638 }
1639
1640 uint64_t
1641 uar_file_get_namelen (const struct uar_file *file)
1642 {
1643 return file->namelen;
1644 }
1645
1646 uint64_t
1647 uar_get_file_count (const struct uar_archive *restrict uar)
1648 {
1649 return uar->header.nfiles;
1650 }
1651
1652 time_t
1653 uar_file_get_mtime (const struct uar_file *file)
1654 {
1655 return file->mtime;
1656 }
1657
1658 const char *
1659 uar_get_error_file (const struct uar_archive *uar)
1660 {
1661 return uar->err_file;
1662 }
1663
1664 uid_t
1665 uar_file_get_uid (const struct uar_file *file)
1666 {
1667 return file->uid;
1668 }
1669
1670 gid_t
1671 uar_file_get_gid (const struct uar_file *file)
1672 {
1673 return file->gid;
1674 }

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26