Fix extra field modification functions (clone extra fields before modifying).
Check return value of _zip_dirent_clone().
diff --git a/lib/zip_extra_field.c b/lib/zip_extra_field.c
index 5ce002a..d4bc77f 100644
--- a/lib/zip_extra_field.c
+++ b/lib/zip_extra_field.c
@@ -42,6 +42,31 @@
 
 
 struct zip_extra_field *
+_zip_ef_clone(struct zip_extra_field *ef, struct zip_error *error)
+{
+    struct zip_extra_field *head, *prev, *def;
+    
+    head = prev = NULL;
+    
+    while (ef) {
+        if ((def=_zip_ef_new(ef->id, ef->size, ef->data, ef->flags)) == NULL) {
+            _zip_error_set(error, ZIP_ER_MEMORY, 0);
+            _zip_ef_free(head);
+            return NULL;
+        }
+        
+        if (head == NULL)
+            head = def;
+        if (prev)
+            prev->next = def;
+        prev = def;
+    }
+    
+    return head;
+}
+
+
+struct zip_extra_field *
 _zip_ef_delete_by_id(struct zip_extra_field *ef, zip_uint16_t id, zip_uint16_t id_idx, zip_flags_t flags)
 {
     struct zip_extra_field *head, *prev;
@@ -314,6 +339,7 @@
 	}
 	free(ef_raw);
 	
+        /* XXX: remove fields handled internally (Zip64, UTF-8) */
 	e->orig->extra_fields = _zip_ef_merge(e->orig->extra_fields, ef);
     }
 
diff --git a/lib/zip_extra_field_api.c b/lib/zip_extra_field_api.c
index f0ca76c..0458c70 100644
--- a/lib/zip_extra_field_api.c
+++ b/lib/zip_extra_field_api.c
@@ -42,9 +42,19 @@
 {
     struct zip_dirent *de;
     
-    if ((de=_zip_get_dirent(za, idx, flags, &za->error)) == NULL)
+    if (_zip_get_dirent(za, idx, 0, NULL) == NULL)
 	return -1;
+    
+    if (ZIP_IS_RDONLY(za)) {
+	_zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
+	return -1;
+    }
 
+    if (_zip_file_extra_field_prepare_for_change(za, idx) < 0)
+        return -1;
+    
+    de = za->entry[idx].changes;
+    
     de->extra_fields = _zip_ef_delete_by_id(de->extra_fields, ZIP_EXTRA_FIELD_ALL, ef_idx, flags);
     return 0;
 }
@@ -56,9 +66,19 @@
 {
     struct zip_dirent *de;
     
-    if ((de=_zip_get_dirent(za, idx, flags, &za->error)) == NULL)
+    if (_zip_get_dirent(za, idx, 0, NULL) == NULL)
 	return -1;
 
+    if (ZIP_IS_RDONLY(za)) {
+	_zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
+	return -1;
+    }
+    
+    if (_zip_file_extra_field_prepare_for_change(za, idx) < 0)
+        return -1;
+    
+    de = za->entry[idx].changes;
+
     de->extra_fields = _zip_ef_delete_by_id(de->extra_fields, ef_id, ef_idx, flags);
     return 0;
 }
@@ -192,14 +212,24 @@
     struct zip_extra_field *ef, *ef_prev, *ef_new;
     int i, found;
 
-    if ((de=_zip_get_dirent(za, idx, flags, &za->error)) == NULL)
+    if (_zip_get_dirent(za, idx, 0, NULL) == NULL)
 	return -1;
-
+    
+    if (ZIP_IS_RDONLY(za)) {
+	_zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
+	return -1;
+    }
+    
     if (ef_id == ZIP_EF_ZIP64 || ef_id == ZIP_EF_UTF_8_NAME || ef_id == ZIP_EF_UTF_8_COMMENT) {
 	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
 	return -1;
     }
 
+    if (_zip_file_extra_field_prepare_for_change(za, idx) < 0)
+        return -1;
+    
+    de = za->entry[idx].changes;
+
     if (flags & ZIP_EF_LOCAL)
 	ls = _zip_ef_size(de->extra_fields, ZIP_EF_LOCAL);
     else
@@ -258,3 +288,38 @@
     
     return 0;
 }
+
+
+
+int
+_zip_file_extra_field_prepare_for_change(struct zip *za, zip_uint64_t idx)
+{
+    struct zip_entry *e;
+    
+    if (idx >= za->nentry) {
+        _zip_error_set(&za->error, ZIP_ER_INVAL, 0);
+        return -1;
+    }
+    
+    e = za->entry+idx;
+    
+    if (e->changes && (e->changes->changed & ZIP_DIRENT_EXTRA_FIELD))
+        return 0;
+    
+    if (_zip_read_local_ef(za, idx) < 0)
+        return -1;
+    
+    if (e->changes == NULL) {
+        if ((e->changes=_zip_dirent_clone(e->orig)) == NULL) {
+            _zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
+            return -1;
+        }
+    }
+    
+    if ((e->changes->extra_fields=_zip_ef_clone(e->changes->extra_fields, &za->error)) == 0)
+        return -1;
+    e->changes->changed |= ZIP_DIRENT_EXTRA_FIELD;
+    
+    return 0;
+}
+
diff --git a/lib/zip_file_replace.c b/lib/zip_file_replace.c
index a73a261..cc1b72a 100644
--- a/lib/zip_file_replace.c
+++ b/lib/zip_file_replace.c
@@ -93,11 +93,15 @@
     _zip_unchange_data(za->entry+idx);
 
     if (za->entry[idx].orig != NULL && (za->entry[idx].changes == NULL || (za->entry[idx].changes->changed & ZIP_DIRENT_COMP_METHOD) == 0)) {
-	if (za->entry[idx].changes == NULL)
-	    za->entry[idx].changes = _zip_dirent_clone(za->entry[idx].orig);
+        if (za->entry[idx].changes == NULL) {
+            if ((za->entry[idx].changes=_zip_dirent_clone(za->entry[idx].orig)) == NULL) {
+                _zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
+                return -1;
+            }
+        }
 
-	za->entry[idx].changes->comp_method = ZIP_CM_REPLACED_DEFAULT;
-	za->entry[idx].changes->changed |= ZIP_DIRENT_COMP_METHOD;
+        za->entry[idx].changes->comp_method = ZIP_CM_REPLACED_DEFAULT;
+        za->entry[idx].changes->changed |= ZIP_DIRENT_COMP_METHOD;
     }
 	
     za->entry[idx].source = source;
diff --git a/lib/zip_file_set_comment.c b/lib/zip_file_set_comment.c
index 16955ac..143d03a 100644
--- a/lib/zip_file_set_comment.c
+++ b/lib/zip_file_set_comment.c
@@ -85,10 +85,14 @@
 	changed = (cstr != NULL);
 	
     if (changed) {
-	if (e->changes == NULL)
-	    e->changes = _zip_dirent_clone(e->orig);
-	e->changes->comment = cstr;
-	e->changes->changed |= ZIP_DIRENT_COMMENT;
+        if (e->changes == NULL) {
+            if ((e->changes=_zip_dirent_clone(e->orig)) == NULL) {
+                _zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
+                return -1;
+            }
+        }
+        e->changes->comment = cstr;
+        e->changes->changed |= ZIP_DIRENT_COMMENT;
     }
     else {
 	_zip_string_free(cstr);
diff --git a/lib/zip_set_file_compression.c b/lib/zip_set_file_compression.c
index bbcaa65..e62cf45 100644
--- a/lib/zip_set_file_compression.c
+++ b/lib/zip_set_file_compression.c
@@ -70,11 +70,15 @@
 	}
     }
     else {
-	if (e->changes == NULL)
-	    e->changes = _zip_dirent_clone(e->orig);
+        if (e->changes == NULL) {
+            if ((e->changes=_zip_dirent_clone(e->orig)) == NULL) {
+                _zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
+                return -1;
+            }
 
-	e->changes->comp_method = method;
-	e->changes->changed |= ZIP_DIRENT_COMP_METHOD;
+            e->changes->comp_method = method;
+            e->changes->changed |= ZIP_DIRENT_COMP_METHOD;
+        }
     }
     
     return 0;
diff --git a/lib/zip_set_name.c b/lib/zip_set_name.c
index 6a91cd7..dc62c74 100644
--- a/lib/zip_set_name.c
+++ b/lib/zip_set_name.c
@@ -95,10 +95,14 @@
 	changed = (str != NULL);
 	
     if (changed) {
-	if (e->changes == NULL)
-	    e->changes = _zip_dirent_clone(e->orig);
-	e->changes->filename = str;
-	e->changes->changed |= ZIP_DIRENT_FILENAME;
+        if (e->changes == NULL) {
+            if ((e->changes=_zip_dirent_clone(e->orig)) == NULL) {
+                _zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
+                return -1;
+            }
+        }
+        e->changes->filename = str;
+        e->changes->changed |= ZIP_DIRENT_FILENAME;
     }
     else {
 	_zip_string_free(str);
diff --git a/lib/zipint.h b/lib/zipint.h
index 7d5235f..84d377e 100644
--- a/lib/zipint.h
+++ b/lib/zipint.h
@@ -393,6 +393,7 @@
 void _zip_dirent_torrent_normalize(struct zip_dirent *);
 int _zip_dirent_write(struct zip_dirent *, FILE *, zip_flags_t, struct zip_error *);
 
+struct zip_extra_field *_zip_ef_clone(struct zip_extra_field *, struct zip_error *);
 struct zip_extra_field *_zip_ef_delete_by_id(struct zip_extra_field *, zip_uint16_t, zip_uint16_t, zip_flags_t);
 void _zip_ef_free(struct zip_extra_field *);
 const zip_uint8_t *_zip_ef_get_by_id(struct zip_extra_field *, zip_uint16_t *, zip_uint16_t, zip_uint16_t, zip_flags_t, struct zip_error *);
@@ -416,6 +417,7 @@
 
 const zip_uint8_t *_zip_extract_extra_field_by_id(struct zip_error *, zip_uint16_t, int, const zip_uint8_t *, zip_uint16_t, zip_uint16_t *);
 
+int _zip_file_extra_field_prepare_for_change(struct zip *, zip_uint64_t);
 int _zip_file_fillbuf(void *, size_t, struct zip_file *);
 zip_uint64_t _zip_file_get_offset(struct zip *, zip_uint64_t, struct zip_error *);