1edf3f97aSBram Moolenaar /* vi:set ts=8 sts=4 sw=4 noet:
2071d4279SBram Moolenaar *
3071d4279SBram Moolenaar * VIM - Vi IMproved by Bram Moolenaar
4071d4279SBram Moolenaar *
5071d4279SBram Moolenaar * Do ":help uganda" in Vim to read copying and usage conditions.
6071d4279SBram Moolenaar * Do ":help credits" in Vim to see a list of people who contributed.
7071d4279SBram Moolenaar * See README.txt for an overview of the Vim source code.
8071d4279SBram Moolenaar */
9071d4279SBram Moolenaar
10071d4279SBram Moolenaar /*
11071d4279SBram Moolenaar * memfile.c: Contains the functions for handling blocks of memory which can
12071d4279SBram Moolenaar * be stored in a file. This is the implementation of a sort of virtual memory.
13071d4279SBram Moolenaar *
14071d4279SBram Moolenaar * A memfile consists of a sequence of blocks. The blocks numbered from 0
15071d4279SBram Moolenaar * upwards have been assigned a place in the actual file. The block number
16071d4279SBram Moolenaar * is equal to the page number in the file. The
17071d4279SBram Moolenaar * blocks with negative numbers are currently in memory only. They can be
18071d4279SBram Moolenaar * assigned a place in the file when too much memory is being used. At that
19071d4279SBram Moolenaar * moment they get a new, positive, number. A list is used for translation of
20071d4279SBram Moolenaar * negative to positive numbers.
21071d4279SBram Moolenaar *
22071d4279SBram Moolenaar * The size of a block is a multiple of a page size, normally the page size of
23071d4279SBram Moolenaar * the device the file is on. Most blocks are 1 page long. A Block of multiple
24071d4279SBram Moolenaar * pages is used for a line that does not fit in a single page.
25071d4279SBram Moolenaar *
26071d4279SBram Moolenaar * Each block can be in memory and/or in a file. The block stays in memory
27071d4279SBram Moolenaar * as long as it is locked. If it is no longer locked it can be swapped out to
28071d4279SBram Moolenaar * the file. It is only written to the file if it has been changed.
29071d4279SBram Moolenaar *
30071d4279SBram Moolenaar * Under normal operation the file is created when opening the memory file and
31071d4279SBram Moolenaar * deleted when closing the memory file. Only with recovery an existing memory
32071d4279SBram Moolenaar * file is opened.
33071d4279SBram Moolenaar */
34071d4279SBram Moolenaar
35071d4279SBram Moolenaar #include "vim.h"
36071d4279SBram Moolenaar
37071d4279SBram Moolenaar /*
38071d4279SBram Moolenaar * Some systems have the page size in statfs.f_bsize, some in stat.st_blksize
39071d4279SBram Moolenaar */
40071d4279SBram Moolenaar #ifdef HAVE_ST_BLKSIZE
41071d4279SBram Moolenaar # define STATFS stat
42071d4279SBram Moolenaar # define F_BSIZE st_blksize
43071d4279SBram Moolenaar # define fstatfs(fd, buf, len, nul) mch_fstat((fd), (buf))
44071d4279SBram Moolenaar #else
45071d4279SBram Moolenaar # ifdef HAVE_SYS_STATFS_H
46071d4279SBram Moolenaar # include <sys/statfs.h>
47071d4279SBram Moolenaar # define STATFS statfs
48071d4279SBram Moolenaar # define F_BSIZE f_bsize
49071d4279SBram Moolenaar # endif
50071d4279SBram Moolenaar #endif
51071d4279SBram Moolenaar
52071d4279SBram Moolenaar /*
53071d4279SBram Moolenaar * for Amiga Dos 2.0x we use Flush
54071d4279SBram Moolenaar */
55071d4279SBram Moolenaar #ifdef AMIGA
56071d4279SBram Moolenaar # ifdef FEAT_ARP
574ba37b58SBram Moolenaar extern int dos2; // this is in os_amiga.c
58071d4279SBram Moolenaar # endif
59071d4279SBram Moolenaar # ifdef SASC
60071d4279SBram Moolenaar # include <proto/dos.h>
614ba37b58SBram Moolenaar # include <ios1.h> // for chkufb()
62071d4279SBram Moolenaar # endif
63071d4279SBram Moolenaar #endif
64071d4279SBram Moolenaar
654ba37b58SBram Moolenaar #define MEMFILE_PAGE_SIZE 4096 // default page size
66071d4279SBram Moolenaar
674ba37b58SBram Moolenaar static long_u total_mem_used = 0; // total memory used for memfiles
68071d4279SBram Moolenaar
6992b8b2d3SBram Moolenaar static void mf_ins_hash(memfile_T *, bhdr_T *);
7092b8b2d3SBram Moolenaar static void mf_rem_hash(memfile_T *, bhdr_T *);
7192b8b2d3SBram Moolenaar static bhdr_T *mf_find_hash(memfile_T *, blocknr_T);
7292b8b2d3SBram Moolenaar static void mf_ins_used(memfile_T *, bhdr_T *);
7392b8b2d3SBram Moolenaar static void mf_rem_used(memfile_T *, bhdr_T *);
7492b8b2d3SBram Moolenaar static bhdr_T *mf_release(memfile_T *, int);
7592b8b2d3SBram Moolenaar static bhdr_T *mf_alloc_bhdr(memfile_T *, int);
7692b8b2d3SBram Moolenaar static void mf_free_bhdr(bhdr_T *);
7792b8b2d3SBram Moolenaar static void mf_ins_free(memfile_T *, bhdr_T *);
7892b8b2d3SBram Moolenaar static bhdr_T *mf_rem_free(memfile_T *);
7992b8b2d3SBram Moolenaar static int mf_read(memfile_T *, bhdr_T *);
8092b8b2d3SBram Moolenaar static int mf_write(memfile_T *, bhdr_T *);
818767f52fSBram Moolenaar static int mf_write_block(memfile_T *mfp, bhdr_T *hp, off_T offset, unsigned size);
8292b8b2d3SBram Moolenaar static int mf_trans_add(memfile_T *, bhdr_T *);
8392b8b2d3SBram Moolenaar static void mf_do_open(memfile_T *, char_u *, int);
8492b8b2d3SBram Moolenaar static void mf_hash_init(mf_hashtab_T *);
8592b8b2d3SBram Moolenaar static void mf_hash_free(mf_hashtab_T *);
8692b8b2d3SBram Moolenaar static void mf_hash_free_all(mf_hashtab_T *);
8792b8b2d3SBram Moolenaar static mf_hashitem_T *mf_hash_find(mf_hashtab_T *, blocknr_T);
8892b8b2d3SBram Moolenaar static void mf_hash_add_item(mf_hashtab_T *, mf_hashitem_T *);
8992b8b2d3SBram Moolenaar static void mf_hash_rem_item(mf_hashtab_T *, mf_hashitem_T *);
9092b8b2d3SBram Moolenaar static int mf_hash_grow(mf_hashtab_T *);
91071d4279SBram Moolenaar
92071d4279SBram Moolenaar /*
93071d4279SBram Moolenaar * The functions for using a memfile:
94071d4279SBram Moolenaar *
95071d4279SBram Moolenaar * mf_open() open a new or existing memfile
96071d4279SBram Moolenaar * mf_open_file() open a swap file for an existing memfile
97071d4279SBram Moolenaar * mf_close() close (and delete) a memfile
98071d4279SBram Moolenaar * mf_new() create a new block in a memfile and lock it
99071d4279SBram Moolenaar * mf_get() get an existing block and lock it
100071d4279SBram Moolenaar * mf_put() unlock a block, may be marked for writing
101071d4279SBram Moolenaar * mf_free() remove a block
102071d4279SBram Moolenaar * mf_sync() sync changed parts of memfile to disk
103071d4279SBram Moolenaar * mf_release_all() release as much memory as possible
104071d4279SBram Moolenaar * mf_trans_del() may translate negative to positive block number
105071d4279SBram Moolenaar * mf_fullname() make file name full path (use before first :cd)
106071d4279SBram Moolenaar */
107071d4279SBram Moolenaar
108071d4279SBram Moolenaar /*
109071d4279SBram Moolenaar * Open an existing or new memory block file.
110071d4279SBram Moolenaar *
111071d4279SBram Moolenaar * fname: name of file to use (NULL means no file at all)
112071d4279SBram Moolenaar * Note: fname must have been allocated, it is not copied!
113071d4279SBram Moolenaar * If opening the file fails, fname is freed.
114071d4279SBram Moolenaar * flags: flags for open() call
115071d4279SBram Moolenaar *
116071d4279SBram Moolenaar * If fname != NULL and file cannot be opened, fail.
117071d4279SBram Moolenaar *
118071d4279SBram Moolenaar * return value: identifier for this memory block file.
119071d4279SBram Moolenaar */
120071d4279SBram Moolenaar memfile_T *
mf_open(char_u * fname,int flags)12152ea13daSBram Moolenaar mf_open(char_u *fname, int flags)
122071d4279SBram Moolenaar {
123071d4279SBram Moolenaar memfile_T *mfp;
1248767f52fSBram Moolenaar off_T size;
125a03dbed9SBram Moolenaar #if defined(STATFS) && defined(UNIX) && !defined(__QNX__) && !defined(__minix)
126071d4279SBram Moolenaar # define USE_FSTATFS
127071d4279SBram Moolenaar struct STATFS stf;
128071d4279SBram Moolenaar #endif
129071d4279SBram Moolenaar
130c799fe20SBram Moolenaar if ((mfp = ALLOC_ONE(memfile_T)) == NULL)
131071d4279SBram Moolenaar return NULL;
132071d4279SBram Moolenaar
1334ba37b58SBram Moolenaar if (fname == NULL) // no file for this memfile, use memory only
134071d4279SBram Moolenaar {
135071d4279SBram Moolenaar mfp->mf_fname = NULL;
136071d4279SBram Moolenaar mfp->mf_ffname = NULL;
137071d4279SBram Moolenaar mfp->mf_fd = -1;
138071d4279SBram Moolenaar }
139071d4279SBram Moolenaar else
140071d4279SBram Moolenaar {
1414ba37b58SBram Moolenaar mf_do_open(mfp, fname, flags); // try to open the file
142071d4279SBram Moolenaar
1434ba37b58SBram Moolenaar // if the file cannot be opened, return here
144071d4279SBram Moolenaar if (mfp->mf_fd < 0)
145071d4279SBram Moolenaar {
146071d4279SBram Moolenaar vim_free(mfp);
147071d4279SBram Moolenaar return NULL;
148071d4279SBram Moolenaar }
149071d4279SBram Moolenaar }
150071d4279SBram Moolenaar
1514ba37b58SBram Moolenaar mfp->mf_free_first = NULL; // free list is empty
1524ba37b58SBram Moolenaar mfp->mf_used_first = NULL; // used list is empty
153071d4279SBram Moolenaar mfp->mf_used_last = NULL;
154071d4279SBram Moolenaar mfp->mf_dirty = FALSE;
155071d4279SBram Moolenaar mfp->mf_used_count = 0;
156b05b10a3SBram Moolenaar mf_hash_init(&mfp->mf_hash);
157b05b10a3SBram Moolenaar mf_hash_init(&mfp->mf_trans);
158071d4279SBram Moolenaar mfp->mf_page_size = MEMFILE_PAGE_SIZE;
159a8ffcbbfSBram Moolenaar #ifdef FEAT_CRYPT
160a8ffcbbfSBram Moolenaar mfp->mf_old_key = NULL;
161a8ffcbbfSBram Moolenaar #endif
162071d4279SBram Moolenaar
163071d4279SBram Moolenaar #ifdef USE_FSTATFS
164071d4279SBram Moolenaar /*
165071d4279SBram Moolenaar * Try to set the page size equal to the block size of the device.
166071d4279SBram Moolenaar * Speeds up I/O a lot.
167071d4279SBram Moolenaar * When recovering, the actual block size will be retrieved from block 0
168071d4279SBram Moolenaar * in ml_recover(). The size used here may be wrong, therefore
169071d4279SBram Moolenaar * mf_blocknr_max must be rounded up.
170071d4279SBram Moolenaar */
171071d4279SBram Moolenaar if (mfp->mf_fd >= 0
172071d4279SBram Moolenaar && fstatfs(mfp->mf_fd, &stf, sizeof(struct statfs), 0) == 0
173071d4279SBram Moolenaar && stf.F_BSIZE >= MIN_SWAP_PAGE_SIZE
174071d4279SBram Moolenaar && stf.F_BSIZE <= MAX_SWAP_PAGE_SIZE)
175071d4279SBram Moolenaar mfp->mf_page_size = stf.F_BSIZE;
176071d4279SBram Moolenaar #endif
177071d4279SBram Moolenaar
178071d4279SBram Moolenaar if (mfp->mf_fd < 0 || (flags & (O_TRUNC|O_EXCL))
1798767f52fSBram Moolenaar || (size = vim_lseek(mfp->mf_fd, (off_T)0L, SEEK_END)) <= 0)
1804ba37b58SBram Moolenaar mfp->mf_blocknr_max = 0; // no file or empty file
181071d4279SBram Moolenaar else
182071d4279SBram Moolenaar mfp->mf_blocknr_max = (blocknr_T)((size + mfp->mf_page_size - 1)
183071d4279SBram Moolenaar / mfp->mf_page_size);
184071d4279SBram Moolenaar mfp->mf_blocknr_min = -1;
185071d4279SBram Moolenaar mfp->mf_neg_count = 0;
186071d4279SBram Moolenaar mfp->mf_infile_count = mfp->mf_blocknr_max;
187fe265ff5SBram Moolenaar
188fe265ff5SBram Moolenaar /*
189fe265ff5SBram Moolenaar * Compute maximum number of pages ('maxmem' is in Kbyte):
190fe265ff5SBram Moolenaar * 'mammem' * 1Kbyte / page-size-in-bytes.
191fe265ff5SBram Moolenaar * Avoid overflow by first reducing page size as much as possible.
192fe265ff5SBram Moolenaar */
193fe265ff5SBram Moolenaar {
194fe265ff5SBram Moolenaar int shift = 10;
195fe265ff5SBram Moolenaar unsigned page_size = mfp->mf_page_size;
196fe265ff5SBram Moolenaar
197fe265ff5SBram Moolenaar while (shift > 0 && (page_size & 1) == 0)
198fe265ff5SBram Moolenaar {
199fe265ff5SBram Moolenaar page_size = page_size >> 1;
200fe265ff5SBram Moolenaar --shift;
201fe265ff5SBram Moolenaar }
202fe265ff5SBram Moolenaar mfp->mf_used_count_max = (p_mm << shift) / page_size;
203fe265ff5SBram Moolenaar if (mfp->mf_used_count_max < 10)
204fe265ff5SBram Moolenaar mfp->mf_used_count_max = 10;
205fe265ff5SBram Moolenaar }
206071d4279SBram Moolenaar
207071d4279SBram Moolenaar return mfp;
208071d4279SBram Moolenaar }
209071d4279SBram Moolenaar
210071d4279SBram Moolenaar /*
211071d4279SBram Moolenaar * Open a file for an existing memfile. Used when updatecount set from 0 to
212071d4279SBram Moolenaar * some value.
213071d4279SBram Moolenaar * If the file already exists, this fails.
214071d4279SBram Moolenaar * "fname" is the name of file to use (NULL means no file at all)
215071d4279SBram Moolenaar * Note: "fname" must have been allocated, it is not copied! If opening the
216071d4279SBram Moolenaar * file fails, "fname" is freed.
217071d4279SBram Moolenaar *
218071d4279SBram Moolenaar * return value: FAIL if file could not be opened, OK otherwise
219071d4279SBram Moolenaar */
220071d4279SBram Moolenaar int
mf_open_file(memfile_T * mfp,char_u * fname)22152ea13daSBram Moolenaar mf_open_file(memfile_T *mfp, char_u *fname)
222071d4279SBram Moolenaar {
2234ba37b58SBram Moolenaar mf_do_open(mfp, fname, O_RDWR|O_CREAT|O_EXCL); // try to open the file
224071d4279SBram Moolenaar
225071d4279SBram Moolenaar if (mfp->mf_fd < 0)
226071d4279SBram Moolenaar return FAIL;
227071d4279SBram Moolenaar
228071d4279SBram Moolenaar mfp->mf_dirty = TRUE;
229071d4279SBram Moolenaar return OK;
230071d4279SBram Moolenaar }
231071d4279SBram Moolenaar
232071d4279SBram Moolenaar /*
233945e2dbbSBram Moolenaar * Close a memory file and delete the associated file if 'del_file' is TRUE.
234071d4279SBram Moolenaar */
235071d4279SBram Moolenaar void
mf_close(memfile_T * mfp,int del_file)23652ea13daSBram Moolenaar mf_close(memfile_T *mfp, int del_file)
237071d4279SBram Moolenaar {
238071d4279SBram Moolenaar bhdr_T *hp, *nextp;
239071d4279SBram Moolenaar
2404ba37b58SBram Moolenaar if (mfp == NULL) // safety check
241071d4279SBram Moolenaar return;
242071d4279SBram Moolenaar if (mfp->mf_fd >= 0)
243071d4279SBram Moolenaar {
244071d4279SBram Moolenaar if (close(mfp->mf_fd) < 0)
245f9e3e09fSBram Moolenaar emsg(_(e_swapclose));
246071d4279SBram Moolenaar }
247071d4279SBram Moolenaar if (del_file && mfp->mf_fname != NULL)
248071d4279SBram Moolenaar mch_remove(mfp->mf_fname);
2494ba37b58SBram Moolenaar // free entries in used list
250071d4279SBram Moolenaar for (hp = mfp->mf_used_first; hp != NULL; hp = nextp)
251071d4279SBram Moolenaar {
252071d4279SBram Moolenaar total_mem_used -= hp->bh_page_count * mfp->mf_page_size;
253071d4279SBram Moolenaar nextp = hp->bh_next;
254071d4279SBram Moolenaar mf_free_bhdr(hp);
255071d4279SBram Moolenaar }
2564ba37b58SBram Moolenaar while (mfp->mf_free_first != NULL) // free entries in free list
257071d4279SBram Moolenaar vim_free(mf_rem_free(mfp));
258b05b10a3SBram Moolenaar mf_hash_free(&mfp->mf_hash);
2594ba37b58SBram Moolenaar mf_hash_free_all(&mfp->mf_trans); // free hashtable and its items
260071d4279SBram Moolenaar vim_free(mfp->mf_fname);
261071d4279SBram Moolenaar vim_free(mfp->mf_ffname);
262071d4279SBram Moolenaar vim_free(mfp);
263071d4279SBram Moolenaar }
264071d4279SBram Moolenaar
265071d4279SBram Moolenaar /*
266071d4279SBram Moolenaar * Close the swap file for a memfile. Used when 'swapfile' is reset.
267071d4279SBram Moolenaar */
268071d4279SBram Moolenaar void
mf_close_file(buf_T * buf,int getlines)26952ea13daSBram Moolenaar mf_close_file(
27052ea13daSBram Moolenaar buf_T *buf,
2714ba37b58SBram Moolenaar int getlines) // get all lines into memory?
272071d4279SBram Moolenaar {
273071d4279SBram Moolenaar memfile_T *mfp;
274071d4279SBram Moolenaar linenr_T lnum;
275071d4279SBram Moolenaar
276071d4279SBram Moolenaar mfp = buf->b_ml.ml_mfp;
2774ba37b58SBram Moolenaar if (mfp == NULL || mfp->mf_fd < 0) // nothing to close
278071d4279SBram Moolenaar return;
279071d4279SBram Moolenaar
280071d4279SBram Moolenaar if (getlines)
281071d4279SBram Moolenaar {
2824ba37b58SBram Moolenaar // get all blocks in memory by accessing all lines (clumsy!)
28347b8b15bSBram Moolenaar mf_dont_release = TRUE;
284071d4279SBram Moolenaar for (lnum = 1; lnum <= buf->b_ml.ml_line_count; ++lnum)
285071d4279SBram Moolenaar (void)ml_get_buf(buf, lnum, FALSE);
28647b8b15bSBram Moolenaar mf_dont_release = FALSE;
2874ba37b58SBram Moolenaar // TODO: should check if all blocks are really in core
288071d4279SBram Moolenaar }
289071d4279SBram Moolenaar
2904ba37b58SBram Moolenaar if (close(mfp->mf_fd) < 0) // close the file
291f9e3e09fSBram Moolenaar emsg(_(e_swapclose));
292071d4279SBram Moolenaar mfp->mf_fd = -1;
293071d4279SBram Moolenaar
294071d4279SBram Moolenaar if (mfp->mf_fname != NULL)
295071d4279SBram Moolenaar {
2964ba37b58SBram Moolenaar mch_remove(mfp->mf_fname); // delete the swap file
297d23a8236SBram Moolenaar VIM_CLEAR(mfp->mf_fname);
298d23a8236SBram Moolenaar VIM_CLEAR(mfp->mf_ffname);
299071d4279SBram Moolenaar }
300071d4279SBram Moolenaar }
301071d4279SBram Moolenaar
302071d4279SBram Moolenaar /*
303071d4279SBram Moolenaar * Set new size for a memfile. Used when block 0 of a swapfile has been read
304071d4279SBram Moolenaar * and the size it indicates differs from what was guessed.
305071d4279SBram Moolenaar */
306071d4279SBram Moolenaar void
mf_new_page_size(memfile_T * mfp,unsigned new_size)30752ea13daSBram Moolenaar mf_new_page_size(memfile_T *mfp, unsigned new_size)
308071d4279SBram Moolenaar {
3094ba37b58SBram Moolenaar // Correct the memory used for block 0 to the new size, because it will be
3104ba37b58SBram Moolenaar // freed with that size later on.
311071d4279SBram Moolenaar total_mem_used += new_size - mfp->mf_page_size;
312071d4279SBram Moolenaar mfp->mf_page_size = new_size;
313071d4279SBram Moolenaar }
314071d4279SBram Moolenaar
315071d4279SBram Moolenaar /*
316071d4279SBram Moolenaar * get a new block
317071d4279SBram Moolenaar *
318071d4279SBram Moolenaar * negative: TRUE if negative block number desired (data block)
319071d4279SBram Moolenaar */
320071d4279SBram Moolenaar bhdr_T *
mf_new(memfile_T * mfp,int negative,int page_count)32152ea13daSBram Moolenaar mf_new(memfile_T *mfp, int negative, int page_count)
322071d4279SBram Moolenaar {
3234ba37b58SBram Moolenaar bhdr_T *hp; // new bhdr_T
3244ba37b58SBram Moolenaar bhdr_T *freep; // first block in free list
325071d4279SBram Moolenaar char_u *p;
326071d4279SBram Moolenaar
327071d4279SBram Moolenaar /*
328071d4279SBram Moolenaar * If we reached the maximum size for the used memory blocks, release one
329071d4279SBram Moolenaar * If a bhdr_T is returned, use it and adjust the page_count if necessary.
330071d4279SBram Moolenaar */
331071d4279SBram Moolenaar hp = mf_release(mfp, page_count);
332071d4279SBram Moolenaar
333071d4279SBram Moolenaar /*
334071d4279SBram Moolenaar * Decide on the number to use:
335071d4279SBram Moolenaar * If there is a free block, use its number.
336071d4279SBram Moolenaar * Otherwise use mf_block_min for a negative number, mf_block_max for
337071d4279SBram Moolenaar * a positive number.
338071d4279SBram Moolenaar */
339071d4279SBram Moolenaar freep = mfp->mf_free_first;
340071d4279SBram Moolenaar if (!negative && freep != NULL && freep->bh_page_count >= page_count)
341071d4279SBram Moolenaar {
342071d4279SBram Moolenaar /*
343071d4279SBram Moolenaar * If the block in the free list has more pages, take only the number
344071d4279SBram Moolenaar * of pages needed and allocate a new bhdr_T with data
345071d4279SBram Moolenaar *
34617e79197SBram Moolenaar * If the number of pages matches and mf_release() did not return a
34717e79197SBram Moolenaar * bhdr_T, use the bhdr_T from the free list and allocate the data
348071d4279SBram Moolenaar *
34917e79197SBram Moolenaar * If the number of pages matches and mf_release() returned a bhdr_T,
350071d4279SBram Moolenaar * just use the number and free the bhdr_T from the free list
351071d4279SBram Moolenaar */
352071d4279SBram Moolenaar if (freep->bh_page_count > page_count)
353071d4279SBram Moolenaar {
354071d4279SBram Moolenaar if (hp == NULL && (hp = mf_alloc_bhdr(mfp, page_count)) == NULL)
355071d4279SBram Moolenaar return NULL;
356071d4279SBram Moolenaar hp->bh_bnum = freep->bh_bnum;
357071d4279SBram Moolenaar freep->bh_bnum += page_count;
358071d4279SBram Moolenaar freep->bh_page_count -= page_count;
359071d4279SBram Moolenaar }
3604ba37b58SBram Moolenaar else if (hp == NULL) // need to allocate memory for this block
361071d4279SBram Moolenaar {
362c799fe20SBram Moolenaar if ((p = alloc(mfp->mf_page_size * page_count)) == NULL)
363071d4279SBram Moolenaar return NULL;
364071d4279SBram Moolenaar hp = mf_rem_free(mfp);
365071d4279SBram Moolenaar hp->bh_data = p;
366071d4279SBram Moolenaar }
3674ba37b58SBram Moolenaar else // use the number, remove entry from free list
368071d4279SBram Moolenaar {
369071d4279SBram Moolenaar freep = mf_rem_free(mfp);
370071d4279SBram Moolenaar hp->bh_bnum = freep->bh_bnum;
371071d4279SBram Moolenaar vim_free(freep);
372071d4279SBram Moolenaar }
373071d4279SBram Moolenaar }
3744ba37b58SBram Moolenaar else // get a new number
375071d4279SBram Moolenaar {
376071d4279SBram Moolenaar if (hp == NULL && (hp = mf_alloc_bhdr(mfp, page_count)) == NULL)
377071d4279SBram Moolenaar return NULL;
378071d4279SBram Moolenaar if (negative)
379071d4279SBram Moolenaar {
380071d4279SBram Moolenaar hp->bh_bnum = mfp->mf_blocknr_min--;
381071d4279SBram Moolenaar mfp->mf_neg_count++;
382071d4279SBram Moolenaar }
383071d4279SBram Moolenaar else
384071d4279SBram Moolenaar {
385071d4279SBram Moolenaar hp->bh_bnum = mfp->mf_blocknr_max;
386071d4279SBram Moolenaar mfp->mf_blocknr_max += page_count;
387071d4279SBram Moolenaar }
388071d4279SBram Moolenaar }
3894ba37b58SBram Moolenaar hp->bh_flags = BH_LOCKED | BH_DIRTY; // new block is always dirty
390071d4279SBram Moolenaar mfp->mf_dirty = TRUE;
391071d4279SBram Moolenaar hp->bh_page_count = page_count;
392071d4279SBram Moolenaar mf_ins_used(mfp, hp);
393071d4279SBram Moolenaar mf_ins_hash(mfp, hp);
394071d4279SBram Moolenaar
395071d4279SBram Moolenaar /*
396071d4279SBram Moolenaar * Init the data to all zero, to avoid reading uninitialized data.
397071d4279SBram Moolenaar * This also avoids that the passwd file ends up in the swap file!
398071d4279SBram Moolenaar */
399945e2dbbSBram Moolenaar (void)vim_memset((char *)(hp->bh_data), 0,
400945e2dbbSBram Moolenaar (size_t)mfp->mf_page_size * page_count);
401071d4279SBram Moolenaar
402071d4279SBram Moolenaar return hp;
403071d4279SBram Moolenaar }
404071d4279SBram Moolenaar
405071d4279SBram Moolenaar /*
406a8ffcbbfSBram Moolenaar * Get existing block "nr" with "page_count" pages.
407071d4279SBram Moolenaar *
408071d4279SBram Moolenaar * Note: The caller should first check a negative nr with mf_trans_del()
409071d4279SBram Moolenaar */
410071d4279SBram Moolenaar bhdr_T *
mf_get(memfile_T * mfp,blocknr_T nr,int page_count)41152ea13daSBram Moolenaar mf_get(memfile_T *mfp, blocknr_T nr, int page_count)
412071d4279SBram Moolenaar {
413071d4279SBram Moolenaar bhdr_T *hp;
4144ba37b58SBram Moolenaar // doesn't exist
415071d4279SBram Moolenaar if (nr >= mfp->mf_blocknr_max || nr <= mfp->mf_blocknr_min)
416071d4279SBram Moolenaar return NULL;
417071d4279SBram Moolenaar
418071d4279SBram Moolenaar /*
419071d4279SBram Moolenaar * see if it is in the cache
420071d4279SBram Moolenaar */
421071d4279SBram Moolenaar hp = mf_find_hash(mfp, nr);
4224ba37b58SBram Moolenaar if (hp == NULL) // not in the hash list
423071d4279SBram Moolenaar {
4244ba37b58SBram Moolenaar if (nr < 0 || nr >= mfp->mf_infile_count) // can't be in the file
425071d4279SBram Moolenaar return NULL;
426071d4279SBram Moolenaar
4274ba37b58SBram Moolenaar // could check here if the block is in the free list
428071d4279SBram Moolenaar
429071d4279SBram Moolenaar /*
430071d4279SBram Moolenaar * Check if we need to flush an existing block.
431071d4279SBram Moolenaar * If so, use that block.
432071d4279SBram Moolenaar * If not, allocate a new block.
433071d4279SBram Moolenaar */
434071d4279SBram Moolenaar hp = mf_release(mfp, page_count);
435071d4279SBram Moolenaar if (hp == NULL && (hp = mf_alloc_bhdr(mfp, page_count)) == NULL)
436071d4279SBram Moolenaar return NULL;
437071d4279SBram Moolenaar
438071d4279SBram Moolenaar hp->bh_bnum = nr;
439071d4279SBram Moolenaar hp->bh_flags = 0;
440071d4279SBram Moolenaar hp->bh_page_count = page_count;
4414ba37b58SBram Moolenaar if (mf_read(mfp, hp) == FAIL) // cannot read the block!
442071d4279SBram Moolenaar {
443071d4279SBram Moolenaar mf_free_bhdr(hp);
444071d4279SBram Moolenaar return NULL;
445071d4279SBram Moolenaar }
446071d4279SBram Moolenaar }
447071d4279SBram Moolenaar else
448071d4279SBram Moolenaar {
4494ba37b58SBram Moolenaar mf_rem_used(mfp, hp); // remove from list, insert in front below
450071d4279SBram Moolenaar mf_rem_hash(mfp, hp);
451071d4279SBram Moolenaar }
452071d4279SBram Moolenaar
453071d4279SBram Moolenaar hp->bh_flags |= BH_LOCKED;
4544ba37b58SBram Moolenaar mf_ins_used(mfp, hp); // put in front of used list
4554ba37b58SBram Moolenaar mf_ins_hash(mfp, hp); // put in front of hash list
456071d4279SBram Moolenaar
457071d4279SBram Moolenaar return hp;
458071d4279SBram Moolenaar }
459071d4279SBram Moolenaar
460071d4279SBram Moolenaar /*
461071d4279SBram Moolenaar * release the block *hp
462071d4279SBram Moolenaar *
463071d4279SBram Moolenaar * dirty: Block must be written to file later
464071d4279SBram Moolenaar * infile: Block should be in file (needed for recovery)
465071d4279SBram Moolenaar *
466071d4279SBram Moolenaar * no return value, function cannot fail
467071d4279SBram Moolenaar */
468071d4279SBram Moolenaar void
mf_put(memfile_T * mfp,bhdr_T * hp,int dirty,int infile)46952ea13daSBram Moolenaar mf_put(
47052ea13daSBram Moolenaar memfile_T *mfp,
47152ea13daSBram Moolenaar bhdr_T *hp,
47252ea13daSBram Moolenaar int dirty,
47352ea13daSBram Moolenaar int infile)
474071d4279SBram Moolenaar {
475071d4279SBram Moolenaar int flags;
476071d4279SBram Moolenaar
477071d4279SBram Moolenaar flags = hp->bh_flags;
478071d4279SBram Moolenaar
479071d4279SBram Moolenaar if ((flags & BH_LOCKED) == 0)
480f9e3e09fSBram Moolenaar iemsg(_("E293: block was not locked"));
481071d4279SBram Moolenaar flags &= ~BH_LOCKED;
482071d4279SBram Moolenaar if (dirty)
483071d4279SBram Moolenaar {
484071d4279SBram Moolenaar flags |= BH_DIRTY;
485071d4279SBram Moolenaar mfp->mf_dirty = TRUE;
486071d4279SBram Moolenaar }
487071d4279SBram Moolenaar hp->bh_flags = flags;
488071d4279SBram Moolenaar if (infile)
4894ba37b58SBram Moolenaar mf_trans_add(mfp, hp); // may translate negative in positive nr
490071d4279SBram Moolenaar }
491071d4279SBram Moolenaar
492071d4279SBram Moolenaar /*
493071d4279SBram Moolenaar * block *hp is no longer in used, may put it in the free list of memfile *mfp
494071d4279SBram Moolenaar */
495071d4279SBram Moolenaar void
mf_free(memfile_T * mfp,bhdr_T * hp)49652ea13daSBram Moolenaar mf_free(memfile_T *mfp, bhdr_T *hp)
497071d4279SBram Moolenaar {
4984ba37b58SBram Moolenaar vim_free(hp->bh_data); // free the memory
4994ba37b58SBram Moolenaar mf_rem_hash(mfp, hp); // get *hp out of the hash list
5004ba37b58SBram Moolenaar mf_rem_used(mfp, hp); // get *hp out of the used list
501071d4279SBram Moolenaar if (hp->bh_bnum < 0)
502071d4279SBram Moolenaar {
5034ba37b58SBram Moolenaar vim_free(hp); // don't want negative numbers in free list
504071d4279SBram Moolenaar mfp->mf_neg_count--;
505071d4279SBram Moolenaar }
506071d4279SBram Moolenaar else
5074ba37b58SBram Moolenaar mf_ins_free(mfp, hp); // put *hp in the free list
508071d4279SBram Moolenaar }
509071d4279SBram Moolenaar
510071d4279SBram Moolenaar /*
511071d4279SBram Moolenaar * Sync the memory file *mfp to disk.
512071d4279SBram Moolenaar * Flags:
513071d4279SBram Moolenaar * MFS_ALL If not given, blocks with negative numbers are not synced,
514071d4279SBram Moolenaar * even when they are dirty!
515071d4279SBram Moolenaar * MFS_STOP Stop syncing when a character becomes available, but sync at
516071d4279SBram Moolenaar * least one block.
517071d4279SBram Moolenaar * MFS_FLUSH Make sure buffers are flushed to disk, so they will survive a
518071d4279SBram Moolenaar * system crash.
519071d4279SBram Moolenaar * MFS_ZERO Only write block 0.
520071d4279SBram Moolenaar *
521071d4279SBram Moolenaar * Return FAIL for failure, OK otherwise
522071d4279SBram Moolenaar */
523071d4279SBram Moolenaar int
mf_sync(memfile_T * mfp,int flags)52452ea13daSBram Moolenaar mf_sync(memfile_T *mfp, int flags)
525071d4279SBram Moolenaar {
526071d4279SBram Moolenaar int status;
527071d4279SBram Moolenaar bhdr_T *hp;
528071d4279SBram Moolenaar int got_int_save = got_int;
529071d4279SBram Moolenaar
5304ba37b58SBram Moolenaar if (mfp->mf_fd < 0) // there is no file, nothing to do
531071d4279SBram Moolenaar {
532071d4279SBram Moolenaar mfp->mf_dirty = FALSE;
533071d4279SBram Moolenaar return FAIL;
534071d4279SBram Moolenaar }
535071d4279SBram Moolenaar
5364ba37b58SBram Moolenaar // Only a CTRL-C while writing will break us here, not one typed
5374ba37b58SBram Moolenaar // previously.
538071d4279SBram Moolenaar got_int = FALSE;
539071d4279SBram Moolenaar
540071d4279SBram Moolenaar /*
541071d4279SBram Moolenaar * sync from last to first (may reduce the probability of an inconsistent
542071d4279SBram Moolenaar * file) If a write fails, it is very likely caused by a full filesystem.
543071d4279SBram Moolenaar * Then we only try to write blocks within the existing file. If that also
544071d4279SBram Moolenaar * fails then we give up.
545071d4279SBram Moolenaar */
546071d4279SBram Moolenaar status = OK;
547071d4279SBram Moolenaar for (hp = mfp->mf_used_last; hp != NULL; hp = hp->bh_prev)
548071d4279SBram Moolenaar if (((flags & MFS_ALL) || hp->bh_bnum >= 0)
549071d4279SBram Moolenaar && (hp->bh_flags & BH_DIRTY)
550071d4279SBram Moolenaar && (status == OK || (hp->bh_bnum >= 0
551071d4279SBram Moolenaar && hp->bh_bnum < mfp->mf_infile_count)))
552071d4279SBram Moolenaar {
553071d4279SBram Moolenaar if ((flags & MFS_ZERO) && hp->bh_bnum != 0)
554071d4279SBram Moolenaar continue;
555071d4279SBram Moolenaar if (mf_write(mfp, hp) == FAIL)
556071d4279SBram Moolenaar {
5574ba37b58SBram Moolenaar if (status == FAIL) // double error: quit syncing
558071d4279SBram Moolenaar break;
559071d4279SBram Moolenaar status = FAIL;
560071d4279SBram Moolenaar }
561071d4279SBram Moolenaar if (flags & MFS_STOP)
562071d4279SBram Moolenaar {
5634ba37b58SBram Moolenaar // Stop when char available now.
564071d4279SBram Moolenaar if (ui_char_avail())
565071d4279SBram Moolenaar break;
566071d4279SBram Moolenaar }
567071d4279SBram Moolenaar else
568071d4279SBram Moolenaar ui_breakcheck();
569071d4279SBram Moolenaar if (got_int)
570071d4279SBram Moolenaar break;
571071d4279SBram Moolenaar }
572071d4279SBram Moolenaar
573071d4279SBram Moolenaar /*
574071d4279SBram Moolenaar * If the whole list is flushed, the memfile is not dirty anymore.
575071d4279SBram Moolenaar * In case of an error this flag is also set, to avoid trying all the time.
576071d4279SBram Moolenaar */
577071d4279SBram Moolenaar if (hp == NULL || status == FAIL)
578071d4279SBram Moolenaar mfp->mf_dirty = FALSE;
579071d4279SBram Moolenaar
580071d4279SBram Moolenaar if ((flags & MFS_FLUSH) && *p_sws != NUL)
581071d4279SBram Moolenaar {
582071d4279SBram Moolenaar #if defined(UNIX)
583071d4279SBram Moolenaar # ifdef HAVE_FSYNC
584071d4279SBram Moolenaar /*
585071d4279SBram Moolenaar * most Unixes have the very useful fsync() function, just what we need.
586071d4279SBram Moolenaar */
587071d4279SBram Moolenaar if (STRCMP(p_sws, "fsync") == 0)
588071d4279SBram Moolenaar {
589a7870195SBram Moolenaar if (vim_fsync(mfp->mf_fd))
590071d4279SBram Moolenaar status = FAIL;
591071d4279SBram Moolenaar }
592071d4279SBram Moolenaar else
593071d4279SBram Moolenaar # endif
5944ba37b58SBram Moolenaar // OpenNT is strictly POSIX (Benzinger)
5954ba37b58SBram Moolenaar // Tandem/Himalaya NSK-OSS doesn't have sync()
5964ba37b58SBram Moolenaar // No sync() on Stratus VOS
597ba17ed67SBram Moolenaar # if defined(__OPENNT) || defined(__TANDEM) || defined(__VOS__)
598071d4279SBram Moolenaar fflush(NULL);
599071d4279SBram Moolenaar # else
600071d4279SBram Moolenaar sync();
601071d4279SBram Moolenaar # endif
602071d4279SBram Moolenaar #endif
603071d4279SBram Moolenaar #ifdef VMS
604071d4279SBram Moolenaar if (STRCMP(p_sws, "fsync") == 0)
605071d4279SBram Moolenaar {
606a7870195SBram Moolenaar if (vim_fsync(mfp->mf_fd))
607071d4279SBram Moolenaar status = FAIL;
608071d4279SBram Moolenaar }
609071d4279SBram Moolenaar #endif
6104f97475dSBram Moolenaar #ifdef MSWIN
6110bd40517SBram Moolenaar if (_commit(mfp->mf_fd))
6120bd40517SBram Moolenaar status = FAIL;
613071d4279SBram Moolenaar #endif
614071d4279SBram Moolenaar #ifdef AMIGA
6155a6404cfSBram Moolenaar # if defined(__AROS__) || defined(__amigaos4__)
616a7870195SBram Moolenaar if (vim_fsync(mfp->mf_fd) != 0)
617071d4279SBram Moolenaar status = FAIL;
618071d4279SBram Moolenaar # else
619071d4279SBram Moolenaar /*
620071d4279SBram Moolenaar * Flush() only exists for AmigaDos 2.0.
621071d4279SBram Moolenaar * For 1.3 it should be done with close() + open(), but then the risk
622071d4279SBram Moolenaar * is that the open() may fail and lose the file....
623071d4279SBram Moolenaar */
624071d4279SBram Moolenaar # ifdef FEAT_ARP
625071d4279SBram Moolenaar if (dos2)
626071d4279SBram Moolenaar # endif
627071d4279SBram Moolenaar # ifdef SASC
628071d4279SBram Moolenaar {
629071d4279SBram Moolenaar struct UFB *fp = chkufb(mfp->mf_fd);
630071d4279SBram Moolenaar
631071d4279SBram Moolenaar if (fp != NULL)
632071d4279SBram Moolenaar Flush(fp->ufbfh);
633071d4279SBram Moolenaar }
634071d4279SBram Moolenaar # else
635071d4279SBram Moolenaar # if defined(_DCC) || defined(__GNUC__) || defined(__MORPHOS__)
636071d4279SBram Moolenaar {
63789f3727bSBram Moolenaar # if defined(__GNUC__) && !defined(__MORPHOS__) && defined(__libnix__)
6384ba37b58SBram Moolenaar // Have function (in libnix at least),
6394ba37b58SBram Moolenaar // but ain't got no prototype anywhere.
640071d4279SBram Moolenaar extern unsigned long fdtofh(int filedescriptor);
641071d4279SBram Moolenaar # endif
64289f3727bSBram Moolenaar # if !defined(__libnix__)
64389f3727bSBram Moolenaar fflush(NULL);
64489f3727bSBram Moolenaar # else
645071d4279SBram Moolenaar BPTR fh = (BPTR)fdtofh(mfp->mf_fd);
646071d4279SBram Moolenaar
647071d4279SBram Moolenaar if (fh != 0)
648071d4279SBram Moolenaar Flush(fh);
64989f3727bSBram Moolenaar # endif
650071d4279SBram Moolenaar }
6514ba37b58SBram Moolenaar # else // assume Manx
652071d4279SBram Moolenaar Flush(_devtab[mfp->mf_fd].fd);
653071d4279SBram Moolenaar # endif
654071d4279SBram Moolenaar # endif
655071d4279SBram Moolenaar # endif
6564ba37b58SBram Moolenaar #endif // AMIGA
657071d4279SBram Moolenaar }
658071d4279SBram Moolenaar
659071d4279SBram Moolenaar got_int |= got_int_save;
660071d4279SBram Moolenaar
661071d4279SBram Moolenaar return status;
662071d4279SBram Moolenaar }
663071d4279SBram Moolenaar
664071d4279SBram Moolenaar /*
665c32840f2SBram Moolenaar * For all blocks in memory file *mfp that have a positive block number set
666c32840f2SBram Moolenaar * the dirty flag. These are blocks that need to be written to a newly
667c32840f2SBram Moolenaar * created swapfile.
668c32840f2SBram Moolenaar */
669c32840f2SBram Moolenaar void
mf_set_dirty(memfile_T * mfp)67052ea13daSBram Moolenaar mf_set_dirty(memfile_T *mfp)
671c32840f2SBram Moolenaar {
672c32840f2SBram Moolenaar bhdr_T *hp;
673c32840f2SBram Moolenaar
674c32840f2SBram Moolenaar for (hp = mfp->mf_used_last; hp != NULL; hp = hp->bh_prev)
675c32840f2SBram Moolenaar if (hp->bh_bnum > 0)
676c32840f2SBram Moolenaar hp->bh_flags |= BH_DIRTY;
677c32840f2SBram Moolenaar mfp->mf_dirty = TRUE;
678c32840f2SBram Moolenaar }
679c32840f2SBram Moolenaar
680c32840f2SBram Moolenaar /*
681071d4279SBram Moolenaar * insert block *hp in front of hashlist of memfile *mfp
682071d4279SBram Moolenaar */
683071d4279SBram Moolenaar static void
mf_ins_hash(memfile_T * mfp,bhdr_T * hp)68452ea13daSBram Moolenaar mf_ins_hash(memfile_T *mfp, bhdr_T *hp)
685071d4279SBram Moolenaar {
686b05b10a3SBram Moolenaar mf_hash_add_item(&mfp->mf_hash, (mf_hashitem_T *)hp);
687071d4279SBram Moolenaar }
688071d4279SBram Moolenaar
689071d4279SBram Moolenaar /*
690071d4279SBram Moolenaar * remove block *hp from hashlist of memfile list *mfp
691071d4279SBram Moolenaar */
692071d4279SBram Moolenaar static void
mf_rem_hash(memfile_T * mfp,bhdr_T * hp)69352ea13daSBram Moolenaar mf_rem_hash(memfile_T *mfp, bhdr_T *hp)
694071d4279SBram Moolenaar {
695b05b10a3SBram Moolenaar mf_hash_rem_item(&mfp->mf_hash, (mf_hashitem_T *)hp);
696071d4279SBram Moolenaar }
697071d4279SBram Moolenaar
698071d4279SBram Moolenaar /*
699071d4279SBram Moolenaar * look in hash lists of memfile *mfp for block header with number 'nr'
700071d4279SBram Moolenaar */
701071d4279SBram Moolenaar static bhdr_T *
mf_find_hash(memfile_T * mfp,blocknr_T nr)70252ea13daSBram Moolenaar mf_find_hash(memfile_T *mfp, blocknr_T nr)
703071d4279SBram Moolenaar {
704b05b10a3SBram Moolenaar return (bhdr_T *)mf_hash_find(&mfp->mf_hash, nr);
705071d4279SBram Moolenaar }
706071d4279SBram Moolenaar
707071d4279SBram Moolenaar /*
708071d4279SBram Moolenaar * insert block *hp in front of used list of memfile *mfp
709071d4279SBram Moolenaar */
710071d4279SBram Moolenaar static void
mf_ins_used(memfile_T * mfp,bhdr_T * hp)71152ea13daSBram Moolenaar mf_ins_used(memfile_T *mfp, bhdr_T *hp)
712071d4279SBram Moolenaar {
713071d4279SBram Moolenaar hp->bh_next = mfp->mf_used_first;
714071d4279SBram Moolenaar mfp->mf_used_first = hp;
715071d4279SBram Moolenaar hp->bh_prev = NULL;
7164ba37b58SBram Moolenaar if (hp->bh_next == NULL) // list was empty, adjust last pointer
717071d4279SBram Moolenaar mfp->mf_used_last = hp;
718071d4279SBram Moolenaar else
719071d4279SBram Moolenaar hp->bh_next->bh_prev = hp;
720071d4279SBram Moolenaar mfp->mf_used_count += hp->bh_page_count;
721071d4279SBram Moolenaar total_mem_used += hp->bh_page_count * mfp->mf_page_size;
722071d4279SBram Moolenaar }
723071d4279SBram Moolenaar
724071d4279SBram Moolenaar /*
725071d4279SBram Moolenaar * remove block *hp from used list of memfile *mfp
726071d4279SBram Moolenaar */
727071d4279SBram Moolenaar static void
mf_rem_used(memfile_T * mfp,bhdr_T * hp)72852ea13daSBram Moolenaar mf_rem_used(memfile_T *mfp, bhdr_T *hp)
729071d4279SBram Moolenaar {
7304ba37b58SBram Moolenaar if (hp->bh_next == NULL) // last block in used list
731071d4279SBram Moolenaar mfp->mf_used_last = hp->bh_prev;
732071d4279SBram Moolenaar else
733071d4279SBram Moolenaar hp->bh_next->bh_prev = hp->bh_prev;
7344ba37b58SBram Moolenaar if (hp->bh_prev == NULL) // first block in used list
735071d4279SBram Moolenaar mfp->mf_used_first = hp->bh_next;
736071d4279SBram Moolenaar else
737071d4279SBram Moolenaar hp->bh_prev->bh_next = hp->bh_next;
738071d4279SBram Moolenaar mfp->mf_used_count -= hp->bh_page_count;
739071d4279SBram Moolenaar total_mem_used -= hp->bh_page_count * mfp->mf_page_size;
740071d4279SBram Moolenaar }
741071d4279SBram Moolenaar
742071d4279SBram Moolenaar /*
743071d4279SBram Moolenaar * Release the least recently used block from the used list if the number
744071d4279SBram Moolenaar * of used memory blocks gets to big.
745071d4279SBram Moolenaar *
746071d4279SBram Moolenaar * Return the block header to the caller, including the memory block, so
747071d4279SBram Moolenaar * it can be re-used. Make sure the page_count is right.
748bc56336bSBram Moolenaar *
749bc56336bSBram Moolenaar * Returns NULL if no block is released.
750071d4279SBram Moolenaar */
751071d4279SBram Moolenaar static bhdr_T *
mf_release(memfile_T * mfp,int page_count)75252ea13daSBram Moolenaar mf_release(memfile_T *mfp, int page_count)
753071d4279SBram Moolenaar {
754071d4279SBram Moolenaar bhdr_T *hp;
755071d4279SBram Moolenaar int need_release;
756071d4279SBram Moolenaar buf_T *buf;
757071d4279SBram Moolenaar
7584ba37b58SBram Moolenaar // don't release while in mf_close_file()
75947b8b15bSBram Moolenaar if (mf_dont_release)
760071d4279SBram Moolenaar return NULL;
761071d4279SBram Moolenaar
762071d4279SBram Moolenaar /*
763071d4279SBram Moolenaar * Need to release a block if the number of blocks for this memfile is
764071d4279SBram Moolenaar * higher than the maximum or total memory used is over 'maxmemtot'
765071d4279SBram Moolenaar */
766071d4279SBram Moolenaar need_release = ((mfp->mf_used_count >= mfp->mf_used_count_max)
767071d4279SBram Moolenaar || (total_mem_used >> 10) >= (long_u)p_mmt);
768071d4279SBram Moolenaar
769071d4279SBram Moolenaar /*
770071d4279SBram Moolenaar * Try to create a swap file if the amount of memory used is getting too
771071d4279SBram Moolenaar * high.
772071d4279SBram Moolenaar */
773071d4279SBram Moolenaar if (mfp->mf_fd < 0 && need_release && p_uc)
774071d4279SBram Moolenaar {
7754ba37b58SBram Moolenaar // find for which buffer this memfile is
77629323590SBram Moolenaar FOR_ALL_BUFFERS(buf)
777071d4279SBram Moolenaar if (buf->b_ml.ml_mfp == mfp)
778071d4279SBram Moolenaar break;
779071d4279SBram Moolenaar if (buf != NULL && buf->b_may_swap)
780071d4279SBram Moolenaar ml_open_file(buf);
781071d4279SBram Moolenaar }
782071d4279SBram Moolenaar
783071d4279SBram Moolenaar /*
784071d4279SBram Moolenaar * don't release a block if
785071d4279SBram Moolenaar * there is no file for this memfile
786071d4279SBram Moolenaar * or
787071d4279SBram Moolenaar * the number of blocks for this memfile is lower than the maximum
788071d4279SBram Moolenaar * and
789071d4279SBram Moolenaar * total memory used is not up to 'maxmemtot'
790071d4279SBram Moolenaar */
791071d4279SBram Moolenaar if (mfp->mf_fd < 0 || !need_release)
792071d4279SBram Moolenaar return NULL;
793071d4279SBram Moolenaar
794071d4279SBram Moolenaar for (hp = mfp->mf_used_last; hp != NULL; hp = hp->bh_prev)
795071d4279SBram Moolenaar if (!(hp->bh_flags & BH_LOCKED))
796071d4279SBram Moolenaar break;
7974ba37b58SBram Moolenaar if (hp == NULL) // not a single one that can be released
798071d4279SBram Moolenaar return NULL;
799071d4279SBram Moolenaar
800071d4279SBram Moolenaar /*
801071d4279SBram Moolenaar * If the block is dirty, write it.
802071d4279SBram Moolenaar * If the write fails we don't free it.
803071d4279SBram Moolenaar */
804071d4279SBram Moolenaar if ((hp->bh_flags & BH_DIRTY) && mf_write(mfp, hp) == FAIL)
805071d4279SBram Moolenaar return NULL;
806071d4279SBram Moolenaar
807071d4279SBram Moolenaar mf_rem_used(mfp, hp);
808071d4279SBram Moolenaar mf_rem_hash(mfp, hp);
809071d4279SBram Moolenaar
810071d4279SBram Moolenaar /*
811071d4279SBram Moolenaar * If a bhdr_T is returned, make sure that the page_count of bh_data is
812071d4279SBram Moolenaar * right
813071d4279SBram Moolenaar */
814071d4279SBram Moolenaar if (hp->bh_page_count != page_count)
815071d4279SBram Moolenaar {
816071d4279SBram Moolenaar vim_free(hp->bh_data);
817071d4279SBram Moolenaar if ((hp->bh_data = alloc(mfp->mf_page_size * page_count)) == NULL)
818071d4279SBram Moolenaar {
819071d4279SBram Moolenaar vim_free(hp);
820071d4279SBram Moolenaar return NULL;
821071d4279SBram Moolenaar }
822071d4279SBram Moolenaar hp->bh_page_count = page_count;
823071d4279SBram Moolenaar }
824071d4279SBram Moolenaar return hp;
825071d4279SBram Moolenaar }
826071d4279SBram Moolenaar
827071d4279SBram Moolenaar /*
828071d4279SBram Moolenaar * release as many blocks as possible
829071d4279SBram Moolenaar * Used in case of out of memory
830071d4279SBram Moolenaar *
831071d4279SBram Moolenaar * return TRUE if any memory was released
832071d4279SBram Moolenaar */
833071d4279SBram Moolenaar int
mf_release_all(void)83452ea13daSBram Moolenaar mf_release_all(void)
835071d4279SBram Moolenaar {
836071d4279SBram Moolenaar buf_T *buf;
837071d4279SBram Moolenaar memfile_T *mfp;
838071d4279SBram Moolenaar bhdr_T *hp;
839071d4279SBram Moolenaar int retval = FALSE;
840071d4279SBram Moolenaar
84129323590SBram Moolenaar FOR_ALL_BUFFERS(buf)
842071d4279SBram Moolenaar {
843071d4279SBram Moolenaar mfp = buf->b_ml.ml_mfp;
844071d4279SBram Moolenaar if (mfp != NULL)
845071d4279SBram Moolenaar {
8464ba37b58SBram Moolenaar // If no swap file yet, may open one
847071d4279SBram Moolenaar if (mfp->mf_fd < 0 && buf->b_may_swap)
848071d4279SBram Moolenaar ml_open_file(buf);
849071d4279SBram Moolenaar
8504ba37b58SBram Moolenaar // only if there is a swapfile
851071d4279SBram Moolenaar if (mfp->mf_fd >= 0)
852071d4279SBram Moolenaar {
853071d4279SBram Moolenaar for (hp = mfp->mf_used_last; hp != NULL; )
854071d4279SBram Moolenaar {
855071d4279SBram Moolenaar if (!(hp->bh_flags & BH_LOCKED)
856071d4279SBram Moolenaar && (!(hp->bh_flags & BH_DIRTY)
857071d4279SBram Moolenaar || mf_write(mfp, hp) != FAIL))
858071d4279SBram Moolenaar {
859071d4279SBram Moolenaar mf_rem_used(mfp, hp);
860071d4279SBram Moolenaar mf_rem_hash(mfp, hp);
861071d4279SBram Moolenaar mf_free_bhdr(hp);
8624ba37b58SBram Moolenaar hp = mfp->mf_used_last; // re-start, list was changed
863071d4279SBram Moolenaar retval = TRUE;
864071d4279SBram Moolenaar }
865071d4279SBram Moolenaar else
866071d4279SBram Moolenaar hp = hp->bh_prev;
867071d4279SBram Moolenaar }
868071d4279SBram Moolenaar }
869071d4279SBram Moolenaar }
870071d4279SBram Moolenaar }
871071d4279SBram Moolenaar return retval;
872071d4279SBram Moolenaar }
873071d4279SBram Moolenaar
874071d4279SBram Moolenaar /*
875071d4279SBram Moolenaar * Allocate a block header and a block of memory for it
876071d4279SBram Moolenaar */
877071d4279SBram Moolenaar static bhdr_T *
mf_alloc_bhdr(memfile_T * mfp,int page_count)87852ea13daSBram Moolenaar mf_alloc_bhdr(memfile_T *mfp, int page_count)
879071d4279SBram Moolenaar {
880071d4279SBram Moolenaar bhdr_T *hp;
881071d4279SBram Moolenaar
882c799fe20SBram Moolenaar if ((hp = ALLOC_ONE(bhdr_T)) != NULL)
883071d4279SBram Moolenaar {
884c799fe20SBram Moolenaar if ((hp->bh_data = alloc(mfp->mf_page_size * page_count)) == NULL)
885071d4279SBram Moolenaar {
8864ba37b58SBram Moolenaar vim_free(hp); // not enough memory
887071d4279SBram Moolenaar return NULL;
888071d4279SBram Moolenaar }
889071d4279SBram Moolenaar hp->bh_page_count = page_count;
890071d4279SBram Moolenaar }
891071d4279SBram Moolenaar return hp;
892071d4279SBram Moolenaar }
893071d4279SBram Moolenaar
894071d4279SBram Moolenaar /*
895071d4279SBram Moolenaar * Free a block header and the block of memory for it
896071d4279SBram Moolenaar */
897071d4279SBram Moolenaar static void
mf_free_bhdr(bhdr_T * hp)89852ea13daSBram Moolenaar mf_free_bhdr(bhdr_T *hp)
899071d4279SBram Moolenaar {
900071d4279SBram Moolenaar vim_free(hp->bh_data);
901071d4279SBram Moolenaar vim_free(hp);
902071d4279SBram Moolenaar }
903071d4279SBram Moolenaar
904071d4279SBram Moolenaar /*
905071d4279SBram Moolenaar * insert entry *hp in the free list
906071d4279SBram Moolenaar */
907071d4279SBram Moolenaar static void
mf_ins_free(memfile_T * mfp,bhdr_T * hp)90852ea13daSBram Moolenaar mf_ins_free(memfile_T *mfp, bhdr_T *hp)
909071d4279SBram Moolenaar {
910071d4279SBram Moolenaar hp->bh_next = mfp->mf_free_first;
911071d4279SBram Moolenaar mfp->mf_free_first = hp;
912071d4279SBram Moolenaar }
913071d4279SBram Moolenaar
914071d4279SBram Moolenaar /*
915071d4279SBram Moolenaar * remove the first entry from the free list and return a pointer to it
916071d4279SBram Moolenaar * Note: caller must check that mfp->mf_free_first is not NULL!
917071d4279SBram Moolenaar */
918071d4279SBram Moolenaar static bhdr_T *
mf_rem_free(memfile_T * mfp)91952ea13daSBram Moolenaar mf_rem_free(memfile_T *mfp)
920071d4279SBram Moolenaar {
921071d4279SBram Moolenaar bhdr_T *hp;
922071d4279SBram Moolenaar
923071d4279SBram Moolenaar hp = mfp->mf_free_first;
924071d4279SBram Moolenaar mfp->mf_free_first = hp->bh_next;
925071d4279SBram Moolenaar return hp;
926071d4279SBram Moolenaar }
927071d4279SBram Moolenaar
928071d4279SBram Moolenaar /*
929071d4279SBram Moolenaar * read a block from disk
930071d4279SBram Moolenaar *
931071d4279SBram Moolenaar * Return FAIL for failure, OK otherwise
932071d4279SBram Moolenaar */
933071d4279SBram Moolenaar static int
mf_read(memfile_T * mfp,bhdr_T * hp)93452ea13daSBram Moolenaar mf_read(memfile_T *mfp, bhdr_T *hp)
935071d4279SBram Moolenaar {
9368767f52fSBram Moolenaar off_T offset;
937071d4279SBram Moolenaar unsigned page_size;
938071d4279SBram Moolenaar unsigned size;
939071d4279SBram Moolenaar
9404ba37b58SBram Moolenaar if (mfp->mf_fd < 0) // there is no file, can't read
941071d4279SBram Moolenaar return FAIL;
942071d4279SBram Moolenaar
943071d4279SBram Moolenaar page_size = mfp->mf_page_size;
9448767f52fSBram Moolenaar offset = (off_T)page_size * hp->bh_bnum;
945071d4279SBram Moolenaar size = page_size * hp->bh_page_count;
9468767f52fSBram Moolenaar if (vim_lseek(mfp->mf_fd, offset, SEEK_SET) != offset)
947071d4279SBram Moolenaar {
9483f2d9814SBram Moolenaar PERROR(_("E294: Seek error in swap file read"));
949071d4279SBram Moolenaar return FAIL;
950071d4279SBram Moolenaar }
951540fc6fbSBram Moolenaar if ((unsigned)read_eintr(mfp->mf_fd, hp->bh_data, size) != size)
952071d4279SBram Moolenaar {
9533f2d9814SBram Moolenaar PERROR(_("E295: Read error in swap file"));
954071d4279SBram Moolenaar return FAIL;
955071d4279SBram Moolenaar }
956a8ffcbbfSBram Moolenaar
957a8ffcbbfSBram Moolenaar #ifdef FEAT_CRYPT
9584ba37b58SBram Moolenaar // Decrypt if 'key' is set and this is a data block. And when changing the
9594ba37b58SBram Moolenaar // key.
9604a8c2cfcSBram Moolenaar if (*mfp->mf_buffer->b_p_key != NUL || mfp->mf_old_key != NULL)
961a8ffcbbfSBram Moolenaar ml_decrypt_data(mfp, hp->bh_data, offset, size);
962a8ffcbbfSBram Moolenaar #endif
963a8ffcbbfSBram Moolenaar
964071d4279SBram Moolenaar return OK;
965071d4279SBram Moolenaar }
966071d4279SBram Moolenaar
967071d4279SBram Moolenaar /*
968071d4279SBram Moolenaar * write a block to disk
969071d4279SBram Moolenaar *
970071d4279SBram Moolenaar * Return FAIL for failure, OK otherwise
971071d4279SBram Moolenaar */
972071d4279SBram Moolenaar static int
mf_write(memfile_T * mfp,bhdr_T * hp)97352ea13daSBram Moolenaar mf_write(memfile_T *mfp, bhdr_T *hp)
974071d4279SBram Moolenaar {
9754ba37b58SBram Moolenaar off_T offset; // offset in the file
9764ba37b58SBram Moolenaar blocknr_T nr; // block nr which is being written
977071d4279SBram Moolenaar bhdr_T *hp2;
9784ba37b58SBram Moolenaar unsigned page_size; // number of bytes in a page
9794ba37b58SBram Moolenaar unsigned page_count; // number of pages written
9804ba37b58SBram Moolenaar unsigned size; // number of bytes written
981071d4279SBram Moolenaar
982b58a4b93SBram Moolenaar if (mfp->mf_fd < 0 && !mfp->mf_reopen)
983b58a4b93SBram Moolenaar // there is no file and there was no file, can't write
984071d4279SBram Moolenaar return FAIL;
985071d4279SBram Moolenaar
9864ba37b58SBram Moolenaar if (hp->bh_bnum < 0) // must assign file block number
987071d4279SBram Moolenaar if (mf_trans_add(mfp, hp) == FAIL)
988071d4279SBram Moolenaar return FAIL;
989071d4279SBram Moolenaar
990071d4279SBram Moolenaar page_size = mfp->mf_page_size;
991071d4279SBram Moolenaar
992071d4279SBram Moolenaar /*
993071d4279SBram Moolenaar * We don't want gaps in the file. Write the blocks in front of *hp
994071d4279SBram Moolenaar * to extend the file.
995071d4279SBram Moolenaar * If block 'mf_infile_count' is not in the hash list, it has been
996071d4279SBram Moolenaar * freed. Fill the space in the file with data from the current block.
997071d4279SBram Moolenaar */
998071d4279SBram Moolenaar for (;;)
999071d4279SBram Moolenaar {
1000b58a4b93SBram Moolenaar int attempt;
1001b58a4b93SBram Moolenaar
1002071d4279SBram Moolenaar nr = hp->bh_bnum;
10034ba37b58SBram Moolenaar if (nr > mfp->mf_infile_count) // beyond end of file
1004071d4279SBram Moolenaar {
1005071d4279SBram Moolenaar nr = mfp->mf_infile_count;
10064ba37b58SBram Moolenaar hp2 = mf_find_hash(mfp, nr); // NULL caught below
1007071d4279SBram Moolenaar }
1008071d4279SBram Moolenaar else
1009071d4279SBram Moolenaar hp2 = hp;
1010071d4279SBram Moolenaar
10118767f52fSBram Moolenaar offset = (off_T)page_size * nr;
10124ba37b58SBram Moolenaar if (hp2 == NULL) // freed block, fill with dummy data
1013071d4279SBram Moolenaar page_count = 1;
1014071d4279SBram Moolenaar else
1015071d4279SBram Moolenaar page_count = hp2->bh_page_count;
1016071d4279SBram Moolenaar size = page_size * page_count;
1017b58a4b93SBram Moolenaar
1018b58a4b93SBram Moolenaar for (attempt = 1; attempt <= 2; ++attempt)
1019071d4279SBram Moolenaar {
1020b58a4b93SBram Moolenaar if (mfp->mf_fd >= 0)
1021b58a4b93SBram Moolenaar {
1022b58a4b93SBram Moolenaar if (vim_lseek(mfp->mf_fd, offset, SEEK_SET) != offset)
1023b58a4b93SBram Moolenaar {
1024b58a4b93SBram Moolenaar PERROR(_("E296: Seek error in swap file write"));
1025b58a4b93SBram Moolenaar return FAIL;
1026b58a4b93SBram Moolenaar }
1027b58a4b93SBram Moolenaar if (mf_write_block(mfp,
1028b58a4b93SBram Moolenaar hp2 == NULL ? hp : hp2, offset, size) == OK)
1029b58a4b93SBram Moolenaar break;
1030b58a4b93SBram Moolenaar }
1031b58a4b93SBram Moolenaar
1032b58a4b93SBram Moolenaar if (attempt == 1)
1033b58a4b93SBram Moolenaar {
1034b58a4b93SBram Moolenaar // If the swap file is on a network drive, and the network
1035b58a4b93SBram Moolenaar // gets disconnected and then re-connected, we can maybe fix it
1036b58a4b93SBram Moolenaar // by closing and then re-opening the file.
1037b58a4b93SBram Moolenaar if (mfp->mf_fd >= 0)
1038b58a4b93SBram Moolenaar close(mfp->mf_fd);
1039b58a4b93SBram Moolenaar mfp->mf_fd = mch_open_rw((char *)mfp->mf_fname, mfp->mf_flags);
1040b58a4b93SBram Moolenaar mfp->mf_reopen = (mfp->mf_fd < 0);
1041b58a4b93SBram Moolenaar }
1042b58a4b93SBram Moolenaar if (attempt == 2 || mfp->mf_fd < 0)
1043b58a4b93SBram Moolenaar {
1044b58a4b93SBram Moolenaar // Avoid repeating the error message, this mostly happens when
1045b58a4b93SBram Moolenaar // the disk is full. We give the message again only after a
1046b58a4b93SBram Moolenaar // successful write or when hitting a key. We keep on trying,
1047b58a4b93SBram Moolenaar // in case some space becomes available.
1048071d4279SBram Moolenaar if (!did_swapwrite_msg)
1049f9e3e09fSBram Moolenaar emsg(_("E297: Write error in swap file"));
1050071d4279SBram Moolenaar did_swapwrite_msg = TRUE;
1051071d4279SBram Moolenaar return FAIL;
1052071d4279SBram Moolenaar }
1053b58a4b93SBram Moolenaar }
1054b58a4b93SBram Moolenaar
1055071d4279SBram Moolenaar did_swapwrite_msg = FALSE;
10564ba37b58SBram Moolenaar if (hp2 != NULL) // written a non-dummy block
1057071d4279SBram Moolenaar hp2->bh_flags &= ~BH_DIRTY;
10584ba37b58SBram Moolenaar // appended to the file
1059071d4279SBram Moolenaar if (nr + (blocknr_T)page_count > mfp->mf_infile_count)
1060071d4279SBram Moolenaar mfp->mf_infile_count = nr + page_count;
10614ba37b58SBram Moolenaar if (nr == hp->bh_bnum) // written the desired block
1062071d4279SBram Moolenaar break;
1063071d4279SBram Moolenaar }
1064071d4279SBram Moolenaar return OK;
1065071d4279SBram Moolenaar }
1066071d4279SBram Moolenaar
1067071d4279SBram Moolenaar /*
1068a8ffcbbfSBram Moolenaar * Write block "hp" with data size "size" to file "mfp->mf_fd".
1069a8ffcbbfSBram Moolenaar * Takes care of encryption.
1070a8ffcbbfSBram Moolenaar * Return FAIL or OK.
1071a8ffcbbfSBram Moolenaar */
1072a8ffcbbfSBram Moolenaar static int
mf_write_block(memfile_T * mfp,bhdr_T * hp,off_T offset UNUSED,unsigned size)107352ea13daSBram Moolenaar mf_write_block(
107452ea13daSBram Moolenaar memfile_T *mfp,
107552ea13daSBram Moolenaar bhdr_T *hp,
10768767f52fSBram Moolenaar off_T offset UNUSED,
107752ea13daSBram Moolenaar unsigned size)
1078a8ffcbbfSBram Moolenaar {
1079a8ffcbbfSBram Moolenaar char_u *data = hp->bh_data;
1080a8ffcbbfSBram Moolenaar int result = OK;
1081a8ffcbbfSBram Moolenaar
1082a8ffcbbfSBram Moolenaar #ifdef FEAT_CRYPT
10834ba37b58SBram Moolenaar // Encrypt if 'key' is set and this is a data block.
1084a8ffcbbfSBram Moolenaar if (*mfp->mf_buffer->b_p_key != NUL)
1085a8ffcbbfSBram Moolenaar {
1086a8ffcbbfSBram Moolenaar data = ml_encrypt_data(mfp, data, offset, size);
1087a8ffcbbfSBram Moolenaar if (data == NULL)
1088a8ffcbbfSBram Moolenaar return FAIL;
1089a8ffcbbfSBram Moolenaar }
1090a8ffcbbfSBram Moolenaar #endif
1091a8ffcbbfSBram Moolenaar
1092540fc6fbSBram Moolenaar if ((unsigned)write_eintr(mfp->mf_fd, data, size) != size)
1093a8ffcbbfSBram Moolenaar result = FAIL;
1094a8ffcbbfSBram Moolenaar
1095a8ffcbbfSBram Moolenaar #ifdef FEAT_CRYPT
1096a8ffcbbfSBram Moolenaar if (data != hp->bh_data)
1097a8ffcbbfSBram Moolenaar vim_free(data);
1098a8ffcbbfSBram Moolenaar #endif
1099a8ffcbbfSBram Moolenaar
1100a8ffcbbfSBram Moolenaar return result;
1101a8ffcbbfSBram Moolenaar }
1102a8ffcbbfSBram Moolenaar
1103a8ffcbbfSBram Moolenaar /*
1104071d4279SBram Moolenaar * Make block number for *hp positive and add it to the translation list
1105071d4279SBram Moolenaar *
1106071d4279SBram Moolenaar * Return FAIL for failure, OK otherwise
1107071d4279SBram Moolenaar */
1108071d4279SBram Moolenaar static int
mf_trans_add(memfile_T * mfp,bhdr_T * hp)110952ea13daSBram Moolenaar mf_trans_add(memfile_T *mfp, bhdr_T *hp)
1110071d4279SBram Moolenaar {
1111071d4279SBram Moolenaar bhdr_T *freep;
1112071d4279SBram Moolenaar blocknr_T new_bnum;
1113071d4279SBram Moolenaar NR_TRANS *np;
1114071d4279SBram Moolenaar int page_count;
1115071d4279SBram Moolenaar
11164ba37b58SBram Moolenaar if (hp->bh_bnum >= 0) // it's already positive
1117071d4279SBram Moolenaar return OK;
1118071d4279SBram Moolenaar
1119c799fe20SBram Moolenaar if ((np = ALLOC_ONE(NR_TRANS)) == NULL)
1120071d4279SBram Moolenaar return FAIL;
1121071d4279SBram Moolenaar
1122071d4279SBram Moolenaar /*
1123a8ffcbbfSBram Moolenaar * Get a new number for the block.
1124071d4279SBram Moolenaar * If the first item in the free list has sufficient pages, use its number
1125071d4279SBram Moolenaar * Otherwise use mf_blocknr_max.
1126071d4279SBram Moolenaar */
1127071d4279SBram Moolenaar freep = mfp->mf_free_first;
1128071d4279SBram Moolenaar page_count = hp->bh_page_count;
1129071d4279SBram Moolenaar if (freep != NULL && freep->bh_page_count >= page_count)
1130071d4279SBram Moolenaar {
1131071d4279SBram Moolenaar new_bnum = freep->bh_bnum;
1132071d4279SBram Moolenaar /*
113384a05accSBram Moolenaar * If the page count of the free block was larger, reduce it.
1134071d4279SBram Moolenaar * If the page count matches, remove the block from the free list
1135071d4279SBram Moolenaar */
1136071d4279SBram Moolenaar if (freep->bh_page_count > page_count)
1137071d4279SBram Moolenaar {
1138071d4279SBram Moolenaar freep->bh_bnum += page_count;
1139071d4279SBram Moolenaar freep->bh_page_count -= page_count;
1140071d4279SBram Moolenaar }
1141071d4279SBram Moolenaar else
1142071d4279SBram Moolenaar {
1143071d4279SBram Moolenaar freep = mf_rem_free(mfp);
1144071d4279SBram Moolenaar vim_free(freep);
1145071d4279SBram Moolenaar }
1146071d4279SBram Moolenaar }
1147071d4279SBram Moolenaar else
1148071d4279SBram Moolenaar {
1149071d4279SBram Moolenaar new_bnum = mfp->mf_blocknr_max;
1150071d4279SBram Moolenaar mfp->mf_blocknr_max += page_count;
1151071d4279SBram Moolenaar }
1152071d4279SBram Moolenaar
11534ba37b58SBram Moolenaar np->nt_old_bnum = hp->bh_bnum; // adjust number
1154071d4279SBram Moolenaar np->nt_new_bnum = new_bnum;
1155071d4279SBram Moolenaar
11564ba37b58SBram Moolenaar mf_rem_hash(mfp, hp); // remove from old hash list
1157071d4279SBram Moolenaar hp->bh_bnum = new_bnum;
11584ba37b58SBram Moolenaar mf_ins_hash(mfp, hp); // insert in new hash list
1159071d4279SBram Moolenaar
11604ba37b58SBram Moolenaar // Insert "np" into "mf_trans" hashtable with key "np->nt_old_bnum"
1161b05b10a3SBram Moolenaar mf_hash_add_item(&mfp->mf_trans, (mf_hashitem_T *)np);
1162071d4279SBram Moolenaar
1163071d4279SBram Moolenaar return OK;
1164071d4279SBram Moolenaar }
1165071d4279SBram Moolenaar
1166071d4279SBram Moolenaar /*
1167bc56336bSBram Moolenaar * Lookup a translation from the trans lists and delete the entry.
1168071d4279SBram Moolenaar *
1169071d4279SBram Moolenaar * Return the positive new number when found, the old number when not found
1170071d4279SBram Moolenaar */
1171071d4279SBram Moolenaar blocknr_T
mf_trans_del(memfile_T * mfp,blocknr_T old_nr)117252ea13daSBram Moolenaar mf_trans_del(memfile_T *mfp, blocknr_T old_nr)
1173071d4279SBram Moolenaar {
1174071d4279SBram Moolenaar NR_TRANS *np;
1175071d4279SBram Moolenaar blocknr_T new_bnum;
1176071d4279SBram Moolenaar
1177b05b10a3SBram Moolenaar np = (NR_TRANS *)mf_hash_find(&mfp->mf_trans, old_nr);
1178b05b10a3SBram Moolenaar
11794ba37b58SBram Moolenaar if (np == NULL) // not found
1180071d4279SBram Moolenaar return old_nr;
1181071d4279SBram Moolenaar
1182071d4279SBram Moolenaar mfp->mf_neg_count--;
1183071d4279SBram Moolenaar new_bnum = np->nt_new_bnum;
1184b05b10a3SBram Moolenaar
11854ba37b58SBram Moolenaar // remove entry from the trans list
1186b05b10a3SBram Moolenaar mf_hash_rem_item(&mfp->mf_trans, (mf_hashitem_T *)np);
1187b05b10a3SBram Moolenaar
1188071d4279SBram Moolenaar vim_free(np);
1189071d4279SBram Moolenaar
1190071d4279SBram Moolenaar return new_bnum;
1191071d4279SBram Moolenaar }
1192071d4279SBram Moolenaar
1193071d4279SBram Moolenaar /*
1194071d4279SBram Moolenaar * Set mfp->mf_ffname according to mfp->mf_fname and some other things.
1195071d4279SBram Moolenaar * Only called when creating or renaming the swapfile. Either way it's a new
1196071d4279SBram Moolenaar * name so we must work out the full path name.
1197071d4279SBram Moolenaar */
1198071d4279SBram Moolenaar void
mf_set_ffname(memfile_T * mfp)119952ea13daSBram Moolenaar mf_set_ffname(memfile_T *mfp)
1200071d4279SBram Moolenaar {
1201071d4279SBram Moolenaar mfp->mf_ffname = FullName_save(mfp->mf_fname, FALSE);
1202071d4279SBram Moolenaar }
1203071d4279SBram Moolenaar
1204071d4279SBram Moolenaar /*
1205071d4279SBram Moolenaar * Make the name of the file used for the memfile a full path.
1206071d4279SBram Moolenaar * Used before doing a :cd
1207071d4279SBram Moolenaar */
1208071d4279SBram Moolenaar void
mf_fullname(memfile_T * mfp)120952ea13daSBram Moolenaar mf_fullname(memfile_T *mfp)
1210071d4279SBram Moolenaar {
1211071d4279SBram Moolenaar if (mfp != NULL && mfp->mf_fname != NULL && mfp->mf_ffname != NULL)
1212071d4279SBram Moolenaar {
1213071d4279SBram Moolenaar vim_free(mfp->mf_fname);
1214071d4279SBram Moolenaar mfp->mf_fname = mfp->mf_ffname;
1215071d4279SBram Moolenaar mfp->mf_ffname = NULL;
1216071d4279SBram Moolenaar }
1217071d4279SBram Moolenaar }
1218071d4279SBram Moolenaar
1219071d4279SBram Moolenaar /*
1220071d4279SBram Moolenaar * return TRUE if there are any translations pending for 'mfp'
1221071d4279SBram Moolenaar */
1222071d4279SBram Moolenaar int
mf_need_trans(memfile_T * mfp)122352ea13daSBram Moolenaar mf_need_trans(memfile_T *mfp)
1224071d4279SBram Moolenaar {
1225071d4279SBram Moolenaar return (mfp->mf_fname != NULL && mfp->mf_neg_count > 0);
1226071d4279SBram Moolenaar }
1227071d4279SBram Moolenaar
1228071d4279SBram Moolenaar /*
1229071d4279SBram Moolenaar * Open a swap file for a memfile.
1230071d4279SBram Moolenaar * The "fname" must be in allocated memory, and is consumed (also when an
1231071d4279SBram Moolenaar * error occurs).
1232071d4279SBram Moolenaar */
1233071d4279SBram Moolenaar static void
mf_do_open(memfile_T * mfp,char_u * fname,int flags)123452ea13daSBram Moolenaar mf_do_open(
123552ea13daSBram Moolenaar memfile_T *mfp,
123652ea13daSBram Moolenaar char_u *fname,
12374ba37b58SBram Moolenaar int flags) // flags for open()
1238071d4279SBram Moolenaar {
1239071d4279SBram Moolenaar #ifdef HAVE_LSTAT
12408767f52fSBram Moolenaar stat_T sb;
1241071d4279SBram Moolenaar #endif
1242071d4279SBram Moolenaar
1243071d4279SBram Moolenaar mfp->mf_fname = fname;
1244071d4279SBram Moolenaar
1245071d4279SBram Moolenaar /*
1246071d4279SBram Moolenaar * Get the full path name before the open, because this is
1247071d4279SBram Moolenaar * not possible after the open on the Amiga.
1248071d4279SBram Moolenaar * fname cannot be NameBuff, because it must have been allocated.
1249071d4279SBram Moolenaar */
1250071d4279SBram Moolenaar mf_set_ffname(mfp);
125148e330afSBram Moolenaar #if defined(MSWIN)
1252071d4279SBram Moolenaar /*
125384a05accSBram Moolenaar * A ":!cd e:xxx" may change the directory without us knowing, use the
1254071d4279SBram Moolenaar * full pathname always. Careful: This frees fname!
1255071d4279SBram Moolenaar */
1256071d4279SBram Moolenaar mf_fullname(mfp);
1257071d4279SBram Moolenaar #endif
1258071d4279SBram Moolenaar
1259071d4279SBram Moolenaar #ifdef HAVE_LSTAT
1260071d4279SBram Moolenaar /*
1261071d4279SBram Moolenaar * Extra security check: When creating a swap file it really shouldn't
1262071d4279SBram Moolenaar * exist yet. If there is a symbolic link, this is most likely an attack.
1263071d4279SBram Moolenaar */
1264071d4279SBram Moolenaar if ((flags & O_CREAT) && mch_lstat((char *)mfp->mf_fname, &sb) >= 0)
1265071d4279SBram Moolenaar {
1266071d4279SBram Moolenaar mfp->mf_fd = -1;
1267f9e3e09fSBram Moolenaar emsg(_("E300: Swap file already exists (symlink attack?)"));
1268071d4279SBram Moolenaar }
1269071d4279SBram Moolenaar else
1270071d4279SBram Moolenaar #endif
1271071d4279SBram Moolenaar {
1272071d4279SBram Moolenaar /*
1273071d4279SBram Moolenaar * try to open the file
1274071d4279SBram Moolenaar */
1275a5792f58SBram Moolenaar flags |= O_EXTRA | O_NOFOLLOW;
12764f97475dSBram Moolenaar #ifdef MSWIN
12774ba37b58SBram Moolenaar // Prevent handle inheritance that cause problems with Cscope
12784ba37b58SBram Moolenaar // (swap file may not be deleted if cscope connection was open after
12794ba37b58SBram Moolenaar // the file)
1280071d4279SBram Moolenaar flags |= O_NOINHERIT;
1281071d4279SBram Moolenaar #endif
1282b58a4b93SBram Moolenaar mfp->mf_flags = flags;
1283071d4279SBram Moolenaar mfp->mf_fd = mch_open_rw((char *)mfp->mf_fname, flags);
1284071d4279SBram Moolenaar }
1285071d4279SBram Moolenaar
1286071d4279SBram Moolenaar /*
1287071d4279SBram Moolenaar * If the file cannot be opened, use memory only
1288071d4279SBram Moolenaar */
1289071d4279SBram Moolenaar if (mfp->mf_fd < 0)
1290071d4279SBram Moolenaar {
1291d23a8236SBram Moolenaar VIM_CLEAR(mfp->mf_fname);
1292d23a8236SBram Moolenaar VIM_CLEAR(mfp->mf_ffname);
1293071d4279SBram Moolenaar }
1294071d4279SBram Moolenaar else
1295588ebeb7SBram Moolenaar {
1296f05da219SBram Moolenaar #ifdef HAVE_FD_CLOEXEC
1297f05da219SBram Moolenaar int fdflags = fcntl(mfp->mf_fd, F_GETFD);
1298f05da219SBram Moolenaar if (fdflags >= 0 && (fdflags & FD_CLOEXEC) == 0)
1299fbc4b4dbSBram Moolenaar (void)fcntl(mfp->mf_fd, F_SETFD, fdflags | FD_CLOEXEC);
1300f05da219SBram Moolenaar #endif
13015bd32f47SBram Moolenaar #if defined(HAVE_SELINUX) || defined(HAVE_SMACK)
1302588ebeb7SBram Moolenaar mch_copy_sec(fname, mfp->mf_fname);
1303588ebeb7SBram Moolenaar #endif
13044ba37b58SBram Moolenaar mch_hide(mfp->mf_fname); // try setting the 'hidden' flag
1305071d4279SBram Moolenaar }
1306588ebeb7SBram Moolenaar }
1307b05b10a3SBram Moolenaar
1308b05b10a3SBram Moolenaar /*
1309b05b10a3SBram Moolenaar * Implementation of mf_hashtab_T follows.
1310b05b10a3SBram Moolenaar */
1311b05b10a3SBram Moolenaar
1312b05b10a3SBram Moolenaar /*
1313b05b10a3SBram Moolenaar * The number of buckets in the hashtable is increased by a factor of
1314b05b10a3SBram Moolenaar * MHT_GROWTH_FACTOR when the average number of items per bucket
1315b05b10a3SBram Moolenaar * exceeds 2 ^ MHT_LOG_LOAD_FACTOR.
1316b05b10a3SBram Moolenaar */
1317b05b10a3SBram Moolenaar #define MHT_LOG_LOAD_FACTOR 6
13184ba37b58SBram Moolenaar #define MHT_GROWTH_FACTOR 2 // must be a power of two
1319b05b10a3SBram Moolenaar
1320b05b10a3SBram Moolenaar /*
1321b05b10a3SBram Moolenaar * Initialize an empty hash table.
1322b05b10a3SBram Moolenaar */
1323b05b10a3SBram Moolenaar static void
mf_hash_init(mf_hashtab_T * mht)132452ea13daSBram Moolenaar mf_hash_init(mf_hashtab_T *mht)
1325b05b10a3SBram Moolenaar {
1326*a80faa89SBram Moolenaar CLEAR_POINTER(mht);
1327b05b10a3SBram Moolenaar mht->mht_buckets = mht->mht_small_buckets;
1328b05b10a3SBram Moolenaar mht->mht_mask = MHT_INIT_SIZE - 1;
1329b05b10a3SBram Moolenaar }
1330b05b10a3SBram Moolenaar
1331b05b10a3SBram Moolenaar /*
1332b05b10a3SBram Moolenaar * Free the array of a hash table. Does not free the items it contains!
1333b05b10a3SBram Moolenaar * The hash table must not be used again without another mf_hash_init() call.
1334b05b10a3SBram Moolenaar */
1335b05b10a3SBram Moolenaar static void
mf_hash_free(mf_hashtab_T * mht)133652ea13daSBram Moolenaar mf_hash_free(mf_hashtab_T *mht)
1337b05b10a3SBram Moolenaar {
1338b05b10a3SBram Moolenaar if (mht->mht_buckets != mht->mht_small_buckets)
1339b05b10a3SBram Moolenaar vim_free(mht->mht_buckets);
1340b05b10a3SBram Moolenaar }
1341b05b10a3SBram Moolenaar
1342b05b10a3SBram Moolenaar /*
1343b05b10a3SBram Moolenaar * Free the array of a hash table and all the items it contains.
1344b05b10a3SBram Moolenaar */
1345b05b10a3SBram Moolenaar static void
mf_hash_free_all(mf_hashtab_T * mht)134652ea13daSBram Moolenaar mf_hash_free_all(mf_hashtab_T *mht)
1347b05b10a3SBram Moolenaar {
1348b05b10a3SBram Moolenaar long_u idx;
1349b05b10a3SBram Moolenaar mf_hashitem_T *mhi;
1350b05b10a3SBram Moolenaar mf_hashitem_T *next;
1351b05b10a3SBram Moolenaar
1352b05b10a3SBram Moolenaar for (idx = 0; idx <= mht->mht_mask; idx++)
1353b05b10a3SBram Moolenaar for (mhi = mht->mht_buckets[idx]; mhi != NULL; mhi = next)
1354b05b10a3SBram Moolenaar {
1355b05b10a3SBram Moolenaar next = mhi->mhi_next;
1356b05b10a3SBram Moolenaar vim_free(mhi);
1357b05b10a3SBram Moolenaar }
1358b05b10a3SBram Moolenaar
1359b05b10a3SBram Moolenaar mf_hash_free(mht);
1360b05b10a3SBram Moolenaar }
1361b05b10a3SBram Moolenaar
1362b05b10a3SBram Moolenaar /*
1363b05b10a3SBram Moolenaar * Find "key" in hashtable "mht".
1364b05b10a3SBram Moolenaar * Returns a pointer to a mf_hashitem_T or NULL if the item was not found.
1365b05b10a3SBram Moolenaar */
1366b05b10a3SBram Moolenaar static mf_hashitem_T *
mf_hash_find(mf_hashtab_T * mht,blocknr_T key)136752ea13daSBram Moolenaar mf_hash_find(mf_hashtab_T *mht, blocknr_T key)
1368b05b10a3SBram Moolenaar {
1369b05b10a3SBram Moolenaar mf_hashitem_T *mhi;
1370b05b10a3SBram Moolenaar
1371b05b10a3SBram Moolenaar mhi = mht->mht_buckets[key & mht->mht_mask];
1372b05b10a3SBram Moolenaar while (mhi != NULL && mhi->mhi_key != key)
1373b05b10a3SBram Moolenaar mhi = mhi->mhi_next;
1374b05b10a3SBram Moolenaar
1375b05b10a3SBram Moolenaar return mhi;
1376b05b10a3SBram Moolenaar }
1377b05b10a3SBram Moolenaar
1378b05b10a3SBram Moolenaar /*
1379b05b10a3SBram Moolenaar * Add item "mhi" to hashtable "mht".
1380b05b10a3SBram Moolenaar * "mhi" must not be NULL.
1381b05b10a3SBram Moolenaar */
1382b05b10a3SBram Moolenaar static void
mf_hash_add_item(mf_hashtab_T * mht,mf_hashitem_T * mhi)138352ea13daSBram Moolenaar mf_hash_add_item(mf_hashtab_T *mht, mf_hashitem_T *mhi)
1384b05b10a3SBram Moolenaar {
1385b05b10a3SBram Moolenaar long_u idx;
1386b05b10a3SBram Moolenaar
1387b05b10a3SBram Moolenaar idx = mhi->mhi_key & mht->mht_mask;
1388b05b10a3SBram Moolenaar mhi->mhi_next = mht->mht_buckets[idx];
1389b05b10a3SBram Moolenaar mhi->mhi_prev = NULL;
1390b05b10a3SBram Moolenaar if (mhi->mhi_next != NULL)
1391b05b10a3SBram Moolenaar mhi->mhi_next->mhi_prev = mhi;
1392b05b10a3SBram Moolenaar mht->mht_buckets[idx] = mhi;
1393b05b10a3SBram Moolenaar
1394b05b10a3SBram Moolenaar mht->mht_count++;
1395b05b10a3SBram Moolenaar
1396b05b10a3SBram Moolenaar /*
1397b05b10a3SBram Moolenaar * Grow hashtable when we have more thank 2^MHT_LOG_LOAD_FACTOR
1398b05b10a3SBram Moolenaar * items per bucket on average
1399b05b10a3SBram Moolenaar */
1400b05b10a3SBram Moolenaar if (mht->mht_fixed == 0
1401b05b10a3SBram Moolenaar && (mht->mht_count >> MHT_LOG_LOAD_FACTOR) > mht->mht_mask)
1402b05b10a3SBram Moolenaar {
1403b05b10a3SBram Moolenaar if (mf_hash_grow(mht) == FAIL)
1404b05b10a3SBram Moolenaar {
14054ba37b58SBram Moolenaar // stop trying to grow after first failure to allocate memory
1406b05b10a3SBram Moolenaar mht->mht_fixed = 1;
1407b05b10a3SBram Moolenaar }
1408b05b10a3SBram Moolenaar }
1409b05b10a3SBram Moolenaar }
1410b05b10a3SBram Moolenaar
1411b05b10a3SBram Moolenaar /*
1412b05b10a3SBram Moolenaar * Remove item "mhi" from hashtable "mht".
1413b05b10a3SBram Moolenaar * "mhi" must not be NULL and must have been inserted into "mht".
1414b05b10a3SBram Moolenaar */
1415b05b10a3SBram Moolenaar static void
mf_hash_rem_item(mf_hashtab_T * mht,mf_hashitem_T * mhi)141652ea13daSBram Moolenaar mf_hash_rem_item(mf_hashtab_T *mht, mf_hashitem_T *mhi)
1417b05b10a3SBram Moolenaar {
1418b05b10a3SBram Moolenaar if (mhi->mhi_prev == NULL)
1419b05b10a3SBram Moolenaar mht->mht_buckets[mhi->mhi_key & mht->mht_mask] = mhi->mhi_next;
1420b05b10a3SBram Moolenaar else
1421b05b10a3SBram Moolenaar mhi->mhi_prev->mhi_next = mhi->mhi_next;
1422b05b10a3SBram Moolenaar
1423b05b10a3SBram Moolenaar if (mhi->mhi_next != NULL)
1424b05b10a3SBram Moolenaar mhi->mhi_next->mhi_prev = mhi->mhi_prev;
1425b05b10a3SBram Moolenaar
1426b05b10a3SBram Moolenaar mht->mht_count--;
1427b05b10a3SBram Moolenaar
14284ba37b58SBram Moolenaar // We could shrink the table here, but it typically takes little memory,
14294ba37b58SBram Moolenaar // so why bother?
1430b05b10a3SBram Moolenaar }
1431b05b10a3SBram Moolenaar
1432b05b10a3SBram Moolenaar /*
1433b05b10a3SBram Moolenaar * Increase number of buckets in the hashtable by MHT_GROWTH_FACTOR and
1434b05b10a3SBram Moolenaar * rehash items.
1435b05b10a3SBram Moolenaar * Returns FAIL when out of memory.
1436b05b10a3SBram Moolenaar */
1437b05b10a3SBram Moolenaar static int
mf_hash_grow(mf_hashtab_T * mht)143852ea13daSBram Moolenaar mf_hash_grow(mf_hashtab_T *mht)
1439b05b10a3SBram Moolenaar {
1440b05b10a3SBram Moolenaar long_u i, j;
1441b05b10a3SBram Moolenaar int shift;
1442b05b10a3SBram Moolenaar mf_hashitem_T *mhi;
1443b05b10a3SBram Moolenaar mf_hashitem_T *tails[MHT_GROWTH_FACTOR];
1444b05b10a3SBram Moolenaar mf_hashitem_T **buckets;
1445b05b10a3SBram Moolenaar size_t size;
1446b05b10a3SBram Moolenaar
1447b05b10a3SBram Moolenaar size = (mht->mht_mask + 1) * MHT_GROWTH_FACTOR * sizeof(void *);
1448c799fe20SBram Moolenaar buckets = lalloc_clear(size, FALSE);
1449b05b10a3SBram Moolenaar if (buckets == NULL)
1450b05b10a3SBram Moolenaar return FAIL;
1451b05b10a3SBram Moolenaar
1452b05b10a3SBram Moolenaar shift = 0;
1453b05b10a3SBram Moolenaar while ((mht->mht_mask >> shift) != 0)
1454b05b10a3SBram Moolenaar shift++;
1455b05b10a3SBram Moolenaar
1456b05b10a3SBram Moolenaar for (i = 0; i <= mht->mht_mask; i++)
1457b05b10a3SBram Moolenaar {
1458b05b10a3SBram Moolenaar /*
1459b05b10a3SBram Moolenaar * Traverse the items in the i-th original bucket and move them into
1460b05b10a3SBram Moolenaar * MHT_GROWTH_FACTOR new buckets, preserving their relative order
1461b05b10a3SBram Moolenaar * within each new bucket. Preserving the order is important because
1462b05b10a3SBram Moolenaar * mf_get() tries to keep most recently used items at the front of
1463b05b10a3SBram Moolenaar * each bucket.
1464b05b10a3SBram Moolenaar *
1465b05b10a3SBram Moolenaar * Here we strongly rely on the fact the hashes are computed modulo
1466b05b10a3SBram Moolenaar * a power of two.
1467b05b10a3SBram Moolenaar */
1468b05b10a3SBram Moolenaar
1469*a80faa89SBram Moolenaar CLEAR_FIELD(tails);
1470b05b10a3SBram Moolenaar
1471b05b10a3SBram Moolenaar for (mhi = mht->mht_buckets[i]; mhi != NULL; mhi = mhi->mhi_next)
1472b05b10a3SBram Moolenaar {
1473b05b10a3SBram Moolenaar j = (mhi->mhi_key >> shift) & (MHT_GROWTH_FACTOR - 1);
1474b05b10a3SBram Moolenaar if (tails[j] == NULL)
1475b05b10a3SBram Moolenaar {
1476b05b10a3SBram Moolenaar buckets[i + (j << shift)] = mhi;
1477b05b10a3SBram Moolenaar tails[j] = mhi;
1478b05b10a3SBram Moolenaar mhi->mhi_prev = NULL;
1479b05b10a3SBram Moolenaar }
1480b05b10a3SBram Moolenaar else
1481b05b10a3SBram Moolenaar {
1482b05b10a3SBram Moolenaar tails[j]->mhi_next = mhi;
1483b05b10a3SBram Moolenaar mhi->mhi_prev = tails[j];
1484b05b10a3SBram Moolenaar tails[j] = mhi;
1485b05b10a3SBram Moolenaar }
1486b05b10a3SBram Moolenaar }
1487b05b10a3SBram Moolenaar
1488b05b10a3SBram Moolenaar for (j = 0; j < MHT_GROWTH_FACTOR; j++)
1489b05b10a3SBram Moolenaar if (tails[j] != NULL)
1490b05b10a3SBram Moolenaar tails[j]->mhi_next = NULL;
1491b05b10a3SBram Moolenaar }
1492b05b10a3SBram Moolenaar
1493b05b10a3SBram Moolenaar if (mht->mht_buckets != mht->mht_small_buckets)
1494b05b10a3SBram Moolenaar vim_free(mht->mht_buckets);
1495b05b10a3SBram Moolenaar
1496b05b10a3SBram Moolenaar mht->mht_buckets = buckets;
1497b05b10a3SBram Moolenaar mht->mht_mask = (mht->mht_mask + 1) * MHT_GROWTH_FACTOR - 1;
1498b05b10a3SBram Moolenaar
1499b05b10a3SBram Moolenaar return OK;
1500b05b10a3SBram Moolenaar }
1501