summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2010-06-21 06:15:46 +0200
committerBram Moolenaar <Bram@vim.org>2010-06-21 06:15:46 +0200
commita8ffcbbf5d6070380e41b3d0841c3944396a27c0 (patch)
treeb8608922fc9956be229912f1323b0d0a94de526c /src
parent191e0a2bc7cb4787e19aa1f8c6958b47e05d7882 (diff)
downloadvim-git-a8ffcbbf5d6070380e41b3d0841c3944396a27c0.tar.gz
Crypt the swapfile.
Diffstat (limited to 'src')
-rw-r--r--src/blowfish.c42
-rw-r--r--src/fileio.c43
-rw-r--r--src/globals.h4
-rw-r--r--src/main.c2
-rw-r--r--src/memfile.c54
-rw-r--r--src/memline.c599
-rw-r--r--src/misc2.c55
-rw-r--r--src/option.c17
-rw-r--r--src/proto/blowfish.pro2
-rw-r--r--src/proto/memline.pro5
-rw-r--r--src/proto/misc2.pro2
-rw-r--r--src/sha256.c8
-rw-r--r--src/structs.h17
-rw-r--r--src/testdir/test72.in2
-rw-r--r--src/undo.c22
-rw-r--r--src/workshop.c4
16 files changed, 752 insertions, 126 deletions
diff --git a/src/blowfish.c b/src/blowfish.c
index f0b97b7b9..c8e68d224 100644
--- a/src/blowfish.c
+++ b/src/blowfish.c
@@ -436,13 +436,7 @@ bf_key_init(password, salt, salt_len)
key[i] = j;
}
- for (i = 0; i < 256; ++i)
- {
- sbx[0][i] = sbi[0][i];
- sbx[1][i] = sbi[1][i];
- sbx[2][i] = sbi[2][i];
- sbx[3][i] = sbi[3][i];
- }
+ mch_memmove(sbx, sbi, 4 * 4 * 256);
for (i = 0; i < 18; ++i)
{
@@ -655,6 +649,40 @@ bf_crypt_init_keys(passwd)
}
}
+static int save_randbyte_offset;
+static int save_update_offset;
+static char_u save_ofb_buffer[BF_OFB_LEN];
+static UINT32_T save_pax[18];
+static UINT32_T save_sbx[4][256];
+
+/*
+ * Save the current crypt state. Can only be used once before
+ * bf_crypt_restore().
+ */
+ void
+bf_crypt_save()
+{
+ save_randbyte_offset = randbyte_offset;
+ save_update_offset = update_offset;
+ mch_memmove(save_ofb_buffer, ofb_buffer, BF_OFB_LEN);
+ mch_memmove(save_pax, pax, 4 * 18);
+ mch_memmove(save_sbx, sbx, 4 * 4 * 256);
+}
+
+/*
+ * Restore the current crypt state. Can only be used after
+ * bf_crypt_save().
+ */
+ void
+bf_crypt_restore()
+{
+ randbyte_offset = save_randbyte_offset;
+ update_offset = save_update_offset;
+ mch_memmove(ofb_buffer, save_ofb_buffer, BF_OFB_LEN);
+ mch_memmove(pax, save_pax, 4 * 18);
+ mch_memmove(sbx, save_sbx, 4 * 4 * 256);
+}
+
/*
* Run a test to check if the encryption works as expected.
* Give an error and return FAIL when not.
diff --git a/src/fileio.c b/src/fileio.c
index aad76170e..b09946167 100644
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -64,7 +64,7 @@ static void check_marks_read __ARGS((void));
#endif
#ifdef FEAT_CRYPT
static int get_crypt_method __ARGS((char *ptr, int len));
-static char_u *check_for_cryptkey __ARGS((char_u *cryptkey, char_u *ptr, long *sizep, off_t *filesizep, int newfile, int *did_ask));
+static char_u *check_for_cryptkey __ARGS((char_u *cryptkey, char_u *ptr, long *sizep, off_t *filesizep, int newfile, char_u *fname, int *did_ask));
#endif
#ifdef UNIX
static void set_file_time __ARGS((char_u *fname, time_t atime, time_t mtime));
@@ -995,6 +995,13 @@ retry:
#endif
}
+#ifdef FEAT_CRYPT
+ if (cryptkey != NULL)
+ /* Need to reset the state, but keep the key, don't want to ask for it
+ * again. */
+ crypt_pop_state();
+#endif
+
/*
* When retrying with another "fenc" and the first time "fileformat"
* will be reset.
@@ -1426,7 +1433,8 @@ retry:
*/
if (filesize == 0)
cryptkey = check_for_cryptkey(cryptkey, ptr, &size,
- &filesize, newfile, &did_ask_for_key);
+ &filesize, newfile, sfname,
+ &did_ask_for_key);
/*
* Decrypt the read bytes.
*/
@@ -2277,8 +2285,14 @@ failed:
save_file_ff(curbuf); /* remember the current file format */
#ifdef FEAT_CRYPT
- if (cryptkey != curbuf->b_p_key)
- free_crypt_key(cryptkey);
+ if (cryptkey != NULL)
+ {
+ crypt_pop_state();
+ if (cryptkey != curbuf->b_p_key)
+ free_crypt_key(cryptkey);
+ /* don't set cryptkey to NULL, it's used below as a flag that
+ * encryption was used */
+ }
#endif
#ifdef FEAT_MBYTE
@@ -2869,12 +2883,13 @@ get_crypt_method(ptr, len)
* Return the (new) encryption key, NULL for no encryption.
*/
static char_u *
-check_for_cryptkey(cryptkey, ptr, sizep, filesizep, newfile, did_ask)
+check_for_cryptkey(cryptkey, ptr, sizep, filesizep, newfile, fname, did_ask)
char_u *cryptkey; /* previous encryption key or NULL */
char_u *ptr; /* pointer to read bytes */
long *sizep; /* length of read bytes */
off_t *filesizep; /* nr of bytes used from file */
int newfile; /* editing a new buffer */
+ char_u *fname; /* file name to display */
int *did_ask; /* flag: whether already asked for key */
{
int method = get_crypt_method((char *)ptr, *sizep);
@@ -2882,7 +2897,6 @@ check_for_cryptkey(cryptkey, ptr, sizep, filesizep, newfile, did_ask)
if (method >= 0)
{
curbuf->b_p_cm = method;
- use_crypt_method = method;
if (method > 0)
(void)blowfish_self_test();
if (cryptkey == NULL && !*did_ask)
@@ -2895,6 +2909,8 @@ check_for_cryptkey(cryptkey, ptr, sizep, filesizep, newfile, did_ask)
* option and don't free it. bf needs hash of the key saved.
* Don't ask for the key again when first time Enter was hit.
* Happens when retrying to detect encoding. */
+ smsg((char_u *)_(need_key_msg), fname);
+ msg_scroll = TRUE;
cryptkey = get_crypt_key(newfile, FALSE);
*did_ask = TRUE;
@@ -2913,6 +2929,8 @@ check_for_cryptkey(cryptkey, ptr, sizep, filesizep, newfile, did_ask)
int seed_len = crypt_seed_len[method];
int salt_len = crypt_salt_len[method];
+ crypt_push_state();
+ use_crypt_method = method;
if (method == 0)
crypt_init_keys(cryptkey);
else
@@ -2924,7 +2942,8 @@ check_for_cryptkey(cryptkey, ptr, sizep, filesizep, newfile, did_ask)
/* Remove magic number from the text */
*filesizep += CRYPT_MAGIC_LEN + salt_len + seed_len;
*sizep -= CRYPT_MAGIC_LEN + salt_len + seed_len;
- mch_memmove(ptr, ptr + CRYPT_MAGIC_LEN + salt_len + seed_len, (size_t)*sizep);
+ mch_memmove(ptr, ptr + CRYPT_MAGIC_LEN + salt_len + seed_len,
+ (size_t)*sizep);
}
}
/* When starting to edit a new file which does not have encryption, clear
@@ -2956,6 +2975,7 @@ prepare_crypt_read(fp)
if (method < 0 || method != curbuf->b_p_cm)
return FAIL;
+ crypt_push_state();
if (method == 0)
crypt_init_keys(curbuf->b_p_key);
else
@@ -2974,6 +2994,8 @@ prepare_crypt_read(fp)
/*
* Prepare for writing encrypted bytes for buffer "buf".
* Returns a pointer to an allocated header of length "*lenp".
+ * When out of memory returns NULL.
+ * Otherwise calls crypt_push_state(), call crypt_pop_state() later.
*/
char_u *
prepare_crypt_write(buf, lenp)
@@ -2990,6 +3012,7 @@ prepare_crypt_write(buf, lenp)
+ CRYPT_SEED_LEN_MAX + 2);
if (header != NULL)
{
+ crypt_push_state();
use_crypt_method = buf->b_p_cm; /* select pkzip or blowfish */
vim_strncpy(header, (char_u *)crypt_magic[use_crypt_method],
CRYPT_MAGIC_LEN);
@@ -4404,7 +4427,7 @@ restore_backup:
write_info.bw_fd = fd;
#ifdef FEAT_CRYPT
- if (*buf->b_p_key && !filtering)
+ if (*buf->b_p_key != NUL && !filtering)
{
char_u *header;
int header_len;
@@ -4674,6 +4697,10 @@ restore_backup:
if (!backup_copy)
mch_set_acl(wfname, acl);
#endif
+#ifdef FEAT_CRYPT
+ if (wb_flags & FIO_ENCRYPTED)
+ crypt_pop_state();
+#endif
#if defined(FEAT_MBYTE) && defined(FEAT_EVAL)
diff --git a/src/globals.h b/src/globals.h
index f4ec25713..c9bfc1fe8 100644
--- a/src/globals.h
+++ b/src/globals.h
@@ -1564,6 +1564,10 @@ EXTERN short disallow_gui INIT(= FALSE);
EXTERN char top_bot_msg[] INIT(= N_("search hit TOP, continuing at BOTTOM"));
EXTERN char bot_top_msg[] INIT(= N_("search hit BOTTOM, continuing at TOP"));
+#ifdef FEAT_CRYPT
+EXTERN char need_key_msg[] INIT(= N_("Need encryption key for \"%s\""));
+#endif
+
/*
* Comms. with the session manager (XSMP)
*/
diff --git a/src/main.c b/src/main.c
index f4e1fde19..0f2925586 100644
--- a/src/main.c
+++ b/src/main.c
@@ -595,7 +595,7 @@ main
*/
if (recoverymode && fname == NULL)
{
- recover_names(NULL, TRUE, 0);
+ recover_names(NULL, TRUE, 0, NULL);
mch_exit(0);
}
diff --git a/src/memfile.c b/src/memfile.c
index 5412a6145..c6f5fdf8d 100644
--- a/src/memfile.c
+++ b/src/memfile.c
@@ -85,6 +85,7 @@ static void mf_ins_free __ARGS((memfile_T *, bhdr_T *));
static bhdr_T *mf_rem_free __ARGS((memfile_T *));
static int mf_read __ARGS((memfile_T *, bhdr_T *));
static int mf_write __ARGS((memfile_T *, bhdr_T *));
+static int mf_write_block __ARGS((memfile_T *mfp, bhdr_T *hp, off_t offset, unsigned size));
static int mf_trans_add __ARGS((memfile_T *, bhdr_T *));
static void mf_do_open __ARGS((memfile_T *, char_u *, int));
@@ -161,6 +162,9 @@ mf_open(fname, flags)
mfp->mf_trans[i] = NULL; /* trans lists are empty */
}
mfp->mf_page_size = MEMFILE_PAGE_SIZE;
+#ifdef FEAT_CRYPT
+ mfp->mf_old_key = NULL;
+#endif
#ifdef USE_FSTATFS
/*
@@ -422,7 +426,7 @@ mf_new(mfp, negative, page_count)
}
/*
- * get existing block 'nr' with 'page_count' pages
+ * Get existing block "nr" with "page_count" pages.
*
* Note: The caller should first check a negative nr with mf_trans_del()
*/
@@ -1050,6 +1054,13 @@ mf_read(mfp, hp)
PERROR(_("E295: Read error in swap file"));
return FAIL;
}
+
+#ifdef FEAT_CRYPT
+ /* Decrypt if 'key' is set and this is a data block. */
+ if (*mfp->mf_buffer->b_p_key != NUL)
+ ml_decrypt_data(mfp, hp->bh_data, offset, size);
+#endif
+
return OK;
}
@@ -1107,8 +1118,7 @@ mf_write(mfp, hp)
else
page_count = hp2->bh_page_count;
size = page_size * page_count;
- if ((unsigned)vim_write(mfp->mf_fd,
- (hp2 == NULL ? hp : hp2)->bh_data, size) != size)
+ if (mf_write_block(mfp, hp2 == NULL ? hp : hp2, offset, size) == FAIL)
{
/*
* Avoid repeating the error message, this mostly happens when the
@@ -1134,6 +1144,42 @@ mf_write(mfp, hp)
}
/*
+ * Write block "hp" with data size "size" to file "mfp->mf_fd".
+ * Takes care of encryption.
+ * Return FAIL or OK.
+ */
+ static int
+mf_write_block(mfp, hp, offset, size)
+ memfile_T *mfp;
+ bhdr_T *hp;
+ off_t offset UNUSED;
+ unsigned size;
+{
+ char_u *data = hp->bh_data;
+ int result = OK;
+
+#ifdef FEAT_CRYPT
+ /* Encrypt if 'key' is set and this is a data block. */
+ if (*mfp->mf_buffer->b_p_key != NUL)
+ {
+ data = ml_encrypt_data(mfp, data, offset, size);
+ if (data == NULL)
+ return FAIL;
+ }
+#endif
+
+ if ((unsigned)vim_write(mfp->mf_fd, data, size) != size)
+ result = FAIL;
+
+#ifdef FEAT_CRYPT
+ if (data != hp->bh_data)
+ vim_free(data);
+#endif
+
+ return result;
+}
+
+/*
* Make block number for *hp positive and add it to the translation list
*
* Return FAIL for failure, OK otherwise
@@ -1156,7 +1202,7 @@ mf_trans_add(mfp, hp)
return FAIL;
/*
- * get a new number for the block.
+ * Get a new number for the block.
* If the first item in the free list has sufficient pages, use its number
* Otherwise use mf_blocknr_max.
*/
diff --git a/src/memline.c b/src/memline.c
index d9043dce6..75800b15b 100644
--- a/src/memline.c
+++ b/src/memline.c
@@ -65,10 +65,12 @@ typedef struct pointer_block PTR_BL; /* contents of a pointer block */
typedef struct data_block DATA_BL; /* contents of a data block */
typedef struct pointer_entry PTR_EN; /* block/line-count pair */
-#define DATA_ID (('d' << 8) + 'a') /* data block id */
-#define PTR_ID (('p' << 8) + 't') /* pointer block id */
-#define BLOCK0_ID0 'b' /* block 0 id 0 */
-#define BLOCK0_ID1 '0' /* block 0 id 1 */
+#define DATA_ID (('d' << 8) + 'a') /* data block id */
+#define PTR_ID (('p' << 8) + 't') /* pointer block id */
+#define BLOCK0_ID0 'b' /* block 0 id 0 */
+#define BLOCK0_ID1 '0' /* block 0 id 1 */
+#define BLOCK0_ID1_C0 'c' /* block 0 id 1 'cm' 0 */
+#define BLOCK0_ID1_C1 'C' /* block 0 id 1 'cm' 1 */
/*
* pointer to a block, used in a pointer block
@@ -128,7 +130,8 @@ struct data_block
#define HEADER_SIZE (sizeof(DATA_BL) - INDEX_SIZE) /* size of data block header */
#define B0_FNAME_SIZE_ORG 900 /* what it was in older versions */
-#define B0_FNAME_SIZE 898
+#define B0_FNAME_SIZE_NOCRYPT 898 /* 2 bytes used for other things */
+#define B0_FNAME_SIZE_CRYPT 890 /* 10 bytes used for other things */
#define B0_UNAME_SIZE 40
#define B0_HNAME_SIZE 40
/*
@@ -155,7 +158,8 @@ struct data_block
*/
struct block0
{
- char_u b0_id[2]; /* id for block 0: BLOCK0_ID0 and BLOCK0_ID1 */
+ char_u b0_id[2]; /* id for block 0: BLOCK0_ID0 and BLOCK0_ID1,
+ * BLOCK0_ID1_C0, BLOCK0_ID1_C1 */
char_u b0_version[10]; /* Vim version string */
char_u b0_page_size[4];/* number of bytes per page */
char_u b0_mtime[4]; /* last modification time of file */
@@ -177,12 +181,18 @@ struct block0
* when there is room, for very long file names it's omitted.
*/
#define B0_DIRTY 0x55
-#define b0_dirty b0_fname[B0_FNAME_SIZE_ORG-1]
+#define b0_dirty b0_fname[B0_FNAME_SIZE_ORG - 1]
/*
* The b0_flags field is new in Vim 7.0.
*/
-#define b0_flags b0_fname[B0_FNAME_SIZE_ORG-2]
+#define b0_flags b0_fname[B0_FNAME_SIZE_ORG - 2]
+
+/*
+ * Crypt seed goes here, 8 bytes. New in Vim 7.3.
+ * Without encryption these bytes may be used for 'fenc'.
+ */
+#define b0_seed b0_fname[B0_FNAME_SIZE_ORG - 2 - MF_SEED_LEN]
/* The lowest two bits contain the fileformat. Zero means it's not set
* (compatible with Vim 6.x), otherwise it's EOL_UNIX + 1, EOL_DOS + 1 or
@@ -216,7 +226,18 @@ static linenr_T lowest_marked = 0;
#define ML_FLUSH 0x02 /* flush locked block */
#define ML_SIMPLE(x) (x & 0x10) /* DEL, INS or FIND */
-static void ml_upd_block0 __ARGS((buf_T *buf, int set_fname));
+/* argument for ml_upd_block0() */
+typedef enum {
+ UB_FNAME = 0 /* update timestamp and filename */
+ , UB_SAME_DIR /* update the B0_SAME_DIR flag */
+ , UB_CRYPT /* update crypt key */
+} upd_block0_T;
+
+#ifdef FEAT_CRYPT
+static void ml_set_b0_crypt __ARGS((buf_T *buf, ZERO_BL *b0p));
+#endif
+static int ml_check_b0_id __ARGS((ZERO_BL *b0p));
+static void ml_upd_block0 __ARGS((buf_T *buf, upd_block0_T what));
static void set_b0_fname __ARGS((ZERO_BL *, buf_T *buf));
static void set_b0_dir_flag __ARGS((ZERO_BL *b0p, buf_T *buf));
#ifdef FEAT_MBYTE
@@ -242,6 +263,9 @@ static long char_to_long __ARGS((char_u *));
#if defined(UNIX) || defined(WIN3264)
static char_u *make_percent_swname __ARGS((char_u *dir, char_u *name));
#endif
+#ifdef FEAT_CRYPT
+static void ml_crypt_prepare __ARGS((memfile_T *mfp, off_t offset, int reading));
+#endif
#ifdef FEAT_BYTEOFF
static void ml_updatechunk __ARGS((buf_T *buf, long line, long len, int updtype));
#endif
@@ -264,7 +288,7 @@ ml_open(buf)
/*
* init fields in memline struct
*/
- buf->b_ml.ml_stack_size = 0; /* no stack yet */
+ buf->b_ml.ml_stack_size = 0; /* no stack yet */
buf->b_ml.ml_stack = NULL; /* no stack yet */
buf->b_ml.ml_stack_top = 0; /* nothing in the stack */
buf->b_ml.ml_locked = NULL; /* no cached block */
@@ -289,6 +313,9 @@ ml_open(buf)
goto error;
buf->b_ml.ml_mfp = mfp;
+#ifdef FEAT_CRYPT
+ mfp->mf_buffer = buf;
+#endif
buf->b_ml.ml_flags = ML_EMPTY;
buf->b_ml.ml_line_count = 1;
#ifdef FEAT_LINEBREAK
@@ -336,12 +363,16 @@ ml_open(buf)
mch_get_host_name(b0p->b0_hname, B0_HNAME_SIZE);
b0p->b0_hname[B0_HNAME_SIZE - 1] = NUL;
long_to_char(mch_get_pid(), b0p->b0_pid);
+#ifdef FEAT_CRYPT
+ if (*buf->b_p_key != NUL)
+ ml_set_b0_crypt(buf, b0p);
+#endif
}
/*
* Always sync block number 0 to disk, so we can check the file name in
- * the swap file in findswapname(). Don't do this for help files though
- * and spell buffer though.
+ * the swap file in findswapname(). Don't do this for a help files or
+ * a spell buffer though.
* Only works when there's a swapfile, otherwise it's done when the file
* is created.
*/
@@ -397,6 +428,165 @@ error:
return FAIL;
}
+#if defined(FEAT_CRYPT) || defined(PROTO)
+/*
+ * Prepare encryption for "buf" with block 0 "b0p".
+ */
+ static void
+ml_set_b0_crypt(buf, b0p)
+ buf_T *buf;
+ ZERO_BL *b0p;
+{
+ if (*buf->b_p_key == NUL)
+ b0p->b0_id[1] = BLOCK0_ID1;
+ else
+ {
+ if (buf->b_p_cm == 0)
+ b0p->b0_id[1] = BLOCK0_ID1_C0;
+ else
+ {
+ b0p->b0_id[1] = BLOCK0_ID1_C1;
+ /* Generate a seed and store it in block 0 and in the memfile. */
+ sha2_seed(&b0p->b0_seed, MF_SEED_LEN, NULL, 0);
+ mch_memmove(buf->b_ml.ml_mfp->mf_seed, &b0p->b0_seed, MF_SEED_LEN);
+ }
+ }
+}
+
+/*
+ * Called after the crypt key or 'cryptmethod' was changed for "buf".
+ * Will apply this to the swapfile.
+ * "old_key" is the previous key. It is equal to buf->b_p_key when
+ * 'cryptmethod' is changed.
+ * "old_cm" is the previous 'cryptmethod'. It is equal to buf->b_p_cm when
+ * 'key' is changed.
+ */
+ void
+ml_set_crypt_key(buf, old_key, old_cm)
+ buf_T *buf;
+ char_u *old_key;
+ int old_cm;
+{
+ memfile_T *mfp = buf->b_ml.ml_mfp;
+ bhdr_T *hp;
+ int page_count;
+ int idx;
+ long error;
+ infoptr_T *ip;
+ PTR_BL *pp;
+ DATA_BL *dp;
+ blocknr_T bnum;
+ int top;
+
+ if (mfp == NULL || mfp->mf_fd < 0)
+ return; /* no memfile yet, nothing to do */
+
+ /* Set the key, method and seed to be used for reading, these must be the
+ * old values. */
+ mfp->mf_old_key = old_key;
+ mfp->mf_old_cm = old_cm;
+ if (old_cm > 0)
+ mch_memmove(mfp->mf_old_seed, mfp->mf_seed, MF_SEED_LEN);
+
+ /* Update block 0 with the crypt flag and may set a new seed. */
+ ml_upd_block0(buf, UB_CRYPT);
+
+ if (mfp->mf_infile_count > 2)
+ {
+ /*
+ * Need to read back all data blocks from disk, decrypt them with the
+ * old key/method and mark them to be written. The algorithm is
+ * similar to what happens in ml_recover(), but we skip negative block
+ * numbers.
+ */
+ ml_flush_line(buf); /* flush buffered line */
+ (void)ml_find_line(buf, (linenr_T)0, ML_FLUSH); /* flush locked block */
+
+ hp = NULL;
+ bnum = 1; /* start with block 1 */
+ page_count = 1; /* which is 1 page */
+ idx = 0; /* start with first index in block 1 */
+ error = 0;
+ buf->b_ml.ml_stack_top = 0;
+ buf->b_ml.ml_stack = NULL;
+ buf->b_ml.ml_stack_size = 0; /* no stack yet */
+
+ for ( ; !got_int; line_breakcheck())
+ {
+ if (hp != NULL)
+ mf_put(mfp, hp, FALSE, FALSE); /* release previous block */
+
+ /* get the block (pointer or data) */
+ if ((hp = mf_get(mfp, (blocknr_T)bnum, page_count)) == NULL)
+ {
+ if (bnum == 1)
+ break;
+ ++error;
+ }
+ else
+ {
+ pp = (PTR_BL *)(hp->bh_data);
+ if (pp->pb_id == PTR_ID) /* it is a pointer block */
+ {
+ if (pp->pb_count == 0)
+ {
+ /* empty block? */
+ ++error;
+ }
+ else if (idx < (int)pp->pb_count) /* go a block deeper */
+ {
+ if (pp->pb_pointer[idx].pe_bnum < 0)
+ {
+ /* Skip data block with negative block number. */
+ ++idx; /* get same block again for next index */
+ continue;
+ }
+
+ /* going one block deeper in the tree, new entry in
+ * stack */
+ if ((top = ml_add_stack(buf)) < 0)
+ {
+ ++error;
+ break; /* out of memory */
+ }
+ ip = &(buf->b_ml.ml_stack[top]);
+ ip->ip_bnum = bnum;
+ ip->ip_index = idx;
+
+ bnum = pp->pb_pointer[idx].pe_bnum;
+ page_count = pp->pb_pointer[idx].pe_page_count;
+ continue;
+ }
+ }
+ else /* not a pointer block */
+ {
+ dp = (DATA_BL *)(hp->bh_data);
+ if (dp->db_id != DATA_ID) /* block id wrong */
+ ++error;
+ else
+ {
+ /* It is a data block, need to write it back to disk. */
+ mf_put(mfp, hp, TRUE, FALSE);
+ hp = NULL;
+ }
+ }
+ }
+
+ if (buf->b_ml.ml_stack_top == 0) /* finished */
+ break;
+
+ /* go one block up in the tree */
+ ip = &(buf->b_ml.ml_stack[--(buf->b_ml.ml_stack_top)]);
+ bnum = ip->ip_bnum;
+ idx = ip->ip_index + 1; /* go to next index */
+ page_count = 1;
+ }
+ }
+
+ mfp->mf_old_key = NULL;
+}
+#endif
+
/*
* ml_setname() is called when the file name of "buf" has been changed.
* It may rename the swap file.
@@ -475,7 +665,7 @@ ml_setname(buf)
#else
mf_set_ffname(mfp);
#endif
- ml_upd_block0(buf, FALSE);
+ ml_upd_block0(buf, UB_SAME_DIR);
break;
}
vim_free(fname); /* this fname didn't work, try another */
@@ -569,7 +759,7 @@ ml_open_file(buf)
*/
mf_fullname(mfp);
#endif
- ml_upd_block0(buf, FALSE);
+ ml_upd_block0(buf, UB_SAME_DIR);
/* Flush block zero, so others can read it */
if (mf_sync(mfp, MFS_ZERO) == OK)
@@ -680,16 +870,32 @@ ml_close_notmod()
ml_timestamp(buf)
buf_T *buf;
{
- ml_upd_block0(buf, TRUE);
+ ml_upd_block0(buf, UB_FNAME);
+}
+
+/*
+ * Return FAIL when the ID of "b0p" is wrong.
+ */
+ static int
+ml_check_b0_id(b0p)
+ ZERO_BL *b0p;
+{
+ if (b0p->b0_id[0] != BLOCK0_ID0
+ || (b0p->b0_id[1] != BLOCK0_ID1
+ && b0p->b0_id[1] != BLOCK0_ID1_C0
+ && b0p->b0_id[1] != BLOCK0_ID1_C1)
+ )
+ return FAIL;
+ return OK;
}
/*
* Update the timestamp or the B0_SAME_DIR flag of the .swp file.
*/
static void
-ml_upd_block0(buf, set_fname)
+ml_upd_block0(buf, what)
buf_T *buf;
- int set_fname;
+ upd_block0_T what;
{
memfile_T *mfp;
bhdr_T *hp;
@@ -699,13 +905,17 @@ ml_upd_block0(buf, set_fname)
if (mfp == NULL || (hp = mf_get(mfp, (blocknr_T)0, 1)) == NULL)
return;
b0p = (ZERO_BL *)(hp->bh_data);
- if (b0p->b0_id[0] != BLOCK0_ID0 || b0p->b0_id[1] != BLOCK0_ID1)
+ if (ml_check_b0_id(b0p) == FAIL)
EMSG(_("E304: ml_upd_block0(): Didn't get block 0??"));
else
{
- if (set_fname)
+ if (what == UB_FNAME)
set_b0_fname(b0p, buf);
- else
+#ifdef FEAT_CRYPT
+ else if (what == UB_CRYPT)
+ ml_set_b0_crypt(buf, b0p);
+#endif
+ else /* what == UB_SAME_DIR */
set_b0_dir_flag(b0p, buf);
}
mf_put(mfp, hp, TRUE, FALSE);
@@ -731,7 +941,7 @@ set_b0_fname(b0p, buf)
/* Systems that cannot translate "~user" back into a path: copy the
* file name unmodified. Do use slashes instead of backslashes for
* portability. */
- vim_strncpy(b0p->b0_fname, buf->b_ffname, B0_FNAME_SIZE - 1);
+ vim_strncpy(b0p->b0_fname, buf->b_ffname, B0_FNAME_SIZE_CRYPT - 1);
# ifdef BACKSLASH_IN_FILENAME
forward_slash(b0p->b0_fname);
# endif
@@ -746,14 +956,16 @@ set_b0_fname(b0p, buf)
* First replace home dir path with "~/" with home_replace().
* Then insert the user name to get "~user/".
*/
- home_replace(NULL, buf->b_ffname, b0p->b0_fname, B0_FNAME_SIZE, TRUE);
+ home_replace(NULL, buf->b_ffname, b0p->b0_fname,
+ B0_FNAME_SIZE_CRYPT, TRUE);
if (b0p->b0_fname[0] == '~')
{
flen = STRLEN(b0p->b0_fname);
/* If there is no user name or it is too long, don't use "~/" */
if (get_user_name(uname, B0_UNAME_SIZE) == FAIL
- || (ulen = STRLEN(uname)) + flen > B0_FNAME_SIZE - 1)
- vim_strncpy(b0p->b0_fname, buf->b_ffname, B0_FNAME_SIZE - 1);
+ || (ulen = STRLEN(uname)) + flen > B0_FNAME_SIZE_CRYPT - 1)
+ vim_strncpy(b0p->b0_fname, buf->b_ffname,
+ B0_FNAME_SIZE_CRYPT - 1);
else
{
mch_memmove(b0p->b0_fname + ulen + 1, b0p->b0_fname + 1, flen);
@@ -816,15 +1028,24 @@ add_b0_fenc(b0p, buf)
buf_T *buf;
{
int n;
+ int size = B0_FNAME_SIZE_NOCRYPT;
+
+# ifdef FEAT_CRYPT
+ /* Without encryption use the same offset as in Vim 7.2 to be compatible.
+ * With encryption it's OK to move elsewhere, the swap file is not
+ * compatible anyway. */
+ if (*buf->b_p_key != NUL)
+ size = B0_FNAME_SIZE_CRYPT;
+# endif
n = (int)STRLEN(buf->b_p_fenc);
- if (STRLEN(b0p->b0_fname) + n + 1 > B0_FNAME_SIZE)
+ if ((int)STRLEN(b0p->b0_fname) + n + 1 > size)
b0p->b0_flags &= ~B0_HAS_FENC;
else
{
- mch_memmove((char *)b0p->b0_fname + B0_FNAME_SIZE - n,
+ mch_memmove((char *)b0p->b0_fname + size - n,
(char *)buf->b_p_fenc, (size_t)n);
- *(b0p->b0_fname + B0_FNAME_SIZE - n - 1) = NUL;
+ *(b0p->b0_fname + size - n - 1) = NUL;
b0p->b0_flags |= B0_HAS_FENC;
}
}
@@ -832,7 +1053,7 @@ add_b0_fenc(b0p, buf)
/*
- * try to recover curbuf from the .swp file
+ * Try to recover curbuf from the .swp file.
*/
void
ml_recover()
@@ -840,10 +1061,14 @@ ml_recover()
buf_T *buf = NULL;
memfile_T *mfp = NULL;
char_u *fname;
+ char_u *fname_used = NULL;
bhdr_T *hp = NULL;
ZERO_BL *b0p;
int b0_ff;
char_u *b0_fenc = NULL;
+#ifdef FEAT_CRYPT
+ int b0_cm = -1;
+#endif
PTR_BL *pp;
DATA_BL *dp;
infoptr_T *ip;
@@ -892,14 +1117,14 @@ ml_recover()
&& ASCII_ISALPHA(fname[len - 1]))
{
directly = TRUE;
- fname = vim_strsave(fname); /* make a copy for mf_open() */
+ fname_used = vim_strsave(fname); /* make a copy for mf_open() */
}
else
{
directly = FALSE;
/* count the number of matching swap files */
- len = recover_names(&fname, FALSE, 0);
+ len = recover_names(fname, FALSE, 0, NULL);
if (len == 0) /* no swap files found */
{
EMSG2(_("E305: No swap file found for %s"), fname);
@@ -910,7 +1135,7 @@ ml_recover()
else /* several swap files found, choose */
{
/* list the names of the swap files */
- (void)recover_names(&fname, TRUE, 0);
+ (void)recover_names(fname, TRUE, 0, NULL);
msg_putchar('\n');
MSG_PUTS(_("Enter number of swap file to use (0 to quit): "));
i = get_number(FALSE, NULL);
@@ -918,28 +1143,26 @@ ml_recover()
goto theend;
}
/* get the swap file name that will be used */
- (void)recover_names(&fname, FALSE, i);
+ (void)recover_names(fname, FALSE, i, &fname_used);
}
- if (fname == NULL)
+ if (fname_used == NULL)
goto theend; /* out of memory */
/* When called from main() still need to initialize storage structure */
if (called_from_main && ml_open(curbuf) == FAIL)
getout(1);
-/*
- * allocate a buffer structure (only the memline in it is really used)
- */
+ /*
+ * Allocate a buffer structure for the swap file that is used for recovery.
+ * Only the memline in it is really used.
+ */
buf = (buf_T *)alloc((unsigned)sizeof(buf_T));
if (buf == NULL)
- {
- vim_free(fname);
goto theend;
- }
-/*
- * init fields in memline struct
- */
+ /*
+ * init fields in memline struct
+ */
buf->b_ml.ml_stack_size = 0; /* no stack yet */
buf->b_ml.ml_stack = NULL; /* no stack yet */
buf->b_ml.ml_stack_top = 0; /* nothing in the stack */
@@ -947,23 +1170,24 @@ ml_recover()
buf->b_ml.ml_locked = NULL; /* no locked block */
buf->b_ml.ml_flags = 0;
-/*
- * open the memfile from the old swap file
- */
- p = vim_strsave(fname); /* save fname for the message
- (mf_open() may free fname) */
- mfp = mf_open(fname, O_RDONLY); /* consumes fname! */
+ /*
+ * open the memfile from the old swap file
+ */
+ p = vim_strsave(fname_used); /* save "fname_used" for the message:
+ mf_open() will consume "fname_used"! */
+ mfp = mf_open(fname_used, O_RDONLY);
+ fname_used = p;
if (mfp == NULL || mfp->mf_fd < 0)
{
- if (p != NULL)
- {
- EMSG2(_("E306: Cannot open %s"), p);
- vim_free(p);
- }
+ if (fname_used != NULL)
+ EMSG2(_("E306: Cannot open %s"), fname_used);
goto theend;
}
- vim_free(p);
buf->b_ml.ml_mfp = mfp;
+#ifdef FEAT_CRYPT
+ mfp->mf_buffer = buf;
+ buf->b_p_key = empty_option;
+#endif
/*
* The page size set in mf_open() might be different from the page size
@@ -973,16 +1197,15 @@ ml_recover()
*/
mfp->mf_page_size = MIN_SWAP_PAGE_SIZE;
-/*
- * try to read block 0
- */
+ /*
+ * try to read block 0
+ */
if ((hp = mf_get(mfp, (blocknr_T)0, 1)) == NULL)
{
msg_start();
MSG_PUTS_ATTR(_("Unable to read block 0 from "), attr | MSG_HIST);
msg_outtrans_attr(mfp->mf_fname, attr | MSG_HIST);
- MSG_PUTS_ATTR(
- _("\nMaybe no changes were made or Vim did not update the swap file."),
+ MSG_PUTS_ATTR(_("\nMaybe no changes were made or Vim did not update the swap file."),
attr | MSG_HIST);
msg_end();
goto theend;
@@ -998,7 +1221,7 @@ ml_recover()
msg_end();
goto theend;
}
- if (b0p->b0_id[0] != BLOCK0_ID0 || b0p->b0_id[1] != BLOCK0_ID1)
+ if (ml_check_b0_id(b0p) == FAIL)
{
EMSG2(_("E307: %s does not look like a Vim swap file"), mfp->mf_fname);
goto theend;
@@ -1024,6 +1247,22 @@ ml_recover()
goto theend;
}
+#ifdef FEAT_CRYPT
+ if (b0p->b0_id[1] == BLOCK0_ID1_C0)
+ buf->b_p_cm = b0_cm = 0;
+ else if (b0p->b0_id[1] == BLOCK0_ID1_C1)
+ {
+ buf->b_p_cm = b0_cm = 1;
+ mch_memmove(mfp->mf_seed, &b0p->b0_seed, MF_SEED_LEN);
+ }
+#else
+ if (b0p->b0_id[1] != BLOCK0_ID1)
+ {
+ EMSG2(_("E000: %s is encrypted and this version of Vim does not support encryption"), mfp->mf_fname);
+ goto theend;
+ }
+#endif
+
/*
* If we guessed the wrong page size, we have to recalculate the
* highest block number in the file.
@@ -1058,9 +1297,9 @@ ml_recover()
b0p = (ZERO_BL *)(hp->bh_data);
}
-/*
- * If .swp file name given directly, use name from swap file for buffer.
- */
+ /*
+ * If .swp file name given directly, use name from swap file for buffer.
+ */
if (directly)
{
expand_env(b0p->b0_fname, NameBuff, MAXPATHL);
@@ -1078,9 +1317,9 @@ ml_recover()
smsg((char_u *)_("Original file \"%s\""), NameBuff);
msg_putchar('\n');
-/*
- * check date of swap file and original file
- */
+ /*
+ * check date of swap file and original file
+ */
mtime = char_to_long(b0p->b0_mtime);
if (curbuf->b_ffname != NULL
&& mch_stat((char *)curbuf->b_ffname, &org_stat) != -1
@@ -1096,10 +1335,16 @@ ml_recover()
b0_ff = (b0p->b0_flags & B0_FF_MASK);
if (b0p->b0_flags & B0_HAS_FENC)
{
- for (p = b0p->b0_fname + B0_FNAME_SIZE;
- p > b0p->b0_fname && p[-1] != NUL; --p)
+ int size = B0_FNAME_SIZE_NOCRYPT;
+
+#ifdef FEAT_CRYPT
+ /* Use the same size as in add_b0_fenc(). */
+ if (b0p->b0_id[1] != BLOCK0_ID1)
+ size = B0_FNAME_SIZE_CRYPT;
+#endif
+ for (p = b0p->b0_fname + size; p > b0p->b0_fname && p[-1] != NUL; --p)
;
- b0_fenc = vim_strnsave(p, (int)(b0p->b0_fname + B0_FNAME_SIZE - p));
+ b0_fenc = vim_strnsave(p, (int)(b0p->b0_fname + size - p));
}
mf_put(mfp, hp, FALSE, FALSE); /* release block 0 */
@@ -1115,11 +1360,40 @@ ml_recover()
/*
* Try reading the original file to obtain the values of 'fileformat',
* 'fileencoding', etc. Ignore errors. The text itself is not used.
+ * When the file is encrypted the user is asked to enter the key.
*/
if (curbuf->b_ffname != NULL)
orig_file_status = readfile(curbuf->b_ffname, NULL, (linenr_T)0,
(linenr_T)0, (linenr_T)MAXLNUM, NULL, READ_NEW);
+#ifdef FEAT_CRYPT
+ if (b0_cm >= 0)
+ {
+ /* Need to ask the user for the crypt key. If this fails we continue
+ * without a key, will probably get garbage text. */
+ if (*curbuf->b_p_key != NUL)
+ {
+ smsg((char_u *)_("Swap file is encrypted: \"%s\""), fname_used);
+ MSG_PUTS(_("\nIf you entered a new crypt key but did not write the text file,"));
+ MSG_PUTS(_("\nenter the new crypt key."));
+ MSG_PUTS(_("\nIf you wrote the text file after changing the crypt key press enter"));
+ MSG_PUTS(_("\nto use the same key for text file and swap file"));
+ }
+ else
+ smsg((char_u *)_(need_key_msg), fname_used);
+ buf->b_p_key = get_crypt_key(FALSE, FALSE);
+ if (buf->b_p_key == NULL)
+ buf->b_p_key = curbuf->b_p_key;
+ else if (*buf->b_p_key == NUL)
+ {
+ vim_free(buf->b_p_key);
+ buf->b_p_key = curbuf->b_p_key;
+ }
+ if (buf->b_p_key == NULL)
+ buf->b_p_key = empty_option;
+ }
+#endif
+
/* Use the 'fileformat' and 'fileencoding' as stored in the swap file. */
if (b0_ff != 0)
set_fileformat(b0_ff - 1, OPT_LOCAL);
@@ -1386,9 +1660,17 @@ ml_recover()
MSG_PUTS(_("\nYou may want to delete the .swp file now.\n\n"));
cmdline_row = msg_row;
}
+#ifdef FEAT_CRYPT
+ if (*buf->b_p_key != NUL && STRCMP(curbuf->b_p_key, buf->b_p_key) != 0)
+ {
+ MSG_PUTS(_("Using crypt key from swap file for the text file.\n"));
+ set_option_value((char_u *)"key", 0L, buf->b_p_key, OPT_LOCAL);
+ }
+#endif
redraw_curbuf_later(NOT_VALID);
theend:
+ vim_free(fname_used);
recoverymode = FALSE;
if (mfp != NULL)
{
@@ -1398,6 +1680,10 @@ theend:
}
if (buf != NULL)
{
+#ifdef FEAT_CRYPT
+ if (buf->b_p_key != curbuf->b_p_key)
+ free_string_option(buf->b_p_key);
+#endif
vim_free(buf->b_ml.ml_stack);
vim_free(buf);
}
@@ -1424,10 +1710,11 @@ theend:
* - find the name of the n'th swap file when recovering
*/
int
-recover_names(fname, list, nr)
- char_u **fname; /* base for swap file name */
- int list; /* when TRUE, list the swap file names */
- int nr; /* when non-zero, return nr'th swap file name */
+recover_names(fname, list, nr, fname_out)
+ char_u *fname; /* base for swap file name */
+ int list; /* when TRUE, list the swap file names */
+ int nr; /* when non-zero, return nr'th swap file name */
+ char_u **fname_out; /* result when "nr" > 0 */
{
int num_names;
char_u *(names[6]);
@@ -1447,13 +1734,13 @@ recover_names(fname, list, nr)
if (fname != NULL)
{
#ifdef HAVE_READLINK
- /* Expand symlink in the file name, because the swap file is created with
- * the actual file instead of with the symlink. */
- if (resolve_symlink(*fname, fname_buf) == OK)
- fname_res = fname_buf;
- else
+ /* Expand symlink in the file name, because the swap file is created
+ * with the actual file instead of with the symlink. */
+ if (resolve_symlink(fname, fname_buf) == OK)
+ fname_res = fname_buf;
+ else
#endif
- fname_res = *fname;
+ fname_res = fname;
}
if (list)
@@ -1480,7 +1767,7 @@ recover_names(fname, list, nr)
if (dir_name[0] == '.' && dir_name[1] == NUL) /* check current dir */
{
- if (fname == NULL || *fname == NULL)
+ if (fname == NULL)
{
#ifdef VMS
names[0] = vim_strsave((char_u *)"*_sw%");
@@ -1511,7 +1798,7 @@ recover_names(fname, list, nr)
}
else /* check directory dir_name */
{
- if (fname == NULL || *fname == NULL)
+ if (fname == NULL)
{
#ifdef VMS
names[0] = concat_fnames(dir_name, (char_u *)"*_sw%", TRUE);
@@ -1583,8 +1870,7 @@ recover_names(fname, list, nr)
* not able to execute the shell).
* Try finding a swap file by simply adding ".swp" to the file name.
*/
- if (*dirp == NUL && file_count + num_files == 0
- && fname != NULL && *fname != NULL)
+ if (*dirp == NUL && file_count + num_files == 0 && fname != NULL)
{
struct stat st;
char_u *swapname;
@@ -1637,7 +1923,8 @@ recover_names(fname, list, nr)
file_count += num_files;
if (nr <= file_count)
{
- *fname = vim_strsave(files[nr - 1 + num_files - file_count]);
+ *fname_out = vim_strsave(
+ files[nr - 1 + num_files - file_count]);
dirp = (char_u *)""; /* stop searching */
}
}
@@ -1645,7 +1932,7 @@ recover_names(fname, list, nr)
{
if (dir_name[0] == '.' && dir_name[1] == NUL)
{
- if (fname == NULL || *fname == NULL)
+ if (fname == NULL)
MSG_PUTS(_(" In current directory:\n"));
else
MSG_PUTS(_(" Using specified name:\n"));
@@ -1772,7 +2059,7 @@ swapfile_info(fname)
{
MSG_PUTS(_(" [from Vim version 3.0]"));
}
- else if (b0.b0_id[0] != BLOCK0_ID0 || b0.b0_id[1] != BLOCK0_ID1)
+ else if (ml_check_b0_id(&b0) == FAIL)
{
MSG_PUTS(_(" [does not look like a Vim swap file]"));
}
@@ -3478,11 +3765,11 @@ ml_find_line(buf, lnum, action)
error_block:
mf_put(mfp, hp, FALSE, FALSE);
error_noblock:
-/*
- * If action is ML_DELETE or ML_INSERT we have to correct the tree for
- * the incremented/decremented line counts, because there won't be a line
- * inserted/deleted after all.
- */
+ /*
+ * If action is ML_DELETE or ML_INSERT we have to correct the tree for
+ * the incremented/decremented line counts, because there won't be a line
+ * inserted/deleted after all.
+ */
if (action == ML_DELETE)
ml_lineadd(buf, 1);
else if (action == ML_INSERT)
@@ -3505,10 +3792,10 @@ ml_add_stack(buf)
top = buf->b_ml.ml_stack_top;
- /* may have to increase the stack size */
+ /* may have to increase the stack size */
if (top == buf->b_ml.ml_stack_size)
{
- CHECK(top > 0, _("Stack size increases")); /* more than 5 levels??? */
+ CHECK(top > 0, _("Stack size increases")); /* more than 5 levels??? */
newstack = (infoptr_T *)alloc((unsigned)sizeof(infoptr_T) *
(buf->b_ml.ml_stack_size + STACK_INCR));
@@ -4518,6 +4805,132 @@ ml_setflags(buf)
}
}
+#if defined(FEAT_CRYPT) || defined(PROTO)
+/*
+ * If "data" points to a data block encrypt the text in it and return a copy
+ * in allocated memory. Return NULL when out of memory.
+ * Otherwise return "data".
+ */
+ char_u *
+ml_encrypt_data(mfp, data, offset, size)
+ memfile_T *mfp;
+ char_u *data;
+ off_t offset;
+ unsigned size;
+{
+ DATA_BL *dp = (DATA_BL *)data;
+ char_u *head_end;
+ char_u *text_start;
+ char_u *new_data;
+ int text_len;
+
+ if (dp->db_id != DATA_ID)
+ return data;
+
+ new_data = (char_u *)alloc(size);
+ if (new_data == NULL)
+ return NULL;
+ head_end = (char_u *)(&dp->db_index[dp->db_line_count]);
+ text_start = (char_u *)dp + dp->db_txt_start;
+ text_len = size - dp->db_txt_start;
+
+ /* Copy the header and the text. */
+ mch_memmove(new_data, dp, head_end - (char_u *)dp);
+
+ /* Encrypt the text. */
+ crypt_push_state();
+ ml_crypt_prepare(mfp, offset, FALSE);
+ crypt_encode(text_start, text_len, new_data + dp->db_txt_start);
+ crypt_pop_state();
+
+ /* Clear the gap. */
+ if (head_end < text_start)
+ vim_memset(new_data + (head_end - data), 0, text_start - head_end);
+
+ return new_data;
+}
+
+/*
+ * Decrypt the text in "data" if it points to a data block.
+ */
+ void
+ml_decrypt_data(mfp, data, offset, size)
+ memfile_T *mfp;
+ char_u *data;
+ off_t offset;
+ unsigned size;
+{
+ DATA_BL *dp = (DATA_BL *)data;
+ char_u *head_end;
+ char_u *text_start;
+ int text_len;
+
+ if (dp->db_id == DATA_ID)
+ {
+ head_end = (char_u *)(&dp->db_index[dp->db_line_count]);
+ text_start = (char_u *)dp + dp->db_txt_start;
+ text_len = dp->db_txt_end - dp->db_txt_start;
+
+ if (head_end > text_start || dp->db_txt_start > size
+ || dp->db_txt_end > size)
+ return; /* data was messed up */
+
+ /* Decrypt the text in place. */
+ crypt_push_state();
+ ml_crypt_prepare(mfp, offset, TRUE);
+ crypt_decode(text_start, text_len);
+ crypt_pop_state();
+ }
+}
+
+/*
+ * Prepare for encryption/decryption, using the key, seed and offset.
+ */
+ static void
+ml_crypt_prepare(mfp, offset, reading)
+ memfile_T *mfp;
+ off_t offset;
+ int reading;
+{
+ buf_T *buf = mfp->mf_buffer;
+ char_u salt[50];
+ int method;
+ char_u *key;
+ char_u *seed;
+
+ if (reading && mfp->mf_old_key != NULL)
+ {
+ /* Reading back blocks with the previous key/method/seed. */
+ method = mfp->mf_old_cm;
+ key = mfp->mf_old_key;
+ seed = mfp->mf_old_seed;
+ }
+ else
+ {
+ method = buf->b_p_cm;
+ key = buf->b_p_key;
+ seed = mfp->mf_seed;
+ }
+
+ use_crypt_method = method; /* select pkzip or blowfish */
+ if (method == 0)
+ {
+ vim_snprintf((char *)salt, sizeof(salt), "%s%ld", key, (long)offset);
+ crypt_init_keys(salt);
+ }
+ else
+ {
+ /* Using blowfish, add salt and seed. We use the byte offset of the
+ * block for the salt. */
+ vim_snprintf((char *)salt, sizeof(salt), "%ld", (long)offset);
+ bf_key_init(key, salt, STRLEN(salt));
+ bf_ofb_init(seed, MF_SEED_LEN);
+ }
+}
+
+#endif
+
+
#if defined(FEAT_BYTEOFF) || defined(PROTO)
#define MLCS_MAXL 800 /* max no of lines in chunk */
diff --git a/src/misc2.c b/src/misc2.c
index e0eea68b5..1eb4e8a16 100644
--- a/src/misc2.c
+++ b/src/misc2.c
@@ -3746,6 +3746,59 @@ static ulg keys[3]; /* keys defining the pseudo-random sequence */
keys[2] = CRC32(keys[2], (int)(keys[1] >> 24)); \
}
+static int crypt_busy = 0;
+static ulg saved_keys[3];
+static int saved_crypt_method;
+
+/*
+ * Prepare for initializing encryption. If already doing encryption then save
+ * the state.
+ * Must always be called symmetrycally with crypt_pop_state().
+ */
+ void
+crypt_push_state()
+{
+ if (crypt_busy == 1)
+ {
+ /* save the state */
+ if (use_crypt_method == 0)
+ {
+ saved_keys[0] = keys[0];
+ saved_keys[1] = keys[1];
+ saved_keys[2] = keys[2];
+ }
+ else
+ bf_crypt_save();
+ saved_crypt_method = use_crypt_method;
+ }
+ else if (crypt_busy > 1)
+ EMSG2(_(e_intern2), "crypt_push_state()");
+ ++crypt_busy;
+}
+
+/*
+ * End encryption. If doing encryption before crypt_push_state() then restore
+ * the saved state.
+ * Must always be called symmetrycally with crypt_push_state().
+ */
+ void
+crypt_pop_state()
+{
+ --crypt_busy;
+ if (crypt_busy == 1)
+ {
+ use_crypt_method = saved_crypt_method;
+ if (use_crypt_method == 0)
+ {
+ keys[0] = saved_keys[0];
+ keys[1] = saved_keys[1];
+ keys[2] = saved_keys[2];
+ }
+ else
+ bf_crypt_restore();
+ }
+}
+
/*
* Encrypt "from[len]" into "to[len]".
* "from" and "to" can be equal to encrypt in place.
@@ -3894,6 +3947,8 @@ get_crypt_key(store, twice)
/* since the user typed this, no need to wait for return */
need_wait_return = FALSE;
+ if (msg_didout)
+ msg_putchar('\n');
msg_didout = FALSE;
free_crypt_key(p2);
diff --git a/src/option.c b/src/option.c
index d06381cb8..face9fde9 100644
--- a/src/option.c
+++ b/src/option.c
@@ -5969,13 +5969,18 @@ did_set_string_option(opt_idx, varp, new_value_alloced, oldval, errbuf,
}
}
-#if defined(FEAT_CRYPT) && defined(FEAT_CMDHIST)
+#if defined(FEAT_CRYPT)
/* 'cryptkey' */
else if (gvarp == &p_key)
{
+# if defined(FEAT_CMDHIST)
/* Make sure the ":set" command doesn't show the new value in the
* history. */
remove_key_from_history();
+# endif
+ if (STRCMP(curbuf->b_p_key, oldval) != 0)
+ /* Need to update the swapfile. */
+ ml_set_crypt_key(curbuf, oldval, curbuf->b_p_cm);
}
#endif
@@ -7941,15 +7946,19 @@ set_num_option(opt_idx, varp, value, errbuf, errbuflen, opt_flags)
if (curbuf->b_p_cm < 0)
{
errmsg = e_positive;
- curbuf->b_p_cm = 0;
+ curbuf->b_p_cm = old_value;
}
if (curbuf->b_p_cm > 1)
{
errmsg = e_invarg;
- curbuf->b_p_cm = 1;
+ curbuf->b_p_cm = old_value;
}
if (curbuf->b_p_cm > 0 && blowfish_self_test() == FAIL)
- curbuf->b_p_cm = 0;
+ curbuf->b_p_cm = old_value;
+
+ if (curbuf->b_p_cm != old_value && *curbuf->b_p_key != NUL)
+ /* Need to update the swapfile. */
+ ml_set_crypt_key(curbuf, curbuf->b_p_key, old_value);
}
#endif
diff --git a/src/proto/blowfish.pro b/src/proto/blowfish.pro
index ba18176fb..51e4fa9ec 100644
--- a/src/proto/blowfish.pro
+++ b/src/proto/blowfish.pro
@@ -4,5 +4,7 @@ void bf_ofb_init __ARGS((char_u *iv, int iv_len));
void bf_crypt_encode __ARGS((char_u *from, size_t len, char_u *to));
void bf_crypt_decode __ARGS((char_u *ptr, long len));
void bf_crypt_init_keys __ARGS((char_u *passwd));
+void bf_crypt_save __ARGS((void));
+void bf_crypt_restore __ARGS((void));
int blowfish_self_test __ARGS((void));
/* vim: set ft=c : */
diff --git a/src/proto/memline.pro b/src/proto/memline.pro
index 4d5067129..62a3ce640 100644
--- a/src/proto/memline.pro
+++ b/src/proto/memline.pro
@@ -1,5 +1,6 @@
/* memline.c */
int ml_open __ARGS((buf_T *buf));
+void ml_set_crypt_key __ARGS((buf_T *buf, char_u *old_key, int old_cm));
void ml_setname __ARGS((buf_T *buf));
void ml_open_files __ARGS((void));
void ml_open_file __ARGS((buf_T *buf));
@@ -9,7 +10,7 @@ void ml_close_all __ARGS((int del_file));
void ml_close_notmod __ARGS((void));
void ml_timestamp __ARGS((buf_T *buf));
void ml_recover __ARGS((void));
-int recover_names __ARGS((char_u **fname, int list, int nr));
+int recover_names __ARGS((char_u *fname, int list, int nr, char_u **fname_out));
void ml_sync_all __ARGS((int check_file, int check_char));
void ml_preserve __ARGS((buf_T *buf, int message));
char_u *ml_get __ARGS((linenr_T lnum));
@@ -29,6 +30,8 @@ int resolve_symlink __ARGS((char_u *fname, char_u *buf));
char_u *makeswapname __ARGS((char_u *fname, char_u *ffname, buf_T *buf, char_u *dir_name));
char_u *get_file_in_dir __ARGS((char_u *fname, char_u *dname));
void ml_setflags __ARGS((buf_T *buf));
+char_u *ml_encrypt_data __ARGS((memfile_T *mfp, char_u *data, off_t offset, unsigned size));
+void ml_decrypt_data __ARGS((memfile_T *mfp, char_u *data, off_t offset, unsigned size));
long ml_find_line_or_offset __ARGS((buf_T *buf, linenr_T lnum, long *offp));
void goto_byte __ARGS((long cnt));
/* vim: set ft=c : */
diff --git a/src/proto/misc2.pro b/src/proto/misc2.pro
index 6b292bb81..8d3397fc1 100644
--- a/src/proto/misc2.pro
+++ b/src/proto/misc2.pro
@@ -80,6 +80,8 @@ int illegal_slash __ARGS((char *name));
char_u *parse_shape_opt __ARGS((int what));
int get_shape_idx __ARGS((int mouse));
void update_mouseshape __ARGS((int shape_idx));
+void crypt_push_state __ARGS((void));
+void crypt_pop_state __ARGS((void));
void crypt_encode __ARGS((char_u *from, size_t len, char_u *to));
void crypt_decode __ARGS((char_u *ptr, long len));
void crypt_init_keys __ARGS((char_u *passwd));
diff --git a/src/sha256.c b/src/sha256.c
index c24e8b8f8..3379620f3 100644
--- a/src/sha256.c
+++ b/src/sha256.c
@@ -402,7 +402,8 @@ get_some_time()
}
/*
- * set header = sha2_seed(random_data);
+ * Fill "header[header_len]" with random_data.
+ * Also "salt[salt_len]" when "salt" is not NULL.
*/
void
sha2_seed(header, header_len, salt, salt_len)
@@ -429,8 +430,9 @@ sha2_seed(header, header_len, salt, salt_len)
header[i] = sha256sum[i % sizeof(sha256sum)];
/* put remaining block into salt. */
- for (i = 0; i < salt_len; i++)
- salt[i] = sha256sum[(i + header_len) % sizeof(sha256sum)];
+ if (salt != NULL)
+ for (i = 0; i < salt_len; i++)
+ salt[i] = sha256sum[(i + header_len) % sizeof(sha256sum)];
}
#endif /* FEAT_CRYPT */
diff --git a/src/structs.h b/src/structs.h
index 4f72f998b..a1c7e9d9a 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -392,7 +392,7 @@ struct block_hdr
bhdr_T *bh_prev; /* previous block_hdr in used list */
bhdr_T *bh_hash_next; /* next block_hdr in hash list */
bhdr_T *bh_hash_prev; /* previous block_hdr in hash list */
- blocknr_T bh_bnum; /* block number */
+ blocknr_T bh_bnum; /* block number */
char_u *bh_data; /* pointer to memory (for used block) */
int bh_page_count; /* number of pages in this block */
@@ -491,12 +491,15 @@ typedef struct
# endif
} cmdmod_T;
+typedef struct file_buffer buf_T; /* forward declaration */
+
/*
* Simplistic hashing scheme to quickly locate the blocks in the used list.
* 64 blocks are found directly (64 * 4K = 256K, most files are smaller).
*/
#define MEMHASHSIZE 64
#define MEMHASH(nr) ((nr) & (MEMHASHSIZE - 1))
+#define MF_SEED_LEN 8
struct memfile
{
@@ -516,6 +519,16 @@ struct memfile
blocknr_T mf_infile_count; /* number of pages in the file */
unsigned mf_page_size; /* number of bytes in a page */
int mf_dirty; /* TRUE if there are dirty blocks */
+#ifdef FEAT_CRYPT
+ buf_T *mf_buffer; /* bufer this memfile is for */
+ char_u mf_seed[MF_SEED_LEN]; /* seed for encryption */
+
+ /* Values for key, method and seed used for reading data blocks when
+ * updating for a newly set key or method. Only when mf_old_key != NULL. */
+ char_u *mf_old_key;
+ int mf_old_cm;
+ char_u mf_old_seed[MF_SEED_LEN];
+#endif
};
/*
@@ -1229,8 +1242,6 @@ typedef struct {
* A buffer is new if the associated file has never been loaded yet.
*/
-typedef struct file_buffer buf_T;
-
struct file_buffer
{
memline_T b_ml; /* associated memline (also contains line
diff --git a/src/testdir/test72.in b/src/testdir/test72.in
index b6c6bfab3..ada2654b2 100644
--- a/src/testdir/test72.in
+++ b/src/testdir/test72.in
@@ -6,7 +6,7 @@ STARTTEST
:so small.vim
:"
:" Test 'undofile': first a simple one-line change.
-:set nocp ul=100 undofile
+:set nocp ul=100 undofile nomore
:e! Xtestfile
ggdGithis is one line:set ul=100
:s/one/ONE/
diff --git a/src/undo.c b/src/undo.c
index 07412acc5..45a6a2307 100644
--- a/src/undo.c
+++ b/src/undo.c
@@ -886,7 +886,10 @@ serialize_header(fp, buf, hash)
len = (int)fwrite(header, (size_t)header_len, (size_t)1, fp);
vim_free(header);
if (len != 1)
+ {
+ crypt_pop_state();
return FAIL;
+ }
}
else
#endif
@@ -1240,6 +1243,9 @@ u_write_undo(name, forceit, buf, hash)
struct stat st_old;
struct stat st_new;
#endif
+#ifdef FEAT_CRYPT
+ int do_crypt = FALSE;
+#endif
if (name == NULL)
{
@@ -1397,6 +1403,10 @@ u_write_undo(name, forceit, buf, hash)
*/
if (serialize_header(fp, buf, hash) == FAIL)
goto write_error;
+#ifdef FEAT_CRYPT
+ if (*buf->b_p_key)
+ do_crypt = TRUE;
+#endif
/*
* Iteratively serialize UHPs and their UEPs from the top down.
@@ -1462,6 +1472,10 @@ write_error:
#endif
theend:
+#ifdef FEAT_CRYPT
+ if (do_crypt)
+ crypt_pop_state();
+#endif
if (file_name != name)
vim_free(file_name);
}
@@ -1505,6 +1519,9 @@ u_read_undo(name, hash, orig_name)
struct stat st_orig;
struct stat st_undo;
#endif
+#ifdef FEAT_CRYPT
+ int do_decrypt = FALSE;
+#endif
if (name == NULL)
{
@@ -1572,6 +1589,7 @@ u_read_undo(name, hash, orig_name)
EMSG2(_("E826: Undo file decryption failed: %s"), file_name);
goto error;
}
+ do_decrypt = TRUE;
#else
EMSG2(_("E827: Undo file is encrypted: %s"), file_name);
goto error;
@@ -1776,6 +1794,10 @@ error:
}
theend:
+#ifdef FEAT_CRYPT
+ if (do_decrypt)
+ crypt_pop_state();
+#endif
if (fp != NULL)
fclose(fp);
if (file_name != name)
diff --git a/src/workshop.c b/src/workshop.c
index cd98914bc..334e6afff 100644
--- a/src/workshop.c
+++ b/src/workshop.c
@@ -1304,13 +1304,15 @@ load_window(
}
else
{
+#ifdef FEAT_WINDOWS
/* buf is in a window */
if (win != curwin)
{
win_enter(win, False);
- /* wsdebug("load_window: window endter %s\n",
+ /* wsdebug("load_window: window enter %s\n",
win->w_buffer->b_sfname); */
}
+#endif
if (lnum > 0 && win->w_cursor.lnum != lnum)
{
warp_to_pc(lnum);