Blender  V2.93
undofile.c
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2004 Blender Foundation
17  * All rights reserved.
18  * .blend file reading entry point
19  */
20 
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <math.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 
32 /* open/close */
33 #ifndef _WIN32
34 # include <unistd.h>
35 #else
36 # include <io.h>
37 #endif
38 
39 #include "MEM_guardedalloc.h"
40 
41 #include "DNA_listBase.h"
42 
43 #include "BLI_blenlib.h"
44 #include "BLI_ghash.h"
45 
46 #include "BLO_readfile.h"
47 #include "BLO_undofile.h"
48 
49 #include "BKE_lib_id.h"
50 #include "BKE_main.h"
51 
52 /* keep last */
53 #include "BLI_strict_flags.h"
54 
55 /* **************** support for memory-write, for undo buffers *************** */
56 
57 /* not memfile itself */
58 void BLO_memfile_free(MemFile *memfile)
59 {
60  MemFileChunk *chunk;
61 
62  while ((chunk = BLI_pophead(&memfile->chunks))) {
63  if (chunk->is_identical == false) {
64  MEM_freeN((void *)chunk->buf);
65  }
66  MEM_freeN(chunk);
67  }
68  memfile->size = 0;
69 }
70 
71 /* to keep list of memfiles consistent, 'first' is always first in list */
72 /* result is that 'first' is being freed */
73 void BLO_memfile_merge(MemFile *first, MemFile *second)
74 {
75  /* We use this mapping to store the memory buffers from second memfile chunks which are not owned
76  * by it (i.e. shared with some previous memory steps). */
77  GHash *buffer_to_second_memchunk = BLI_ghash_new(
79 
80  /* First, detect all memchunks in second memfile that are not owned by it. */
81  for (MemFileChunk *sc = second->chunks.first; sc != NULL; sc = sc->next) {
82  if (sc->is_identical) {
83  BLI_ghash_insert(buffer_to_second_memchunk, (void *)sc->buf, sc);
84  }
85  }
86 
87  /* Now, check all chunks from first memfile (the one we are removing), and if a memchunk owned by
88  * it is also used by the second memfile, transfer the ownership. */
89  for (MemFileChunk *fc = first->chunks.first; fc != NULL; fc = fc->next) {
90  if (!fc->is_identical) {
91  MemFileChunk *sc = BLI_ghash_lookup(buffer_to_second_memchunk, fc->buf);
92  if (sc != NULL) {
94  sc->is_identical = false;
95  fc->is_identical = true;
96  }
97  /* Note that if the second memfile does not use that chunk, we assume that the first one
98  * fully owns it without sharing it with any other memfile, and hence it should be freed with
99  * it. */
100  }
101  }
102 
103  BLI_ghash_free(buffer_to_second_memchunk, NULL, NULL);
104 
105  BLO_memfile_free(first);
106 }
107 
108 /* Clear is_identical_future before adding next memfile. */
110 {
111  LISTBASE_FOREACH (MemFileChunk *, chunk, &memfile->chunks) {
112  chunk->is_identical_future = false;
113  }
114 }
115 
117  MemFile *written_memfile,
118  MemFile *reference_memfile)
119 {
120  mem_data->written_memfile = written_memfile;
121  mem_data->reference_memfile = reference_memfile;
122  mem_data->reference_current_chunk = reference_memfile ? reference_memfile->chunks.first : NULL;
123 
124  /* If we have a reference memfile, we generate a mapping between the session_uuid's of the
125  * IDs stored in that previous undo step, and its first matching memchunk. This will allow
126  * us to easily find the existing undo memory storage of IDs even when some re-ordering in
127  * current Main data-base broke the order matching with the memchunks from previous step.
128  */
129  if (reference_memfile != NULL) {
132  uint current_session_uuid = MAIN_ID_SESSION_UUID_UNSET;
133  LISTBASE_FOREACH (MemFileChunk *, mem_chunk, &reference_memfile->chunks) {
134  if (!ELEM(mem_chunk->id_session_uuid, MAIN_ID_SESSION_UUID_UNSET, current_session_uuid)) {
135  current_session_uuid = mem_chunk->id_session_uuid;
136  void **entry;
138  POINTER_FROM_UINT(current_session_uuid),
139  &entry)) {
140  *entry = mem_chunk;
141  }
142  else {
143  BLI_assert(0);
144  }
145  }
146  }
147  }
148 }
149 
151 {
152  if (mem_data->id_session_uuid_mapping != NULL) {
154  }
155 }
156 
157 void BLO_memfile_chunk_add(MemFileWriteData *mem_data, const char *buf, size_t size)
158 {
159  MemFile *memfile = mem_data->written_memfile;
160  MemFileChunk **compchunk_step = &mem_data->reference_current_chunk;
161 
162  MemFileChunk *curchunk = MEM_mallocN(sizeof(MemFileChunk), "MemFileChunk");
163  curchunk->size = size;
164  curchunk->buf = NULL;
165  curchunk->is_identical = false;
166  /* This is unsafe in the sense that an app handler or other code that does not
167  * perform an undo push may make changes after the last undo push that
168  * will then not be undo. Though it's not entirely clear that is wrong behavior. */
169  curchunk->is_identical_future = true;
170  curchunk->id_session_uuid = mem_data->current_id_session_uuid;
171  BLI_addtail(&memfile->chunks, curchunk);
172 
173  /* we compare compchunk with buf */
174  if (*compchunk_step != NULL) {
175  MemFileChunk *compchunk = *compchunk_step;
176  if (compchunk->size == curchunk->size) {
177  if (memcmp(compchunk->buf, buf, size) == 0) {
178  curchunk->buf = compchunk->buf;
179  curchunk->is_identical = true;
180  compchunk->is_identical_future = true;
181  }
182  }
183  *compchunk_step = compchunk->next;
184  }
185 
186  /* not equal... */
187  if (curchunk->buf == NULL) {
188  char *buf_new = MEM_mallocN(size, "Chunk buffer");
189  memcpy(buf_new, buf, size);
190  curchunk->buf = buf_new;
191  memfile->size += size;
192  }
193 }
194 
195 struct Main *BLO_memfile_main_get(struct MemFile *memfile,
196  struct Main *bmain,
197  struct Scene **r_scene)
198 {
199  struct Main *bmain_undo = NULL;
202  memfile,
203  &(const struct BlendFileReadParams){0},
204  NULL);
205 
206  if (bfd) {
207  bmain_undo = bfd->main;
208  if (r_scene) {
209  *r_scene = bfd->curscene;
210  }
211 
212  MEM_freeN(bfd);
213  }
214 
215  return bmain_undo;
216 }
217 
223 bool BLO_memfile_write_file(struct MemFile *memfile, const char *filename)
224 {
225  MemFileChunk *chunk;
226  int file, oflags;
227 
228  /* note: This is currently used for autosave and 'quit.blend',
229  * where _not_ following symlinks is OK,
230  * however if this is ever executed explicitly by the user,
231  * we may want to allow writing to symlinks.
232  */
233 
234  oflags = O_BINARY | O_WRONLY | O_CREAT | O_TRUNC;
235 #ifdef O_NOFOLLOW
236  /* use O_NOFOLLOW to avoid writing to a symlink - use 'O_EXCL' (CVE-2008-1103) */
237  oflags |= O_NOFOLLOW;
238 #else
239  /* TODO(sergey): How to deal with symlinks on windows? */
240 # ifndef _MSC_VER
241 # warning "Symbolic links will be followed on undo save, possibly causing CVE-2008-1103"
242 # endif
243 #endif
244  file = BLI_open(filename, oflags, 0666);
245 
246  if (file == -1) {
247  fprintf(stderr,
248  "Unable to save '%s': %s\n",
249  filename,
250  errno ? strerror(errno) : "Unknown error opening file");
251  return false;
252  }
253 
254  for (chunk = memfile->chunks.first; chunk; chunk = chunk->next) {
255 #ifdef _WIN32
256  if ((size_t)write(file, chunk->buf, (uint)chunk->size) != chunk->size)
257 #else
258  if ((size_t)write(file, chunk->buf, chunk->size) != chunk->size)
259 #endif
260  {
261  break;
262  }
263  }
264 
265  close(file);
266 
267  if (chunk) {
268  fprintf(stderr,
269  "Unable to save '%s': %s\n",
270  filename,
271  errno ? strerror(errno) : "Unknown error writing file");
272  return false;
273  }
274  return true;
275 }
#define MAIN_ID_SESSION_UUID_UNSET
Definition: BKE_lib_id.h:73
const char * BKE_main_blendfile_path(const struct Main *bmain) ATTR_NONNULL()
#define BLI_assert(a)
Definition: BLI_assert.h:58
int BLI_open(const char *filename, int oflag, int pmode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: fileops.c:1017
#define O_BINARY
Definition: BLI_fileops.h:182
unsigned int BLI_ghashutil_ptrhash(const void *key)
GHash * BLI_ghash_new(GHashHashFP hashfp, GHashCmpFP cmpfp, const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
Definition: BLI_ghash.c:718
unsigned int BLI_ghashutil_inthash_p_simple(const void *ptr)
bool BLI_ghashutil_intcmp(const void *a, const void *b)
bool BLI_ghashutil_ptrcmp(const void *a, const void *b)
void BLI_ghash_insert(GHash *gh, void *key, void *val)
Definition: BLI_ghash.c:756
void BLI_ghash_free(GHash *gh, GHashKeyFreeFP keyfreefp, GHashValFreeFP valfreefp)
Definition: BLI_ghash.c:1008
bool BLI_ghash_ensure_p(GHash *gh, void *key, void ***r_val) ATTR_WARN_UNUSED_RESULT
Definition: BLI_ghash.c:851
void * BLI_ghash_lookup(GHash *gh, const void *key) ATTR_WARN_UNUSED_RESULT
Definition: BLI_ghash.c:803
void * BLI_pophead(ListBase *listbase) ATTR_NONNULL(1)
Definition: listbase.c:257
#define LISTBASE_FOREACH(type, var, list)
Definition: BLI_listbase.h:172
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:110
Strict compiler flags for areas of code we want to ensure don't do conversions without us knowing abo...
unsigned int uint
Definition: BLI_sys_types.h:83
#define ELEM(...)
#define POINTER_FROM_UINT(i)
external readfile function prototypes.
BlendFileData * BLO_read_from_memfile(struct Main *oldmain, const char *filename, struct MemFile *memfile, const struct BlendFileReadParams *params, struct ReportList *reports)
These structs are the foundation for all linked lists in the library system.
Read Guarded memory(de)allocation.
ATTR_WARN_UNUSED_RESULT const BMFlagLayer * oflags
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition: btDbvt.cpp:52
FILE * file
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:41
void *(* MEM_mallocN)(size_t len, const char *str)
Definition: mallocn.c:47
struct Scene * curscene
Definition: BLO_readfile.h:78
struct Main * main
Definition: BLO_readfile.h:70
void * first
Definition: DNA_listBase.h:47
Definition: BKE_main.h:116
bool is_identical_future
Definition: BLO_undofile.h:40
uint id_session_uuid
Definition: BLO_undofile.h:43
bool is_identical
Definition: BLO_undofile.h:36
const char * buf
Definition: BLO_undofile.h:32
MemFile * reference_memfile
Definition: BLO_undofile.h:53
MemFile * written_memfile
Definition: BLO_undofile.h:52
struct GHash * id_session_uuid_mapping
Definition: BLO_undofile.h:59
MemFileChunk * reference_current_chunk
Definition: BLO_undofile.h:56
uint current_id_session_uuid
Definition: BLO_undofile.h:55
ListBase chunks
Definition: BLO_undofile.h:47
size_t size
Definition: BLO_undofile.h:48
void BLO_memfile_chunk_add(MemFileWriteData *mem_data, const char *buf, size_t size)
Definition: undofile.c:157
void BLO_memfile_write_init(MemFileWriteData *mem_data, MemFile *written_memfile, MemFile *reference_memfile)
Definition: undofile.c:116
struct Main * BLO_memfile_main_get(struct MemFile *memfile, struct Main *bmain, struct Scene **r_scene)
Definition: undofile.c:195
void BLO_memfile_clear_future(MemFile *memfile)
Definition: undofile.c:109
void BLO_memfile_free(MemFile *memfile)
Definition: undofile.c:58
bool BLO_memfile_write_file(struct MemFile *memfile, const char *filename)
Definition: undofile.c:223
void BLO_memfile_merge(MemFile *first, MemFile *second)
Definition: undofile.c:73
void BLO_memfile_write_finalize(MemFileWriteData *mem_data)
Definition: undofile.c:150