VeraCrypt
aboutsummaryrefslogtreecommitdiff
path: root/src/Common/libzip/zip_dirent.c
blob: 45a2a6a29eb915aa56b00eeee6e37e4b01b50e39 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>VeraCrypt - Free Open source disk encryption with strong security for the Paranoid</title>
<meta name="description" content="VeraCrypt is free open-source disk encryption software for Windows, Mac OS X and Linux. In case an attacker forces you to reveal the password, VeraCrypt provides plausible deniability. In contrast to file encryption, data encryption performed by VeraCrypt is real-time (on-the-fly), automatic, transparent, needs very little memory, and does not involve temporary unencrypted files."/>
<meta name="keywords" content="encryption, security"/>
<link href="styles.css" rel="stylesheet" type="text/css" />
</head>
<body>

<div>
<a href="Documentation.html"><img src="VeraCrypt128x128.png" alt="VeraCrypt"/></a>
</div>

<div id="menu">
	<ul>
	  <li><a href="Home.html">Home</a></li>
	  <li><a href="/code/">Source Code</a></li>
	  <li><a href="Downloads.html">Downloads</a></li>
	  <li><a class="active" href="Documentation.html">Documentation</a></li>
	  <li><a href="Donation.html">Donate</a></li>
	  <li><a href="https://sourceforge.net/p/veracrypt/discussion/" target="_blank">Forums</a></li>
	</ul>
</div>

<div>
<p>
<a href="Documentation.html">Documentation</a>
<img src="arrow_right.gif" alt=">>" style="margin-top: 5px">
<a href="Technical%20Details.html">Technical Details</a>
<img src="arrow_right.gif" alt=">>" style="margin-top: 5px">
<a href="Personal%20Iterations%20Multiplier%20(PIM).html">PIM</a>
</p></div>

<div class="wikidoc">
<h1>PIM</h1>
<div>
<p>PIM stands for &quot;Personal Iterations Multiplier&quot;. It is a parameter that was introduced in VeraCrypt 1.12 and whose value controls the number of iterations used by the header key derivation function. This value can be specified through the password dialog
 or in the command line.</p>
<p>If no PIM value is specified, VeraCrypt will use the default number of iterations used in versions prior to 1.12 (see
<a href="Header%20Key%20Derivation.html">
Header Key Derivation</a>).</p>
<p>When a PIM value is specified, the number of iterations is calculated as follows:</p>
<ul>
<li>For system encryption that doesn't use SHA-512 or Whirlpool: Iterations = <strong>PIM x 2048</strong> </li><li>For system encryption that uses SHA-512 or Whirlpool, non-system encryption and file containers: Iterations = <strong>15000 &#43; (PIM x 1000)</strong>
</li></ul>
<p>Prior to version 1.12, the security of a VeraCrypt volume was only based on the password strength because VeraCrypt was using a fixed number of iterations.<br>
With the introduction of PIM, VeraCrypt has a 2-dimensional security space for volumes based on the couple (Password, PIM). This provides more flexibility for adjusting the desired security level while also controlling the performance of the mount/boot operation.</p>
<h3>PIM Usage</h3>
It is not mandatory to specify a PIM.</div>
<div><br>
When creating a volume or when changing the password, the user has the possibility to specify a PIM value by checking the &quot;Use PIM&quot; checkbox which in turn will make a PIM field available in the GUI so a PIM value can be entered.</div>
<div>&nbsp;</div>
<div>The PIM is treated like a secret value that must be entered by the user each time alongside the password. If the incorrect PIM value is specified, the mount/boot operation will fail.</div>
<div>&nbsp;</div>
<div>Using high PIM values leads to better security thanks to the increased number of iterations but it comes with slower mounting/booting times.</div>
<div>With small PIM values, mounting/booting is quicker but this could decrease security if a weak password is used.</div>
<div>&nbsp;</div>
<div>During the creation of a volume or the encryption of the system, VeraCrypt forces the PIM value to be greater than or equal to a certain minimal value when the password is less than 20 characters. This check is done in order to ensure that, for short passwords,
 the security level is at least equal to the default level provided by an empty PIM.</div>
<div>&nbsp;</div>
<div>The PIM minimal value for short passwords is <strong>98</strong> for system encryption that doesn't use SHA-512 or Whirlpool and
<strong>485</strong> for the other cases. For password with 20 characters and more, the PIM minimal value is
<strong>1</strong>. In all cases, leaving the PIM empty or setting its value to 0 will make VeraCrypt use the default high number of iterations as explained in section
<a href="Header%20Key%20Derivation.html">
Header Key Derivation</a>.</div>
<div><br>
Motivations behind using a custom PIM value can be:<br>
<ul>
<li>Add an extra secret parameter (PIM) that an attacker will have to guess </li><li>Increase security level by using large PIM values to thwart future development of brute force attacks.
</li><li>Speeding up booting or mounting through the use of a small PIM value (less than 98 for system encryption that doesn't use SHA-512 or Whirlpool and less than 485 for the other cases)
</li></ul>
<p>The screenshots below show the step to mount a volume using a PIM equal to 231:</p>
<table style="margin-left:auto; margin-right:auto">
<tbody>
<tr>
<td><img src="Personal Iterations Multiplier (PIM)_VeraCrypt_UsePIM_Step1.png" alt=""></td>
</tr>
<tr>
<td><img src="Personal Iterations Multiplier (PIM)_VeraCrypt_UsePIM_Step2.png" alt=""></td>
</tr>
</tbody>
</table>
<p>&nbsp;</p>
<h3>Changing/clearing the PIM</h3>
<p>The PIM of a volume or for system encryption can be changed or cleared using the change password functionality. The screenshots below shows an example of changing the PIM from the empty default value to a value equal to 3 (this is possible since the password
 has more than 20 characters). In order to do so, the user must first tick &quot;Use PIM&quot; checkbox in the &quot;New&quot; section to reveal the PIM field.</p>
<table width="519" style="height:896px; width:519px; margin-left:auto; margin-right:auto">
<caption><strong>Normal volume case</strong></caption>
<tbody>
<tr>
<td style="text-align:center"><img src="Personal Iterations Multiplier (PIM)_VeraCrypt_ChangePIM_Step1.png" alt=""></td>
</tr>
<tr>
<td>
<p><img src="Personal Iterations Multiplier (PIM)_VeraCrypt_ChangePIM_Step2.png" alt=""></p>
</td>
</tr>
</tbody>
</table>
<h5>&nbsp;</h5>
<table style="margin-left:auto; margin-right:auto">
<caption><strong>System encryption case</strong></caption>
<tbody>
<tr>
<td><img src="Personal Iterations Multiplier (PIM)_VeraCrypt_ChangePIM_System_Step1.png" alt=""></td>
</tr>
<tr>
<td><img src="Personal Iterations Multiplier (PIM)_VeraCrypt_ChangePIM_System_Step2.png" alt=""></td>
</tr>
</tbody>
</table>
<p>&nbsp;</p>
<p><a href="VeraCrypt%20Volume%20Format%20Specification.html" style="text-align:left; color:#0080c0; text-decoration:none; font-weight:bold.html">Next Section &gt;&gt;</a></p>
</div>
</div><div class="ClearBoth"></div></body></html>
n1024'>1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207
/*
  zip_dirent.c -- read directory entry (local or central), clean dirent
  Copyright (C) 1999-2021 Dieter Baron and Thomas Klausner

  This file is part of libzip, a library to manipulate ZIP archives.
  The authors can be contacted at <info@libzip.org>

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions
  are met:
  1. Redistributions of source code must retain the above copyright
     notice, this list of conditions and the following disclaimer.
  2. Redistributions in binary form must reproduce the above copyright
     notice, this list of conditions and the following disclaimer in
     the documentation and/or other materials provided with the
     distribution.
  3. The names of the authors may not be used to endorse or promote
     products derived from this software without specific prior
     written permission.

  THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
  IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <time.h>
#include <zlib.h>

#include "zipint.h"

static zip_string_t *_zip_dirent_process_ef_utf_8(const zip_dirent_t *de, zip_uint16_t id, zip_string_t *str);
static zip_extra_field_t *_zip_ef_utf8(zip_uint16_t, zip_string_t *, zip_error_t *);
static bool _zip_dirent_process_winzip_aes(zip_dirent_t *de, zip_error_t *error);


void
_zip_cdir_free(zip_cdir_t *cd) {
    zip_uint64_t i;

    if (!cd)
        return;

    for (i = 0; i < cd->nentry; i++)
        _zip_entry_finalize(cd->entry + i);
    free(cd->entry);
    _zip_string_free(cd->comment);
    free(cd);
}


zip_cdir_t *
_zip_cdir_new(zip_uint64_t nentry, zip_error_t *error) {
    zip_cdir_t *cd;

    if ((cd = (zip_cdir_t *)malloc(sizeof(*cd))) == NULL) {
        zip_error_set(error, ZIP_ER_MEMORY, 0);
        return NULL;
    }

    cd->entry = NULL;
    cd->nentry = cd->nentry_alloc = 0;
    cd->size = cd->offset = 0;
    cd->comment = NULL;
    cd->is_zip64 = false;

    if (!_zip_cdir_grow(cd, nentry, error)) {
        _zip_cdir_free(cd);
        return NULL;
    }

    return cd;
}


bool
_zip_cdir_grow(zip_cdir_t *cd, zip_uint64_t additional_entries, zip_error_t *error) {
    zip_uint64_t i, new_alloc;
    zip_entry_t *new_entry;

    if (additional_entries == 0) {
        return true;
    }

    new_alloc = cd->nentry_alloc + additional_entries;

    if (new_alloc < additional_entries || new_alloc > SIZE_MAX / sizeof(*(cd->entry))) {
        zip_error_set(error, ZIP_ER_MEMORY, 0);
        return false;
    }

    if ((new_entry = (zip_entry_t *)realloc(cd->entry, sizeof(*(cd->entry)) * (size_t)new_alloc)) == NULL) {
        zip_error_set(error, ZIP_ER_MEMORY, 0);
        return false;
    }

    cd->entry = new_entry;

    for (i = cd->nentry; i < new_alloc; i++) {
        _zip_entry_init(cd->entry + i);
    }

    cd->nentry = cd->nentry_alloc = new_alloc;

    return true;
}


zip_int64_t
_zip_cdir_write(zip_t *za, const zip_filelist_t *filelist, zip_uint64_t survivors) {
    zip_uint64_t offset, size;
    zip_string_t *comment;
    zip_uint8_t buf[EOCDLEN + EOCD64LEN + EOCD64LOCLEN];
    zip_buffer_t *buffer;
    zip_int64_t off;
    zip_uint64_t i;
    bool is_zip64;
    int ret;
    zip_uint32_t cdir_crc;

    if ((off = zip_source_tell_write(za->src)) < 0) {
        zip_error_set_from_source(&za->error, za->src);
        return -1;
    }
    offset = (zip_uint64_t)off;

    is_zip64 = false;

    if (ZIP_WANT_TORRENTZIP(za)) {
        cdir_crc = (zip_uint32_t)crc32(0, NULL, 0);
        za->write_crc = &cdir_crc;
    }

    for (i = 0; i < survivors; i++) {
        zip_entry_t *entry = za->entry + filelist[i].idx;

        if ((ret = _zip_dirent_write(za, entry->changes ? entry->changes : entry->orig, ZIP_FL_CENTRAL)) < 0)
            return -1;
        if (ret)
            is_zip64 = true;
    }

    za->write_crc = NULL;

    if ((off = zip_source_tell_write(za->src)) < 0) {
        zip_error_set_from_source(&za->error, za->src);
        return -1;
    }
    size = (zip_uint64_t)off - offset;

    if (offset > ZIP_UINT32_MAX || survivors > ZIP_UINT16_MAX) {
        is_zip64 = true;
    }

    if ((buffer = _zip_buffer_new(buf, sizeof(buf))) == NULL) {
        zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
        return -1;
    }

    if (is_zip64) {
        _zip_buffer_put(buffer, EOCD64_MAGIC, 4);
        _zip_buffer_put_64(buffer, EOCD64LEN - 12);
        _zip_buffer_put_16(buffer, 45);
        _zip_buffer_put_16(buffer, 45);
        _zip_buffer_put_32(buffer, 0);
        _zip_buffer_put_32(buffer, 0);
        _zip_buffer_put_64(buffer, survivors);
        _zip_buffer_put_64(buffer, survivors);
        _zip_buffer_put_64(buffer, size);
        _zip_buffer_put_64(buffer, offset);
        _zip_buffer_put(buffer, EOCD64LOC_MAGIC, 4);
        _zip_buffer_put_32(buffer, 0);
        _zip_buffer_put_64(buffer, offset + size);
        _zip_buffer_put_32(buffer, 1);
    }

    _zip_buffer_put(buffer, EOCD_MAGIC, 4);
    _zip_buffer_put_32(buffer, 0);
    _zip_buffer_put_16(buffer, (zip_uint16_t)(survivors >= ZIP_UINT16_MAX ? ZIP_UINT16_MAX : survivors));
    _zip_buffer_put_16(buffer, (zip_uint16_t)(survivors >= ZIP_UINT16_MAX ? ZIP_UINT16_MAX : survivors));
    _zip_buffer_put_32(buffer, size >= ZIP_UINT32_MAX ? ZIP_UINT32_MAX : (zip_uint32_t)size);
    _zip_buffer_put_32(buffer, offset >= ZIP_UINT32_MAX ? ZIP_UINT32_MAX : (zip_uint32_t)offset);

    comment = za->comment_changed ? za->comment_changes : za->comment_orig;

    if (ZIP_WANT_TORRENTZIP(za)) {
        _zip_buffer_put_16(buffer, TORRENTZIP_SIGNATURE_LENGTH + TORRENTZIP_CRC_LENGTH);
    }
    else {
        _zip_buffer_put_16(buffer, (zip_uint16_t)(comment ? comment->length : 0));
    }


    if (!_zip_buffer_ok(buffer)) {
        zip_error_set(&za->error, ZIP_ER_INTERNAL, 0);
        _zip_buffer_free(buffer);
        return -1;
    }

    if (_zip_write(za, _zip_buffer_data(buffer), _zip_buffer_offset(buffer)) < 0) {
        _zip_buffer_free(buffer);
        return -1;
    }

    _zip_buffer_free(buffer);

    if (ZIP_WANT_TORRENTZIP(za)) {
        char torrentzip_comment[TORRENTZIP_SIGNATURE_LENGTH + TORRENTZIP_CRC_LENGTH + 1];
        snprintf(torrentzip_comment, sizeof(torrentzip_comment), TORRENTZIP_SIGNATURE "%08X", cdir_crc);

        if (_zip_write(za, torrentzip_comment, strlen(torrentzip_comment)) < 0) {
            return -1;
        }
    }
    else if (comment != NULL) {
        if (_zip_write(za, comment->raw, comment->length) < 0) {
            return -1;
        }
    }

    return (zip_int64_t)size;
}


zip_dirent_t *
_zip_dirent_clone(const zip_dirent_t *sde) {
    zip_dirent_t *tde;

    if ((tde = (zip_dirent_t *)malloc(sizeof(*tde))) == NULL)
        return NULL;

    if (sde)
        (void)memcpy_s(tde, sizeof(*tde), sde, sizeof(*sde));
    else
        _zip_dirent_init(tde);

    tde->changed = 0;
    tde->cloned = 1;

    return tde;
}


void
_zip_dirent_finalize(zip_dirent_t *zde) {
    if (!zde->cloned || zde->changed & ZIP_DIRENT_FILENAME) {
        _zip_string_free(zde->filename);
        zde->filename = NULL;
    }
    if (!zde->cloned || zde->changed & ZIP_DIRENT_EXTRA_FIELD) {
        _zip_ef_free(zde->extra_fields);
        zde->extra_fields = NULL;
    }
    if (!zde->cloned || zde->changed & ZIP_DIRENT_COMMENT) {
        _zip_string_free(zde->comment);
        zde->comment = NULL;
    }
    if (!zde->cloned || zde->changed & ZIP_DIRENT_PASSWORD) {
        if (zde->password) {
            _zip_crypto_clear(zde->password, strlen(zde->password));
        }
        free(zde->password);
        zde->password = NULL;
    }
}


void
_zip_dirent_free(zip_dirent_t *zde) {
    if (zde == NULL)
        return;

    _zip_dirent_finalize(zde);
    free(zde);
}


void
_zip_dirent_init(zip_dirent_t *de) {
    de->changed = 0;
    de->local_extra_fields_read = 0;
    de->cloned = 0;

    de->crc_valid = true;
    de->version_madeby = 63 | (ZIP_OPSYS_DEFAULT << 8);
    de->version_needed = 10; /* 1.0 */
    de->bitflags = 0;
    de->comp_method = ZIP_CM_DEFAULT;
    de->last_mod = 0;
    de->crc = 0;
    de->comp_size = 0;
    de->uncomp_size = 0;
    de->filename = NULL;
    de->extra_fields = NULL;
    de->comment = NULL;
    de->disk_number = 0;
    de->int_attrib = 0;
    de->ext_attrib = ZIP_EXT_ATTRIB_DEFAULT;
    de->offset = 0;
    de->compression_level = 0;
    de->encryption_method = ZIP_EM_NONE;
    de->password = NULL;
}


bool
_zip_dirent_needs_zip64(const zip_dirent_t *de, zip_flags_t flags) {
    if (de->uncomp_size >= ZIP_UINT32_MAX || de->comp_size >= ZIP_UINT32_MAX || ((flags & ZIP_FL_CENTRAL) && de->offset >= ZIP_UINT32_MAX))
        return true;

    return false;
}


zip_dirent_t *
_zip_dirent_new(void) {
    zip_dirent_t *de;

    if ((de = (zip_dirent_t *)malloc(sizeof(*de))) == NULL)
        return NULL;

    _zip_dirent_init(de);
    return de;
}


/* _zip_dirent_read(zde, fp, bufp, left, localp, error):
   Fills the zip directory entry zde.

   If buffer is non-NULL, data is taken from there; otherwise data is read from fp as needed.

   If local is true, it reads a local header instead of a central directory entry.

   Returns size of dirent read if successful. On error, error is filled in and -1 is returned.
*/

zip_int64_t
_zip_dirent_read(zip_dirent_t *zde, zip_source_t *src, zip_buffer_t *buffer, bool local, zip_error_t *error) {
    zip_uint8_t buf[CDENTRYSIZE];
    zip_uint16_t dostime, dosdate;
    zip_uint32_t size, variable_size;
    zip_uint16_t filename_len, comment_len, ef_len;

    bool from_buffer = (buffer != NULL);

    size = local ? LENTRYSIZE : CDENTRYSIZE;

    if (buffer) {
        if (_zip_buffer_left(buffer) < size) {
            zip_error_set(error, ZIP_ER_NOZIP, 0);
            return -1;
        }
    }
    else {
        if ((buffer = _zip_buffer_new_from_source(src, size, buf, error)) == NULL) {
            return -1;
        }
    }

    if (memcmp(_zip_buffer_get(buffer, 4), (local ? LOCAL_MAGIC : CENTRAL_MAGIC), 4) != 0) {
        zip_error_set(error, ZIP_ER_NOZIP, 0);
        if (!from_buffer) {
            _zip_buffer_free(buffer);
        }
        return -1;
    }

    /* convert buffercontents to zip_dirent */

    _zip_dirent_init(zde);
    if (!local)
        zde->version_madeby = _zip_buffer_get_16(buffer);
    else
        zde->version_madeby = 0;
    zde->version_needed = _zip_buffer_get_16(buffer);
    zde->bitflags = _zip_buffer_get_16(buffer);
    zde->comp_method = _zip_buffer_get_16(buffer);

    /* convert to time_t */
    dostime = _zip_buffer_get_16(buffer);
    dosdate = _zip_buffer_get_16(buffer);
    zde->last_mod = _zip_d2u_time(dostime, dosdate);

    zde->crc = _zip_buffer_get_32(buffer);
    zde->comp_size = _zip_buffer_get_32(buffer);
    zde->uncomp_size = _zip_buffer_get_32(buffer);

    filename_len = _zip_buffer_get_16(buffer);
    ef_len = _zip_buffer_get_16(buffer);

    if (local) {
        comment_len = 0;
        zde->disk_number = 0;
        zde->int_attrib = 0;
        zde->ext_attrib = 0;
        zde->offset = 0;
    }
    else {
        comment_len = _zip_buffer_get_16(buffer);
        zde->disk_number = _zip_buffer_get_16(buffer);
        zde->int_attrib = _zip_buffer_get_16(buffer);
        zde->ext_attrib = _zip_buffer_get_32(buffer);
        zde->offset = _zip_buffer_get_32(buffer);
    }

    if (!_zip_buffer_ok(buffer)) {
        zip_error_set(error, ZIP_ER_INTERNAL, 0);
        if (!from_buffer) {
            _zip_buffer_free(buffer);
        }
        return -1;
    }

    if (zde->bitflags & ZIP_GPBF_ENCRYPTED) {
        if (zde->bitflags & ZIP_GPBF_STRONG_ENCRYPTION) {
            /* TODO */
            zde->encryption_method = ZIP_EM_UNKNOWN;
        }
        else {
            zde->encryption_method = ZIP_EM_TRAD_PKWARE;
        }
    }
    else {
        zde->encryption_method = ZIP_EM_NONE;
    }

    zde->filename = NULL;
    zde->extra_fields = NULL;
    zde->comment = NULL;

    variable_size = (zip_uint32_t)filename_len + (zip_uint32_t)ef_len + (zip_uint32_t)comment_len;

    if (from_buffer) {
        if (_zip_buffer_left(buffer) < variable_size) {
            zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_VARIABLE_SIZE_OVERFLOW);
            return -1;
        }
    }
    else {
        _zip_buffer_free(buffer);

        if ((buffer = _zip_buffer_new_from_source(src, variable_size, NULL, error)) == NULL) {
            return -1;
        }
    }

    if (filename_len) {
        zde->filename = _zip_read_string(buffer, src, filename_len, 1, error);
        if (!zde->filename) {
            if (zip_error_code_zip(error) == ZIP_ER_EOF) {
                zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_VARIABLE_SIZE_OVERFLOW);
            }
            if (!from_buffer) {
                _zip_buffer_free(buffer);
            }
            return -1;
        }

        if (zde->bitflags & ZIP_GPBF_ENCODING_UTF_8) {
            if (_zip_guess_encoding(zde->filename, ZIP_ENCODING_UTF8_KNOWN) == ZIP_ENCODING_ERROR) {
                zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_INVALID_UTF8_IN_FILENAME);
                if (!from_buffer) {
                    _zip_buffer_free(buffer);
                }
                return -1;
            }
        }
    }

    if (ef_len) {
        zip_uint8_t *ef = _zip_read_data(buffer, src, ef_len, 0, error);

        if (ef == NULL) {
            if (!from_buffer) {
                _zip_buffer_free(buffer);
            }
            return -1;
        }
        if (!_zip_ef_parse(ef, ef_len, local ? ZIP_EF_LOCAL : ZIP_EF_CENTRAL, &zde->extra_fields, error)) {
            free(ef);
            if (!from_buffer) {
                _zip_buffer_free(buffer);
            }
            return -1;
        }
        free(ef);
        if (local)
            zde->local_extra_fields_read = 1;
    }

    if (comment_len) {
        zde->comment = _zip_read_string(buffer, src, comment_len, 0, error);
        if (!zde->comment) {
            if (!from_buffer) {
                _zip_buffer_free(buffer);
            }
            return -1;
        }
        if (zde->bitflags & ZIP_GPBF_ENCODING_UTF_8) {
            if (_zip_guess_encoding(zde->comment, ZIP_ENCODING_UTF8_KNOWN) == ZIP_ENCODING_ERROR) {
                zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_INVALID_UTF8_IN_COMMENT);
                if (!from_buffer) {
                    _zip_buffer_free(buffer);
                }
                return -1;
            }
        }
    }

    zde->filename = _zip_dirent_process_ef_utf_8(zde, ZIP_EF_UTF_8_NAME, zde->filename);
    zde->comment = _zip_dirent_process_ef_utf_8(zde, ZIP_EF_UTF_8_COMMENT, zde->comment);

    /* Zip64 */

    if (zde->uncomp_size == ZIP_UINT32_MAX || zde->comp_size == ZIP_UINT32_MAX || zde->offset == ZIP_UINT32_MAX) {
        zip_uint16_t got_len;
        const zip_uint8_t *ef = _zip_ef_get_by_id(zde->extra_fields, &got_len, ZIP_EF_ZIP64, 0, local ? ZIP_EF_LOCAL : ZIP_EF_CENTRAL, error);
        if (ef != NULL) {
            if (!zip_dirent_process_ef_zip64(zde, ef, got_len, local, error)) {
                if (!from_buffer) {
                    _zip_buffer_free(buffer);
                }
                return -1;
            }
        }
    }


    if (!_zip_buffer_ok(buffer)) {
        zip_error_set(error, ZIP_ER_INTERNAL, 0);
        if (!from_buffer) {
            _zip_buffer_free(buffer);
        }
        return -1;
    }
    if (!from_buffer) {
        _zip_buffer_free(buffer);
    }

    /* zip_source_seek / zip_source_tell don't support values > ZIP_INT64_MAX */
    if (zde->offset > ZIP_INT64_MAX) {
        zip_error_set(error, ZIP_ER_SEEK, EFBIG);
        return -1;
    }

    if (!_zip_dirent_process_winzip_aes(zde, error)) {
        return -1;
    }

    zde->extra_fields = _zip_ef_remove_internal(zde->extra_fields);

    return (zip_int64_t)size + (zip_int64_t)variable_size;
}

bool zip_dirent_process_ef_zip64(zip_dirent_t* zde, const zip_uint8_t* ef, zip_uint64_t got_len, bool local, zip_error_t* error) {
    zip_buffer_t *ef_buffer;

    if ((ef_buffer = _zip_buffer_new((zip_uint8_t *)ef, got_len)) == NULL) {
        zip_error_set(error, ZIP_ER_MEMORY, 0);
        return false;
    }

    if (zde->uncomp_size == ZIP_UINT32_MAX) {
        zde->uncomp_size = _zip_buffer_get_64(ef_buffer);
    }
    else if (local) {
        /* From appnote.txt: This entry in the Local header MUST
           include BOTH original and compressed file size fields. */
        (void)_zip_buffer_skip(ef_buffer, 8); /* error is caught by _zip_buffer_eof() call */
    }
    if (zde->comp_size == ZIP_UINT32_MAX) {
        zde->comp_size = _zip_buffer_get_64(ef_buffer);
    }
    if (!local) {
        if (zde->offset == ZIP_UINT32_MAX) {
            zde->offset = _zip_buffer_get_64(ef_buffer);
        }
        if (zde->disk_number == ZIP_UINT16_MAX) {
            zde->disk_number = _zip_buffer_get_32(ef_buffer);
        }
    }

    if (!_zip_buffer_eof(ef_buffer)) {
        /* accept additional fields if values match */
        bool ok = true;
        switch (got_len) {
        case 28:
            _zip_buffer_set_offset(ef_buffer, 24);
            if (zde->disk_number != _zip_buffer_get_32(ef_buffer)) {
                ok = false;
            }
            /* fallthrough */
        case 24:
            _zip_buffer_set_offset(ef_buffer, 0);
            if ((zde->uncomp_size != _zip_buffer_get_64(ef_buffer)) || (zde->comp_size != _zip_buffer_get_64(ef_buffer)) || (zde->offset != _zip_buffer_get_64(ef_buffer))) {
                ok = false;
            }
            break;

        default:
            ok = false;
        }
        if (!ok) {
            zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_INVALID_ZIP64_EF);
            _zip_buffer_free(ef_buffer);
            return false;
        }
    }
    _zip_buffer_free(ef_buffer);

    return true;
}


static zip_string_t *
_zip_dirent_process_ef_utf_8(const zip_dirent_t *de, zip_uint16_t id, zip_string_t *str) {
    zip_uint16_t ef_len;
    zip_uint32_t ef_crc;
    zip_buffer_t *buffer;

    const zip_uint8_t *ef = _zip_ef_get_by_id(de->extra_fields, &ef_len, id, 0, ZIP_EF_BOTH, NULL);

    if (ef == NULL || ef_len < 5 || ef[0] != 1) {
        return str;
    }

    if ((buffer = _zip_buffer_new((zip_uint8_t *)ef, ef_len)) == NULL) {
        return str;
    }

    _zip_buffer_get_8(buffer);
    ef_crc = _zip_buffer_get_32(buffer);

    if (_zip_string_crc32(str) == ef_crc) {
        zip_uint16_t len = (zip_uint16_t)_zip_buffer_left(buffer);
        zip_string_t *ef_str = _zip_string_new(_zip_buffer_get(buffer, len), len, ZIP_FL_ENC_UTF_8, NULL);

        if (ef_str != NULL) {
            _zip_string_free(str);
            str = ef_str;
        }
    }

    _zip_buffer_free(buffer);

    return str;
}


static bool
_zip_dirent_process_winzip_aes(zip_dirent_t *de, zip_error_t *error) {
    zip_uint16_t ef_len;
    zip_buffer_t *buffer;
    const zip_uint8_t *ef;
    bool crc_valid;
    zip_uint16_t enc_method;


    if (de->comp_method != ZIP_CM_WINZIP_AES) {
        return true;
    }

    ef = _zip_ef_get_by_id(de->extra_fields, &ef_len, ZIP_EF_WINZIP_AES, 0, ZIP_EF_BOTH, NULL);

    if (ef == NULL || ef_len < 7) {
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_INVALID_WINZIPAES_EF);
        return false;
    }

    if ((buffer = _zip_buffer_new((zip_uint8_t *)ef, ef_len)) == NULL) {
        zip_error_set(error, ZIP_ER_INTERNAL, 0);
        return false;
    }

    /* version */

    crc_valid = true;
    switch (_zip_buffer_get_16(buffer)) {
        case 1:
            break;

        case 2:
            crc_valid = false;
            /* TODO: When checking consistency, check that crc is 0. */
            break;
            
        default:
            zip_error_set(error, ZIP_ER_ENCRNOTSUPP, 0);
            _zip_buffer_free(buffer);
            return false;
    }

    /* vendor */
    if (memcmp(_zip_buffer_get(buffer, 2), "AE", 2) != 0) {
        zip_error_set(error, ZIP_ER_ENCRNOTSUPP, 0);
        _zip_buffer_free(buffer);
        return false;
    }

    /* mode */
    switch (_zip_buffer_get_8(buffer)) {
    case 1:
        enc_method = ZIP_EM_AES_128;
        break;
    case 2:
        enc_method = ZIP_EM_AES_192;
        break;
    case 3:
        enc_method = ZIP_EM_AES_256;
        break;
    default:
        zip_error_set(error, ZIP_ER_ENCRNOTSUPP, 0);
        _zip_buffer_free(buffer);
        return false;
    }

    if (ef_len != 7) {
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_INVALID_WINZIPAES_EF);
        _zip_buffer_free(buffer);
        return false;
    }

    de->crc_valid = crc_valid;
    de->encryption_method = enc_method;
    de->comp_method = _zip_buffer_get_16(buffer);

    _zip_buffer_free(buffer);
    return true;
}


zip_int32_t
_zip_dirent_size(zip_source_t *src, zip_uint16_t flags, zip_error_t *error) {
    zip_int32_t size;
    bool local = (flags & ZIP_EF_LOCAL) != 0;
    int i;
    zip_uint8_t b[6];
    zip_buffer_t *buffer;

    size = local ? LENTRYSIZE : CDENTRYSIZE;

    if (zip_source_seek(src, local ? 26 : 28, SEEK_CUR) < 0) {
        zip_error_set_from_source(error, src);
        return -1;
    }

    if ((buffer = _zip_buffer_new_from_source(src, local ? 4 : 6, b, error)) == NULL) {
        return -1;
    }

    for (i = 0; i < (local ? 2 : 3); i++) {
        size += _zip_buffer_get_16(buffer);
    }

    if (!_zip_buffer_eof(buffer)) {
        zip_error_set(error, ZIP_ER_INTERNAL, 0);
        _zip_buffer_free(buffer);
        return -1;
    }

    _zip_buffer_free(buffer);
    return size;
}


/* _zip_dirent_write
   Writes zip directory entry.

   If flags & ZIP_EF_LOCAL, it writes a local header instead of a central
   directory entry.  If flags & ZIP_EF_FORCE_ZIP64, a ZIP64 extra field is written, even if not needed.

   Returns 0 if successful, 1 if successful and wrote ZIP64 extra field. On error, error is filled in and -1 is
   returned.
*/

int
_zip_dirent_write(zip_t *za, zip_dirent_t *de, zip_flags_t flags) {
    zip_uint16_t dostime, dosdate;
    zip_encoding_type_t com_enc, name_enc;
    zip_extra_field_t *ef;
    zip_extra_field_t *ef64;
    zip_uint32_t ef_total_size;
    bool is_zip64;
    bool is_really_zip64;
    bool is_winzip_aes;
    zip_uint8_t buf[CDENTRYSIZE];
    zip_buffer_t *buffer;

    ef = NULL;

    name_enc = _zip_guess_encoding(de->filename, ZIP_ENCODING_UNKNOWN);
    com_enc = _zip_guess_encoding(de->comment, ZIP_ENCODING_UNKNOWN);

    if ((name_enc == ZIP_ENCODING_UTF8_KNOWN && com_enc == ZIP_ENCODING_ASCII) || (name_enc == ZIP_ENCODING_ASCII && com_enc == ZIP_ENCODING_UTF8_KNOWN) || (name_enc == ZIP_ENCODING_UTF8_KNOWN && com_enc == ZIP_ENCODING_UTF8_KNOWN))
        de->bitflags |= ZIP_GPBF_ENCODING_UTF_8;
    else {
        de->bitflags &= (zip_uint16_t)~ZIP_GPBF_ENCODING_UTF_8;
        if (name_enc == ZIP_ENCODING_UTF8_KNOWN) {
            ef = _zip_ef_utf8(ZIP_EF_UTF_8_NAME, de->filename, &za->error);
            if (ef == NULL)
                return -1;
        }
        if ((flags & ZIP_FL_LOCAL) == 0 && com_enc == ZIP_ENCODING_UTF8_KNOWN) {
            zip_extra_field_t *ef2 = _zip_ef_utf8(ZIP_EF_UTF_8_COMMENT, de->comment, &za->error);
            if (ef2 == NULL) {
                _zip_ef_free(ef);
                return -1;
            }
            ef2->next = ef;
            ef = ef2;
        }
    }

    if (de->encryption_method == ZIP_EM_NONE) {
        de->bitflags &= (zip_uint16_t)~ZIP_GPBF_ENCRYPTED;
    }
    else {
        de->bitflags |= (zip_uint16_t)ZIP_GPBF_ENCRYPTED;
    }

    is_really_zip64 = _zip_dirent_needs_zip64(de, flags);
    is_zip64 = (flags & (ZIP_FL_LOCAL | ZIP_FL_FORCE_ZIP64)) == (ZIP_FL_LOCAL | ZIP_FL_FORCE_ZIP64) || is_really_zip64;
    is_winzip_aes = de->encryption_method == ZIP_EM_AES_128 || de->encryption_method == ZIP_EM_AES_192 || de->encryption_method == ZIP_EM_AES_256;

    if (is_zip64) {
        zip_uint8_t ef_zip64[EFZIP64SIZE];
        zip_buffer_t *ef_buffer = _zip_buffer_new(ef_zip64, sizeof(ef_zip64));
        if (ef_buffer == NULL) {
            zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
            _zip_ef_free(ef);
            return -1;
        }

        if (flags & ZIP_FL_LOCAL) {
            if ((flags & ZIP_FL_FORCE_ZIP64) || de->comp_size > ZIP_UINT32_MAX || de->uncomp_size > ZIP_UINT32_MAX) {
                _zip_buffer_put_64(ef_buffer, de->uncomp_size);
                _zip_buffer_put_64(ef_buffer, de->comp_size);
            }
        }
        else {
            if ((flags & ZIP_FL_FORCE_ZIP64) || de->comp_size > ZIP_UINT32_MAX || de->uncomp_size > ZIP_UINT32_MAX || de->offset > ZIP_UINT32_MAX) {
                if (de->uncomp_size >= ZIP_UINT32_MAX) {
                    _zip_buffer_put_64(ef_buffer, de->uncomp_size);
                }
                if (de->comp_size >= ZIP_UINT32_MAX) {
                    _zip_buffer_put_64(ef_buffer, de->comp_size);
                }
                if (de->offset >= ZIP_UINT32_MAX) {
                    _zip_buffer_put_64(ef_buffer, de->offset);
                }
            }
        }

        if (!_zip_buffer_ok(ef_buffer)) {
            zip_error_set(&za->error, ZIP_ER_INTERNAL, 0);
            _zip_buffer_free(ef_buffer);
            _zip_ef_free(ef);
            return -1;
        }

        ef64 = _zip_ef_new(ZIP_EF_ZIP64, (zip_uint16_t)(_zip_buffer_offset(ef_buffer)), ef_zip64, ZIP_EF_BOTH);
        _zip_buffer_free(ef_buffer);
        ef64->next = ef;
        ef = ef64;
    }

    if (is_winzip_aes) {
        zip_uint8_t data[EF_WINZIP_AES_SIZE];
        zip_buffer_t *ef_buffer = _zip_buffer_new(data, sizeof(data));
        zip_extra_field_t *ef_winzip;

        if (ef_buffer == NULL) {
            zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
            _zip_ef_free(ef);
            return -1;
        }

        _zip_buffer_put_16(ef_buffer, 2);
        _zip_buffer_put(ef_buffer, "AE", 2);
        _zip_buffer_put_8(ef_buffer, (zip_uint8_t)(de->encryption_method & 0xff));
        _zip_buffer_put_16(ef_buffer, (zip_uint16_t)de->comp_method);

        if (!_zip_buffer_ok(ef_buffer)) {
            zip_error_set(&za->error, ZIP_ER_INTERNAL, 0);
            _zip_buffer_free(ef_buffer);
            _zip_ef_free(ef);
            return -1;
        }

        ef_winzip = _zip_ef_new(ZIP_EF_WINZIP_AES, EF_WINZIP_AES_SIZE, data, ZIP_EF_BOTH);
        _zip_buffer_free(ef_buffer);
        ef_winzip->next = ef;
        ef = ef_winzip;
    }

    if ((buffer = _zip_buffer_new(buf, sizeof(buf))) == NULL) {
        zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
        _zip_ef_free(ef);
        return -1;
    }

    _zip_buffer_put(buffer, (flags & ZIP_FL_LOCAL) ? LOCAL_MAGIC : CENTRAL_MAGIC, 4);

    if ((flags & ZIP_FL_LOCAL) == 0) {
        _zip_buffer_put_16(buffer, de->version_madeby);
    }
    _zip_buffer_put_16(buffer, ZIP_MAX(is_really_zip64 ? 45 : 0, de->version_needed));
    _zip_buffer_put_16(buffer, de->bitflags);
    if (is_winzip_aes) {
        _zip_buffer_put_16(buffer, ZIP_CM_WINZIP_AES);
    }
    else {
        _zip_buffer_put_16(buffer, (zip_uint16_t)de->comp_method);
    }

    if (ZIP_WANT_TORRENTZIP(za)) {
        dostime = 0xbc00;
        dosdate = 0x2198;
    }
    else {
        _zip_u2d_time(de->last_mod, &dostime, &dosdate);
    }
    _zip_buffer_put_16(buffer, dostime);
    _zip_buffer_put_16(buffer, dosdate);

    if (is_winzip_aes && de->uncomp_size < 20) {
        _zip_buffer_put_32(buffer, 0);
    }
    else {
        _zip_buffer_put_32(buffer, de->crc);
    }

    if (((flags & ZIP_FL_LOCAL) == ZIP_FL_LOCAL) && ((de->comp_size >= ZIP_UINT32_MAX) || (de->uncomp_size >= ZIP_UINT32_MAX))) {
        /* In local headers, if a ZIP64 EF is written, it MUST contain
         * both compressed and uncompressed sizes (even if one of the
         * two is smaller than 0xFFFFFFFF); on the other hand, those
         * may only appear when the corresponding standard entry is
         * 0xFFFFFFFF.  (appnote.txt 4.5.3) */
        _zip_buffer_put_32(buffer, ZIP_UINT32_MAX);
        _zip_buffer_put_32(buffer, ZIP_UINT32_MAX);
    }
    else {
        if (de->comp_size < ZIP_UINT32_MAX) {
            _zip_buffer_put_32(buffer, (zip_uint32_t)de->comp_size);
        }
        else {
            _zip_buffer_put_32(buffer, ZIP_UINT32_MAX);
        }
        if (de->uncomp_size < ZIP_UINT32_MAX) {
            _zip_buffer_put_32(buffer, (zip_uint32_t)de->uncomp_size);
        }
        else {
            _zip_buffer_put_32(buffer, ZIP_UINT32_MAX);
        }
    }

    _zip_buffer_put_16(buffer, _zip_string_length(de->filename));
    ef_total_size = (zip_uint32_t)_zip_ef_size(ef, ZIP_EF_BOTH);
    if (!ZIP_WANT_TORRENTZIP(za)) {
        /* TODO: check for overflow */
        ef_total_size += (zip_uint32_t)_zip_ef_size(de->extra_fields, flags);
    }
    _zip_buffer_put_16(buffer, (zip_uint16_t)ef_total_size);

    if ((flags & ZIP_FL_LOCAL) == 0) {
        _zip_buffer_put_16(buffer, ZIP_WANT_TORRENTZIP(za) ? 0 : _zip_string_length(de->comment));
        _zip_buffer_put_16(buffer, (zip_uint16_t)de->disk_number);
        _zip_buffer_put_16(buffer, de->int_attrib);
        _zip_buffer_put_32(buffer, de->ext_attrib);
        if (de->offset < ZIP_UINT32_MAX)
            _zip_buffer_put_32(buffer, (zip_uint32_t)de->offset);
        else
            _zip_buffer_put_32(buffer, ZIP_UINT32_MAX);
    }

    if (!_zip_buffer_ok(buffer)) {
        zip_error_set(&za->error, ZIP_ER_INTERNAL, 0);
        _zip_buffer_free(buffer);
        _zip_ef_free(ef);
        return -1;
    }

    if (_zip_write(za, buf, _zip_buffer_offset(buffer)) < 0) {
        _zip_buffer_free(buffer);
        _zip_ef_free(ef);
        return -1;
    }

    _zip_buffer_free(buffer);

    if (de->filename) {
        if (_zip_string_write(za, de->filename) < 0) {
            _zip_ef_free(ef);
            return -1;
        }
    }

    if (ef) {
        if (_zip_ef_write(za, ef, ZIP_EF_BOTH) < 0) {
            _zip_ef_free(ef);
            return -1;
        }
    }
    _zip_ef_free(ef);
    if (de->extra_fields && !ZIP_WANT_TORRENTZIP(za)) {
        if (_zip_ef_write(za, de->extra_fields, flags) < 0) {
            return -1;
        }
    }

    if ((flags & ZIP_FL_LOCAL) == 0 && !ZIP_WANT_TORRENTZIP(za)) {
        if (de->comment) {
            if (_zip_string_write(za, de->comment) < 0) {
                return -1;
            }
        }
    }


    return is_zip64;
}


time_t
_zip_d2u_time(zip_uint16_t dtime, zip_uint16_t ddate) {
    struct tm tm;

    memset(&tm, 0, sizeof(tm));

    /* let mktime decide if DST is in effect */
    tm.tm_isdst = -1;

    tm.tm_year = ((ddate >> 9) & 127) + 1980 - 1900;
    tm.tm_mon = ((ddate >> 5) & 15) - 1;
    tm.tm_mday = ddate & 31;

    tm.tm_hour = (dtime >> 11) & 31;
    tm.tm_min = (dtime >> 5) & 63;
    tm.tm_sec = (dtime << 1) & 62;

    return mktime(&tm);
}


static zip_extra_field_t *
_zip_ef_utf8(zip_uint16_t id, zip_string_t *str, zip_error_t *error) {
    const zip_uint8_t *raw;
    zip_uint32_t len;
    zip_buffer_t *buffer;
    zip_extra_field_t *ef;

    if ((raw = _zip_string_get(str, &len, ZIP_FL_ENC_RAW, NULL)) == NULL) {
        /* error already set */
        return NULL;
    }

    if (len + 5 > ZIP_UINT16_MAX) {
        zip_error_set(error, ZIP_ER_INVAL, 0); /* TODO: better error code? */
        return NULL;
    }

    if ((buffer = _zip_buffer_new(NULL, len + 5)) == NULL) {
        zip_error_set(error, ZIP_ER_MEMORY, 0);
        return NULL;
    }

    _zip_buffer_put_8(buffer, 1);
    _zip_buffer_put_32(buffer, _zip_string_crc32(str));
    _zip_buffer_put(buffer, raw, len);

    if (!_zip_buffer_ok(buffer)) {
        zip_error_set(error, ZIP_ER_INTERNAL, 0);
        _zip_buffer_free(buffer);
        return NULL;
    }

    ef = _zip_ef_new(id, (zip_uint16_t)(_zip_buffer_offset(buffer)), _zip_buffer_data(buffer), ZIP_EF_BOTH);
    _zip_buffer_free(buffer);

    return ef;
}


zip_dirent_t *
_zip_get_dirent(zip_t *za, zip_uint64_t idx, zip_flags_t flags, zip_error_t *error) {
    if (error == NULL)
        error = &za->error;

    if (idx >= za->nentry) {
        zip_error_set(error, ZIP_ER_INVAL, 0);
        return NULL;
    }

    if ((flags & ZIP_FL_UNCHANGED) || za->entry[idx].changes == NULL) {
        if (za->entry[idx].orig == NULL) {
            zip_error_set(error, ZIP_ER_INVAL, 0);
            return NULL;
        }
        if (za->entry[idx].deleted && (flags & ZIP_FL_UNCHANGED) == 0) {
            zip_error_set(error, ZIP_ER_DELETED, 0);
            return NULL;
        }
        return za->entry[idx].orig;
    }
    else
        return za->entry[idx].changes;
}


void
_zip_u2d_time(time_t intime, zip_uint16_t *dtime, zip_uint16_t *ddate) {
    struct tm *tpm;
    struct tm tm;
    tpm = zip_localtime(&intime, &tm);
    if (tpm == NULL) {
        /* if localtime fails, return an arbitrary date (1980-01-01 00:00:00) */
        *ddate = (1 << 5) + 1;
        *dtime = 0;
        return;
    }
    if (tpm->tm_year < 80) {
        tpm->tm_year = 80;
    }

    *ddate = (zip_uint16_t)(((tpm->tm_year + 1900 - 1980) << 9) + ((tpm->tm_mon + 1) << 5) + tpm->tm_mday);
    *dtime = (zip_uint16_t)(((tpm->tm_hour) << 11) + ((tpm->tm_min) << 5) + ((tpm->tm_sec) >> 1));
}


void
_zip_dirent_apply_attributes(zip_dirent_t *de, zip_file_attributes_t *attributes, bool force_zip64, zip_uint32_t changed) {
    zip_uint16_t length;

    if (attributes->valid & ZIP_FILE_ATTRIBUTES_GENERAL_PURPOSE_BIT_FLAGS) {
        zip_uint16_t mask = attributes->general_purpose_bit_mask & ZIP_FILE_ATTRIBUTES_GENERAL_PURPOSE_BIT_FLAGS_ALLOWED_MASK;
        de->bitflags = (de->bitflags & ~mask) | (attributes->general_purpose_bit_flags & mask);
    }
    if (attributes->valid & ZIP_FILE_ATTRIBUTES_ASCII) {
        de->int_attrib = (de->int_attrib & ~0x1) | (attributes->ascii ? 1 : 0);
    }
    /* manually set attributes are preferred over attributes provided by source */
    if ((changed & ZIP_DIRENT_ATTRIBUTES) == 0 && (attributes->valid & ZIP_FILE_ATTRIBUTES_EXTERNAL_FILE_ATTRIBUTES)) {
        de->ext_attrib = attributes->external_file_attributes;
    }

    if (de->comp_method == ZIP_CM_LZMA) {
        de->version_needed = 63;
    }
    else if (de->encryption_method == ZIP_EM_AES_128 || de->encryption_method == ZIP_EM_AES_192 || de->encryption_method == ZIP_EM_AES_256) {
        de->version_needed = 51;
    }
    else if (de->comp_method == ZIP_CM_BZIP2) {
        de->version_needed = 46;
    }
    else if (force_zip64 || _zip_dirent_needs_zip64(de, 0)) {
        de->version_needed = 45;
    }
    else if (de->comp_method == ZIP_CM_DEFLATE || de->encryption_method == ZIP_EM_TRAD_PKWARE) {
        de->version_needed = 20;
    }
    else if ((length = _zip_string_length(de->filename)) > 0 && de->filename->raw[length - 1] == '/') {
        de->version_needed = 20;
    }
    else {
        de->version_needed = 10;
    }

    if (attributes->valid & ZIP_FILE_ATTRIBUTES_VERSION_NEEDED) {
        de->version_needed = ZIP_MAX(de->version_needed, attributes->version_needed);
    }

    de->version_madeby = 63 | (de->version_madeby & 0xff00);
    if ((changed & ZIP_DIRENT_ATTRIBUTES) == 0 && (attributes->valid & ZIP_FILE_ATTRIBUTES_HOST_SYSTEM)) {
        de->version_madeby = (de->version_madeby & 0xff) | (zip_uint16_t)(attributes->host_system << 8);
    }
}


/* _zip_dirent_torrent_normalize(de);
   Set values suitable for torrentzip.
*/

void zip_dirent_torrentzip_normalize(zip_dirent_t *de) {
    de->version_madeby = 0;
    de->version_needed = 20; /* 2.0 */
    de->bitflags = 2; /* maximum compression */
    de->comp_method = ZIP_CM_DEFLATE;
    de->compression_level = TORRENTZIP_COMPRESSION_FLAGS;
    de->disk_number = 0;
    de->int_attrib = 0;
    de->ext_attrib = 0;

    /* last_mod, extra_fields, and comment are normalized in zip_dirent_write() directly */

}