• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2018 The Wuffs Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //    https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 // ----------------
16 
17 /*
18 zcat decodes gzip'ed data to stdout. It is similar to the standard /bin/zcat
19 program, except that this example program only reads from stdin. On Linux, it
20 also self-imposes a SECCOMP_MODE_STRICT sandbox. To run:
21 
22 $CC zcat.c && ./a.out < ../../test/data/romeo.txt.gz; rm -f a.out
23 
24 for a C compiler $CC, such as clang or gcc.
25 */
26 
27 #include <errno.h>
28 #include <unistd.h>
29 
30 // Wuffs ships as a "single file C library" or "header file library" as per
31 // https://github.com/nothings/stb/blob/master/docs/stb_howto.txt
32 //
33 // To use that single file as a "foo.c"-like implementation, instead of a
34 // "foo.h"-like header, #define WUFFS_IMPLEMENTATION before #include'ing or
35 // compiling it.
36 #define WUFFS_IMPLEMENTATION
37 
38 // Defining the WUFFS_CONFIG__MODULE* macros are optional, but it lets users of
39 // release/c/etc.c whitelist which parts of Wuffs to build. That file contains
40 // the entire Wuffs standard library, implementing a variety of codecs and file
41 // formats. Without this macro definition, an optimizing compiler or linker may
42 // very well discard Wuffs code for unused codecs, but listing the Wuffs
43 // modules we use makes that process explicit. Preprocessing means that such
44 // code simply isn't compiled.
45 #define WUFFS_CONFIG__MODULES
46 #define WUFFS_CONFIG__MODULE__BASE
47 #define WUFFS_CONFIG__MODULE__CRC32
48 #define WUFFS_CONFIG__MODULE__DEFLATE
49 #define WUFFS_CONFIG__MODULE__GZIP
50 
51 // If building this program in an environment that doesn't easily accommodate
52 // relative includes, you can use the script/inline-c-relative-includes.go
53 // program to generate a stand-alone C file.
54 #include "../../release/c/wuffs-unsupported-snapshot.c"
55 
56 #if defined(__linux__)
57 #include <linux/prctl.h>
58 #include <linux/seccomp.h>
59 #include <sys/prctl.h>
60 #include <sys/syscall.h>
61 #define WUFFS_EXAMPLE_USE_SECCOMP
62 #endif
63 
64 #ifndef DST_BUFFER_SIZE
65 #define DST_BUFFER_SIZE (128 * 1024)
66 #endif
67 
68 #ifndef SRC_BUFFER_SIZE
69 #define SRC_BUFFER_SIZE (128 * 1024)
70 #endif
71 
72 #define WORK_BUFFER_SIZE WUFFS_GZIP__DECODER_WORKBUF_LEN_MAX_INCL_WORST_CASE
73 
74 uint8_t dst_buffer[DST_BUFFER_SIZE];
75 uint8_t src_buffer[SRC_BUFFER_SIZE];
76 #if WORK_BUFFER_SIZE > 0
77 uint8_t work_buffer[WORK_BUFFER_SIZE];
78 #else
79 // Not all C/C++ compilers support 0-length arrays.
80 uint8_t work_buffer[1];
81 #endif
82 
83 // ignore_return_value suppresses errors from -Wall -Werror.
ignore_return_value(int ignored)84 static void ignore_return_value(int ignored) {}
85 
decode()86 static const char* decode() {
87   wuffs_gzip__decoder dec;
88   const char* status =
89       wuffs_gzip__decoder__initialize(&dec, sizeof dec, WUFFS_VERSION, 0);
90   if (status) {
91     return status;
92   }
93 
94   wuffs_base__io_buffer dst;
95   dst.data.ptr = dst_buffer;
96   dst.data.len = DST_BUFFER_SIZE;
97   dst.meta.wi = 0;
98   dst.meta.ri = 0;
99   dst.meta.pos = 0;
100   dst.meta.closed = false;
101 
102   wuffs_base__io_buffer src;
103   src.data.ptr = src_buffer;
104   src.data.len = SRC_BUFFER_SIZE;
105   src.meta.wi = 0;
106   src.meta.ri = 0;
107   src.meta.pos = 0;
108   src.meta.closed = false;
109 
110   while (true) {
111     const int stdin_fd = 0;
112     ssize_t n =
113         read(stdin_fd, src.data.ptr + src.meta.wi, src.data.len - src.meta.wi);
114     if (n < 0) {
115       if (errno != EINTR) {
116         return strerror(errno);
117       }
118       continue;
119     }
120     src.meta.wi += n;
121     if (n == 0) {
122       src.meta.closed = true;
123     }
124 
125     while (true) {
126       status = wuffs_gzip__decoder__decode_io_writer(
127           &dec, &dst, &src,
128           wuffs_base__make_slice_u8(work_buffer, WORK_BUFFER_SIZE));
129 
130       if (dst.meta.wi) {
131         // TODO: handle EINTR and other write errors; see "man 2 write".
132         const int stdout_fd = 1;
133         ignore_return_value(write(stdout_fd, dst_buffer, dst.meta.wi));
134         dst.meta.ri = dst.meta.wi;
135         wuffs_base__io_buffer__compact(&dst);
136       }
137 
138       if (status == wuffs_base__suspension__short_read) {
139         break;
140       }
141       if (status == wuffs_base__suspension__short_write) {
142         continue;
143       }
144       return status;
145     }
146 
147     wuffs_base__io_buffer__compact(&src);
148     if (src.meta.wi == src.data.len) {
149       return "internal error: no I/O progress possible";
150     }
151   }
152 }
153 
fail(const char * msg)154 int fail(const char* msg) {
155   const int stderr_fd = 2;
156   ignore_return_value(write(stderr_fd, msg, strnlen(msg, 4095)));
157   ignore_return_value(write(stderr_fd, "\n", 1));
158   return 1;
159 }
160 
main(int argc,char ** argv)161 int main(int argc, char** argv) {
162 #if defined(WUFFS_EXAMPLE_USE_SECCOMP)
163   prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT);
164 #endif
165 
166   const char* status = decode();
167   int status_code = status ? fail(status) : 0;
168 
169 #if defined(WUFFS_EXAMPLE_USE_SECCOMP)
170   // Call SYS_exit explicitly instead of SYS_exit_group implicitly.
171   // SECCOMP_MODE_STRICT allows only the former.
172   syscall(SYS_exit, status_code);
173 #endif
174   return status_code;
175 }
176