1 /*
2   zip_source_file_win32_named.c -- source for Windows file opened by name
3   Copyright (C) 1999-2020 Dieter Baron and Thomas Klausner
4 
5   This file is part of libzip, a library to manipulate ZIP archives.
6   The authors can be contacted at <libzip@nih.at>
7 
8   Redistribution and use in source and binary forms, with or without
9   modification, are permitted provided that the following conditions
10   are met:
11   1. Redistributions of source code must retain the above copyright
12   notice, this list of conditions and the following disclaimer.
13   2. Redistributions in binary form must reproduce the above copyright
14   notice, this list of conditions and the following disclaimer in
15   the documentation and/or other materials provided with the
16   distribution.
17   3. The names of the authors may not be used to endorse or promote
18   products derived from this software without specific prior
19   written permission.
20 
21   THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
22   OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24   ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
25   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
27   GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
29   IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30   OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
31   IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33 
34 #include "zip_source_file_win32.h"
35 
36 static zip_int64_t _zip_win32_named_op_commit_write(zip_source_file_context_t *ctx);
37 static zip_int64_t _zip_win32_named_op_create_temp_output(zip_source_file_context_t *ctx);
38 static bool _zip_win32_named_op_open(zip_source_file_context_t *ctx);
39 static zip_int64_t _zip_win32_named_op_remove(zip_source_file_context_t *ctx);
40 static void _zip_win32_named_op_rollback_write(zip_source_file_context_t *ctx);
41 static bool _zip_win32_named_op_stat(zip_source_file_context_t *ctx, zip_source_file_stat_t *st);
42 static char *_zip_win32_named_op_string_duplicate(zip_source_file_context_t *ctx, const char *string);
43 static zip_int64_t _zip_win32_named_op_write(zip_source_file_context_t *ctx, const void *data, zip_uint64_t len);
44 
45 static HANDLE win32_named_open(zip_source_file_context_t *ctx, const char *name, bool temporary, PSECURITY_ATTRIBUTES security_attributes);
46 
47 /* clang-format off */
48 zip_source_file_operations_t _zip_source_file_win32_named_ops = {
49     _zip_win32_op_close,
50     _zip_win32_named_op_commit_write,
51     _zip_win32_named_op_create_temp_output,
52     NULL,
53     _zip_win32_named_op_open,
54     _zip_win32_op_read,
55     _zip_win32_named_op_remove,
56     _zip_win32_named_op_rollback_write,
57     _zip_win32_op_seek,
58     _zip_win32_named_op_stat,
59     _zip_win32_named_op_string_duplicate,
60     _zip_win32_op_tell,
61     _zip_win32_named_op_write
62 };
63 /* clang-format on */
64 
65 static zip_int64_t
_zip_win32_named_op_commit_write(zip_source_file_context_t * ctx)66 _zip_win32_named_op_commit_write(zip_source_file_context_t *ctx) {
67     zip_win32_file_operations_t *file_ops = (zip_win32_file_operations_t *)ctx->ops_userdata;
68 
69     if (!CloseHandle((HANDLE)ctx->fout)) {
70         zip_error_set(&ctx->error, ZIP_ER_WRITE, _zip_win32_error_to_errno(GetLastError()));
71         return -1;
72     }
73 
74     DWORD attributes = file_ops->get_file_attributes(ctx->tmpname);
75     if (attributes == INVALID_FILE_ATTRIBUTES) {
76         zip_error_set(&ctx->error, ZIP_ER_RENAME, _zip_win32_error_to_errno(GetLastError()));
77         return -1;
78     }
79 
80     if (attributes & FILE_ATTRIBUTE_TEMPORARY) {
81         if (!file_ops->set_file_attributes(ctx->tmpname, attributes & ~FILE_ATTRIBUTE_TEMPORARY)) {
82             zip_error_set(&ctx->error, ZIP_ER_RENAME, _zip_win32_error_to_errno(GetLastError()));
83             return -1;
84         }
85     }
86 
87     if (!file_ops->move_file(ctx->tmpname, ctx->fname, MOVEFILE_REPLACE_EXISTING)) {
88         zip_error_set(&ctx->error, ZIP_ER_RENAME, _zip_win32_error_to_errno(GetLastError()));
89         return -1;
90     }
91 
92     return 0;
93 }
94 
95 static zip_int64_t
_zip_win32_named_op_create_temp_output(zip_source_file_context_t * ctx)96 _zip_win32_named_op_create_temp_output(zip_source_file_context_t *ctx) {
97     zip_win32_file_operations_t *file_ops = (zip_win32_file_operations_t *)ctx->ops_userdata;
98 
99     zip_uint32_t value, i;
100     HANDLE th = INVALID_HANDLE_VALUE;
101     void *temp = NULL;
102     PSECURITY_DESCRIPTOR psd = NULL;
103     PSECURITY_ATTRIBUTES psa = NULL;
104     SECURITY_ATTRIBUTES sa;
105     SECURITY_INFORMATION si;
106     DWORD success;
107     PACL dacl = NULL;
108     char *tempname = NULL;
109     size_t tempname_size = 0;
110 
111      if ((HANDLE)ctx->f != INVALID_HANDLE_VALUE && GetFileType((HANDLE)ctx->f) == FILE_TYPE_DISK) {
112          si = DACL_SECURITY_INFORMATION | UNPROTECTED_DACL_SECURITY_INFORMATION;
113          success = GetSecurityInfo((HANDLE)ctx->f, SE_FILE_OBJECT, si, NULL, NULL, &dacl, NULL, &psd);
114          if (success == ERROR_SUCCESS) {
115              sa.nLength = sizeof(SECURITY_ATTRIBUTES);
116              sa.bInheritHandle = FALSE;
117              sa.lpSecurityDescriptor = psd;
118              psa = &sa;
119          }
120      }
121 
122  #ifndef MS_UWP
123     value = GetTickCount();
124 #else
125     value = (zip_uint32_t)(GetTickCount64() & 0xffffffff);
126 #endif
127 
128     if ((tempname = file_ops->allocate_tempname(ctx->fname, 10, &tempname_size)) == NULL) {
129         zip_error_set(&ctx->error, ZIP_ER_MEMORY, 0);
130         return -1;
131     }
132 
133     for (i = 0; i < 1024 && th == INVALID_HANDLE_VALUE; i++) {
134         file_ops->make_tempname(tempname, tempname_size, ctx->fname, value + i);
135 
136         th = win32_named_open(ctx, tempname, true, psa);
137         if (th == INVALID_HANDLE_VALUE && GetLastError() != ERROR_FILE_EXISTS)
138             break;
139     }
140 
141     if (th == INVALID_HANDLE_VALUE) {
142         free(tempname);
143         LocalFree(psd);
144         zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, _zip_win32_error_to_errno(GetLastError()));
145         return -1;
146     }
147 
148     LocalFree(psd);
149     ctx->fout = th;
150     ctx->tmpname = tempname;
151 
152     return 0;
153 }
154 
155 
156 static bool
_zip_win32_named_op_open(zip_source_file_context_t * ctx)157 _zip_win32_named_op_open(zip_source_file_context_t *ctx) {
158     HANDLE h = win32_named_open(ctx, ctx->fname, false, NULL);
159 
160     if (h == INVALID_HANDLE_VALUE) {
161         return false;
162     }
163 
164     ctx->f = h;
165     return true;
166 }
167 
168 
169 static zip_int64_t
_zip_win32_named_op_remove(zip_source_file_context_t * ctx)170 _zip_win32_named_op_remove(zip_source_file_context_t *ctx) {
171     zip_win32_file_operations_t *file_ops = (zip_win32_file_operations_t *)ctx->ops_userdata;
172 
173     if (!file_ops->delete_file(ctx->fname)) {
174         zip_error_set(&ctx->error, ZIP_ER_REMOVE, _zip_win32_error_to_errno(GetLastError()));
175         return -1;
176     }
177 
178     return 0;
179 }
180 
181 
182 static void
_zip_win32_named_op_rollback_write(zip_source_file_context_t * ctx)183 _zip_win32_named_op_rollback_write(zip_source_file_context_t *ctx) {
184     zip_win32_file_operations_t *file_ops = (zip_win32_file_operations_t *)ctx->ops_userdata;
185 
186     if (ctx->fout) {
187         CloseHandle((HANDLE)ctx->fout);
188     }
189     file_ops->delete_file(ctx->tmpname);
190 }
191 
192 
193 static bool
_zip_win32_named_op_stat(zip_source_file_context_t * ctx,zip_source_file_stat_t * st)194 _zip_win32_named_op_stat(zip_source_file_context_t *ctx, zip_source_file_stat_t *st) {
195     zip_win32_file_operations_t *file_ops = (zip_win32_file_operations_t *)ctx->ops_userdata;
196 
197     WIN32_FILE_ATTRIBUTE_DATA file_attributes;
198 
199     if (!file_ops->get_file_attributes_ex(ctx->fname, GetFileExInfoStandard, &file_attributes)) {
200         DWORD error = GetLastError();
201         if (error == ERROR_FILE_NOT_FOUND) {
202             st->exists = false;
203             return true;
204         }
205         zip_error_set(&ctx->error, ZIP_ER_READ, _zip_win32_error_to_errno(error));
206         return false;
207     }
208 
209     st->exists = true;
210     st->regular_file = true; /* TODO: Is this always right? How to determine without a HANDLE? */
211     if (!_zip_filetime_to_time_t(file_attributes.ftLastWriteTime, &st->mtime)) {
212         zip_error_set(&ctx->error, ZIP_ER_READ, ERANGE);
213         return false;
214     }
215     st->size = ((zip_uint64_t)file_attributes.nFileSizeHigh << 32) | file_attributes.nFileSizeLow;
216 
217     /* TODO: fill in ctx->attributes */
218 
219     return true;
220 }
221 
222 
223 static char *
_zip_win32_named_op_string_duplicate(zip_source_file_context_t * ctx,const char * string)224 _zip_win32_named_op_string_duplicate(zip_source_file_context_t *ctx, const char *string) {
225     zip_win32_file_operations_t *file_ops = (zip_win32_file_operations_t *)ctx->ops_userdata;
226 
227     return file_ops->string_duplicate(string);
228 }
229 
230 
231 static zip_int64_t
_zip_win32_named_op_write(zip_source_file_context_t * ctx,const void * data,zip_uint64_t len)232 _zip_win32_named_op_write(zip_source_file_context_t *ctx, const void *data, zip_uint64_t len) {
233     DWORD ret;
234     if (!WriteFile((HANDLE)ctx->fout, data, (DWORD)len, &ret, NULL) || ret != len) {
235         zip_error_set(&ctx->error, ZIP_ER_WRITE, _zip_win32_error_to_errno(GetLastError()));
236         return -1;
237     }
238 
239     return (zip_int64_t)ret;
240 }
241 
242 
243 static HANDLE
win32_named_open(zip_source_file_context_t * ctx,const char * name,bool temporary,PSECURITY_ATTRIBUTES security_attributes)244 win32_named_open(zip_source_file_context_t *ctx, const char *name, bool temporary, PSECURITY_ATTRIBUTES security_attributes) {
245     zip_win32_file_operations_t *file_ops = (zip_win32_file_operations_t *)ctx->ops_userdata;
246 
247     DWORD access = GENERIC_READ;
248     DWORD share_mode = FILE_SHARE_READ | FILE_SHARE_WRITE;
249     DWORD creation_disposition = OPEN_EXISTING;
250     DWORD file_attributes = FILE_ATTRIBUTE_NORMAL;
251 
252     if (temporary) {
253         access = GENERIC_READ | GENERIC_WRITE;
254         share_mode = FILE_SHARE_READ;
255         creation_disposition = CREATE_NEW;
256         file_attributes = FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_TEMPORARY;
257     }
258 
259     HANDLE h = file_ops->create_file(name, access, share_mode, security_attributes, creation_disposition, file_attributes, NULL);
260 
261     if (h == INVALID_HANDLE_VALUE) {
262         zip_error_set(&ctx->error, ZIP_ER_OPEN, _zip_win32_error_to_errno(GetLastError()));
263     }
264 
265     return h;
266 }
267