Imported Upstream version 1.9.1
[platform/upstream/libzip.git] / lib / zip_source_file_win32_named.c
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 <info@libzip.org>
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
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     DWORD attributes;
69
70     if (!CloseHandle((HANDLE)ctx->fout)) {
71         zip_error_set(&ctx->error, ZIP_ER_WRITE, _zip_win32_error_to_errno(GetLastError()));
72         return -1;
73     }
74
75     attributes = file_ops->get_file_attributes(ctx->tmpname);
76     if (attributes == INVALID_FILE_ATTRIBUTES) {
77         zip_error_set(&ctx->error, ZIP_ER_RENAME, _zip_win32_error_to_errno(GetLastError()));
78         return -1;
79     }
80
81     if (attributes & FILE_ATTRIBUTE_TEMPORARY) {
82         if (!file_ops->set_file_attributes(ctx->tmpname, attributes & ~FILE_ATTRIBUTE_TEMPORARY)) {
83             zip_error_set(&ctx->error, ZIP_ER_RENAME, _zip_win32_error_to_errno(GetLastError()));
84             return -1;
85         }
86     }
87
88     if (!file_ops->move_file(ctx->tmpname, ctx->fname, MOVEFILE_REPLACE_EXISTING)) {
89         zip_error_set(&ctx->error, ZIP_ER_RENAME, _zip_win32_error_to_errno(GetLastError()));
90         return -1;
91     }
92
93     return 0;
94 }
95
96 static zip_int64_t
97 _zip_win32_named_op_create_temp_output(zip_source_file_context_t *ctx) {
98     zip_win32_file_operations_t *file_ops = (zip_win32_file_operations_t *)ctx->ops_userdata;
99
100     zip_uint32_t value, i;
101     HANDLE th = INVALID_HANDLE_VALUE;
102     void *temp = NULL;
103     PSECURITY_DESCRIPTOR psd = NULL;
104     PSECURITY_ATTRIBUTES psa = NULL;
105     SECURITY_ATTRIBUTES sa;
106     SECURITY_INFORMATION si;
107     DWORD success;
108     PACL dacl = NULL;
109     char *tempname = NULL;
110     size_t tempname_size = 0;
111
112     if ((HANDLE)ctx->f != INVALID_HANDLE_VALUE && GetFileType((HANDLE)ctx->f) == FILE_TYPE_DISK) {
113         si = DACL_SECURITY_INFORMATION | UNPROTECTED_DACL_SECURITY_INFORMATION;
114         success = GetSecurityInfo((HANDLE)ctx->f, SE_FILE_OBJECT, si, NULL, NULL, &dacl, NULL, &psd);
115         if (success == ERROR_SUCCESS) {
116             sa.nLength = sizeof(SECURITY_ATTRIBUTES);
117             sa.bInheritHandle = FALSE;
118             sa.lpSecurityDescriptor = psd;
119             psa = &sa;
120         }
121     }
122
123 #ifndef MS_UWP
124     value = GetTickCount();
125 #else
126     value = (zip_uint32_t)(GetTickCount64() & 0xffffffff);
127 #endif
128
129     if ((tempname = file_ops->allocate_tempname(ctx->fname, 10, &tempname_size)) == NULL) {
130         zip_error_set(&ctx->error, ZIP_ER_MEMORY, 0);
131         return -1;
132     }
133
134     for (i = 0; i < 1024 && th == INVALID_HANDLE_VALUE; i++) {
135         file_ops->make_tempname(tempname, tempname_size, ctx->fname, value + i);
136
137         th = win32_named_open(ctx, tempname, true, psa);
138         if (th == INVALID_HANDLE_VALUE && GetLastError() != ERROR_FILE_EXISTS)
139             break;
140     }
141
142     if (th == INVALID_HANDLE_VALUE) {
143         free(tempname);
144         LocalFree(psd);
145         zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, _zip_win32_error_to_errno(GetLastError()));
146         return -1;
147     }
148
149     LocalFree(psd);
150     ctx->fout = th;
151     ctx->tmpname = tempname;
152
153     return 0;
154 }
155
156
157 static bool
158 _zip_win32_named_op_open(zip_source_file_context_t *ctx) {
159     HANDLE h = win32_named_open(ctx, ctx->fname, false, NULL);
160
161     if (h == INVALID_HANDLE_VALUE) {
162         return false;
163     }
164
165     ctx->f = h;
166     return true;
167 }
168
169
170 static zip_int64_t
171 _zip_win32_named_op_remove(zip_source_file_context_t *ctx) {
172     zip_win32_file_operations_t *file_ops = (zip_win32_file_operations_t *)ctx->ops_userdata;
173
174     if (!file_ops->delete_file(ctx->fname)) {
175         zip_error_set(&ctx->error, ZIP_ER_REMOVE, _zip_win32_error_to_errno(GetLastError()));
176         return -1;
177     }
178
179     return 0;
180 }
181
182
183 static void
184 _zip_win32_named_op_rollback_write(zip_source_file_context_t *ctx) {
185     zip_win32_file_operations_t *file_ops = (zip_win32_file_operations_t *)ctx->ops_userdata;
186
187     if (ctx->fout) {
188         CloseHandle((HANDLE)ctx->fout);
189     }
190     file_ops->delete_file(ctx->tmpname);
191 }
192
193
194 static bool
195 _zip_win32_named_op_stat(zip_source_file_context_t *ctx, zip_source_file_stat_t *st) {
196     zip_win32_file_operations_t *file_ops = (zip_win32_file_operations_t *)ctx->ops_userdata;
197
198     WIN32_FILE_ATTRIBUTE_DATA file_attributes;
199
200     if (!file_ops->get_file_attributes_ex(ctx->fname, GetFileExInfoStandard, &file_attributes)) {
201         DWORD error = GetLastError();
202         if (error == ERROR_FILE_NOT_FOUND) {
203             st->exists = false;
204             return true;
205         }
206         zip_error_set(&ctx->error, ZIP_ER_READ, _zip_win32_error_to_errno(error));
207         return false;
208     }
209
210     st->exists = true;
211     st->regular_file = false;
212
213     if (file_attributes.dwFileAttributes != INVALID_FILE_ATTRIBUTES) {
214         if ((file_attributes.dwFileAttributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE | FILE_ATTRIBUTE_REPARSE_POINT)) == 0) {
215             st->regular_file = true;
216         }
217     }
218
219     if (!_zip_filetime_to_time_t(file_attributes.ftLastWriteTime, &st->mtime)) {
220         zip_error_set(&ctx->error, ZIP_ER_READ, ERANGE);
221         return false;
222     }
223     st->size = ((zip_uint64_t)file_attributes.nFileSizeHigh << 32) | file_attributes.nFileSizeLow;
224
225     /* TODO: fill in ctx->attributes */
226
227     return true;
228 }
229
230
231 static char *
232 _zip_win32_named_op_string_duplicate(zip_source_file_context_t *ctx, const char *string) {
233     zip_win32_file_operations_t *file_ops = (zip_win32_file_operations_t *)ctx->ops_userdata;
234
235     return file_ops->string_duplicate(string);
236 }
237
238
239 static zip_int64_t
240 _zip_win32_named_op_write(zip_source_file_context_t *ctx, const void *data, zip_uint64_t len) {
241     DWORD ret;
242     if (!WriteFile((HANDLE)ctx->fout, data, (DWORD)len, &ret, NULL) || ret != len) {
243         zip_error_set(&ctx->error, ZIP_ER_WRITE, _zip_win32_error_to_errno(GetLastError()));
244         return -1;
245     }
246
247     return (zip_int64_t)ret;
248 }
249
250
251 static HANDLE
252 win32_named_open(zip_source_file_context_t *ctx, const char *name, bool temporary, PSECURITY_ATTRIBUTES security_attributes) {
253     zip_win32_file_operations_t *file_ops = (zip_win32_file_operations_t *)ctx->ops_userdata;
254
255     DWORD access = GENERIC_READ;
256     DWORD share_mode = FILE_SHARE_READ | FILE_SHARE_WRITE;
257     DWORD creation_disposition = OPEN_EXISTING;
258     DWORD file_attributes = FILE_ATTRIBUTE_NORMAL;
259     HANDLE h;
260
261     if (temporary) {
262         access = GENERIC_READ | GENERIC_WRITE;
263         share_mode = FILE_SHARE_READ;
264         creation_disposition = CREATE_NEW;
265         file_attributes = FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_TEMPORARY;
266     }
267
268     h = file_ops->create_file(name, access, share_mode, security_attributes, creation_disposition, file_attributes, NULL);
269
270     if (h == INVALID_HANDLE_VALUE) {
271         zip_error_set(&ctx->error, ZIP_ER_OPEN, _zip_win32_error_to_errno(GetLastError()));
272     }
273
274     return h;
275 }