1 
2 #include <stdlib.h>
3 #include <time.h>
4 #include <errno.h>
5 #include <sys/stat.h>
6 #include <sys/time.h>
7 
8 #ifdef _WIN32
9 #include <direct.h>		/* _mkdir */
10 #include <windows.h>
11 #endif
12 
13 #include <Rinternals.h>
14 
15 #include "miniz.h"
16 #include "zip.h"
17 
R_zip_list(SEXP zipfile)18 SEXP R_zip_list(SEXP zipfile) {
19   const char *czipfile = CHAR(STRING_ELT(zipfile, 0));
20   size_t num_files;
21   unsigned int i;
22   SEXP result = R_NilValue;
23   mz_bool status;
24   mz_zip_archive zip_archive;
25 
26   FILE *fh;
27   wchar_t *uzipfile = NULL;
28   size_t uzipfile_len = 0;
29 
30 #ifdef _WIN32
31   if (zip__utf8_to_utf16(czipfile, &uzipfile, &uzipfile_len)) {
32     if (uzipfile) free(uzipfile);
33     error("Cannot convert zip file name to unicode");
34   }
35   fh = zip_long_wfopen(uzipfile, L"rb");
36 #else
37   fh = fopen(czipfile, "rb");
38 #endif
39 
40   if (fh == NULL) {
41     if (uzipfile) free(uzipfile);
42     error("Cannot open zip file `%s`");
43   }
44 
45   fseek(fh, 0, SEEK_END);
46   size_t file_size = ftell(fh);
47   fseek(fh, 0, SEEK_SET);
48 
49   memset(&zip_archive, 0, sizeof(zip_archive));
50   status = mz_zip_reader_init_cfile(&zip_archive, fh, file_size, 0);
51   if (!status) {
52     fclose(fh);
53     free(uzipfile);
54     error("Cannot open zip file `%s`", czipfile);
55   }
56 
57   num_files = mz_zip_reader_get_num_files(&zip_archive);
58   result = PROTECT(allocVector(VECSXP, 7));
59   SET_VECTOR_ELT(result, 0, allocVector(STRSXP, num_files));
60   SET_VECTOR_ELT(result, 1, allocVector(REALSXP, num_files));
61   SET_VECTOR_ELT(result, 2, allocVector(REALSXP, num_files));
62   SET_VECTOR_ELT(result, 3, allocVector(INTSXP, num_files));
63   SET_VECTOR_ELT(result, 4, allocVector(INTSXP, num_files));
64   SET_VECTOR_ELT(result, 5, allocVector(INTSXP, num_files));
65   SET_VECTOR_ELT(result, 6, allocVector(REALSXP, num_files));
66 
67   for (i = 0; i < num_files; i++) {
68     mz_zip_archive_file_stat file_stat;
69     mode_t mode;
70     status = mz_zip_reader_file_stat (&zip_archive, i, &file_stat);
71     if (!status) goto cleanup;
72 
73     SET_STRING_ELT(VECTOR_ELT(result, 0), i, mkChar(file_stat.m_filename));
74     REAL(VECTOR_ELT(result, 1))[i] = file_stat.m_comp_size;
75     REAL(VECTOR_ELT(result, 2))[i] = file_stat.m_uncomp_size;
76     INTEGER(VECTOR_ELT(result, 3))[i] = (int) file_stat.m_time;
77     zip_get_permissions(&file_stat, &mode);
78     INTEGER(VECTOR_ELT(result, 4))[i] = (int) mode;
79     INTEGER(VECTOR_ELT(result, 5))[i] = (int) file_stat.m_crc32;
80     REAL(VECTOR_ELT(result, 6))[i] = (double) file_stat.m_local_header_ofs;
81   }
82 
83   fclose(fh);
84   free(uzipfile);
85   mz_zip_reader_end(&zip_archive);
86   UNPROTECT(1);
87   return result;
88 
89  cleanup:
90   fclose(fh);
91   mz_zip_reader_end(&zip_archive);
92   error("Cannot list zip entries, corrupt zip file?");
93   return result;
94 }
95 
R_zip_error_handler(const char * reason,const char * file,int line,int zip_errno,int eno)96 void R_zip_error_handler(const char *reason, const char *file,
97 			 int line, int zip_errno, int eno) {
98   error("zip error: `%s` in file `%s:%i`", reason, file, line);
99 }
100 
R_zip_zip(SEXP zipfile,SEXP keys,SEXP files,SEXP dirs,SEXP mtime,SEXP compression_level,SEXP append)101 SEXP R_zip_zip(SEXP zipfile, SEXP keys, SEXP files, SEXP dirs, SEXP mtime,
102 	       SEXP compression_level, SEXP append) {
103 
104   const char *czipfile = CHAR(STRING_ELT(zipfile, 0));
105   const char **ckeys = 0, **cfiles = 0;
106   int *cdirs = INTEGER(dirs);
107   double *cmtimes = REAL(mtime);
108   int ccompression_level = INTEGER(compression_level)[0];
109   int cappend = LOGICAL(append)[0];
110   int i, n = LENGTH(keys);
111 
112   /* The reason we allocate n+1 here is that otherwise R_alloc will
113      return a NULL pointer for n == 0, and zip_unzip interprets that
114      as extracting the whole archive. */
115 
116   ckeys  = (const char **) R_alloc(n + 1, sizeof(char*));
117   cfiles = (const char **) R_alloc(n + 1, sizeof(char*));
118   for (i = 0; i < n; i++) {
119     ckeys [i] = CHAR(STRING_ELT(keys,  i));
120     cfiles[i] = CHAR(STRING_ELT(files, i));
121   }
122 
123   zip_set_error_handler(R_zip_error_handler);
124 
125   zip_zip(czipfile, n, ckeys, cfiles, cdirs, cmtimes, ccompression_level,
126 	  cappend);
127 
128   return R_NilValue;
129 }
130 
R_zip_unzip(SEXP zipfile,SEXP files,SEXP overwrite,SEXP junkpaths,SEXP exdir)131 SEXP R_zip_unzip(SEXP zipfile, SEXP files, SEXP overwrite, SEXP junkpaths,
132 		 SEXP exdir) {
133 
134   const char *czipfile = CHAR(STRING_ELT(zipfile, 0));
135   int coverwrite = LOGICAL(overwrite)[0];
136   int cjunkpaths = LOGICAL(junkpaths)[0];
137   const char *cexdir = CHAR(STRING_ELT(exdir, 0));
138   int allfiles = isNull(files);
139   int i, n = allfiles ? 0 : LENGTH(files);
140   const char **cfiles = 0;
141 
142   if (!isNull(files)) {
143     /* The reason we allocate n+1 here is that otherwise R_alloc will
144        return a NULL pointer for n == 0, and zip_unzip interprets that
145        as extracting the whole archive. */
146     cfiles = (const char**) R_alloc(n + 1, sizeof(char*));
147     for (i = 0; i < n; i++) cfiles[i] = CHAR(STRING_ELT(files, i));
148   }
149 
150   zip_set_error_handler(R_zip_error_handler);
151   zip_unzip(czipfile, cfiles, n, coverwrite, cjunkpaths, cexdir);
152 
153   return R_NilValue;
154 }
155 
156 
157 #ifdef __APPLE__
158 #include <fcntl.h>
159 #include <unistd.h>
160 #endif
161 
162 
163 #ifdef _WIN32
164 
165 int zip__utf8_to_utf16(const char* s, wchar_t** buffer,
166                        size_t *buffer_size);
167 
168 #endif
169 
R_make_big_file(SEXP filename,SEXP mb)170 SEXP R_make_big_file(SEXP filename, SEXP mb) {
171 
172 #ifdef _WIN32
173 
174   const char *cfilename = CHAR(STRING_ELT(filename, 0));
175   LARGE_INTEGER li;
176 
177   wchar_t *wfilename = NULL;
178   size_t wfilename_size = 0;
179 
180   if (zip__utf8_to_utf16(cfilename, &wfilename, &wfilename_size)) {
181     error("utf8 -> utf16 conversion");
182   }
183 
184   HANDLE h = CreateFileW(
185     wfilename,
186     GENERIC_WRITE,
187     FILE_SHARE_DELETE,
188     NULL,
189     CREATE_NEW,
190     FILE_ATTRIBUTE_NORMAL,
191     NULL);
192   if (h == INVALID_HANDLE_VALUE) {
193     if (wfilename) free(wfilename);
194     error("Cannot create big file");
195   }
196 
197   li.QuadPart = INTEGER(mb)[0] * 1024.0 * 1024.0;
198   li.LowPart = SetFilePointer(h, li.LowPart, &li.HighPart, FILE_BEGIN);
199 
200   if (0xffffffff == li.LowPart && GetLastError() != NO_ERROR) {
201     CloseHandle(h);
202     if (wfilename) free(wfilename);
203     error("Cannot create big file");
204   }
205 
206   if (!SetEndOfFile(h)) {
207     CloseHandle(h);
208     if (wfilename) free(wfilename);
209     error("Cannot create big file");
210   }
211 
212   if (wfilename) free(wfilename);
213   CloseHandle(h);
214 
215 #endif
216 
217 #ifdef __APPLE__
218 
219   const char *cfilename = CHAR(STRING_ELT(filename, 0));
220   int fd = open(cfilename, O_WRONLY | O_CREAT);
221   double sz = INTEGER(mb)[0] * 1024.0 * 1024.0;
222   fstore_t store = { F_ALLOCATECONTIG, F_PEOFPOSMODE, 0, (off_t) sz };
223   // Try to get a continous chunk of disk space
224   int ret = fcntl(fd, F_PREALLOCATE, &store);
225   if (-1 == ret) {
226     // OK, perhaps we are too fragmented, allocate non-continuous
227     store.fst_flags = F_ALLOCATEALL;
228     ret = fcntl(fd, F_PREALLOCATE, &store);
229     if (-1 == ret) error("Cannot create big file");
230   }
231 
232   if (ftruncate(fd, (off_t) sz)) {
233     close(fd);
234     error("Cannot create big file");
235   }
236 
237   close(fd);
238 
239 #endif
240 
241 #ifndef _WIN32
242 #ifndef __APPLE__
243   error("cannot create big file (only implemented for windows and macos");
244 #endif
245 #endif
246 
247   return R_NilValue;
248 }
249