3

I have no idea how swap files are formatted, but I'm wondering if the information in them can be directly examined somehow.

I notice that trying to do this indirectly, by creating an empty file with the same name and/or path and loading it, works, which implies the entire content of the original is in the swap.

According to EvergreenTree's comment below, it also contains meta information such as current process ID, modification timestamps, etc.

3
  • What are you trying to do? (XY Problem)
    – mMontu
    Oct 16, 2015 at 11:31
  • @mMontu I don't have an X, lol. I'm just curious. I was wondering about it yesturday because of my previous question, and surprised when I looked around that no one has anything to say about this. It could be that the only real information in there is the file content, in which case this is a bit pointless -- but then so would be the beefy binary format.
    – goldilocks
    Oct 16, 2015 at 11:41
  • 1
    Swap files do store more information than just the file itself, it stores the user and host name of who last modified it, when it was last modified, and the process ID of the instance of vim that is/was editing that file. Oct 16, 2015 at 12:19

2 Answers 2

3

You can find information about the format from the source.

NB! Swapfiles are “binary” files with for example fixed offsets for various data. For example at byte number N in the file it expect to find specific data. In other words: one can look at, but not modify, unless one know what one are doing. I.e. one can change User but have to be aware of padding bytes etc.

It starts with a block named block0 which holds basic information, see below.

The rest of the file is copies of memory blocks in a tree structure. See description in top of source file.

The base information could easily be extracted. I.e:

$ ./vimswap .foo.swp 
b0_id       6230
Version     VIM 8.0
User        goldilocks
Host        HouseOfCorrection
File        ~goldilocks/devel/foo
inode       2230823
PID         12066
Pagesize    4096
mtime       1572209814
mtime_h     2019-10-27 21:56:54

by something like:

(char_to_long (modified), struct block0 and constants copied form source-code.)

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

typedef unsigned char char_u;

#define B0_FNAME_SIZE_ORG   900 /* what it was in older versions */
#define B0_UNAME_SIZE       40
#define B0_HNAME_SIZE       40

struct block0 {
    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 */
    char_u  b0_ino[4];      /* inode of b0_fname */
    char_u  b0_pid[4];      /* process id of creator (or 0) */
    char_u  b0_uname[B0_UNAME_SIZE]; /* name of user (uid if no name) */
    char_u  b0_hname[B0_HNAME_SIZE]; /* host name (if it has a name) */
    char_u  b0_fname[B0_FNAME_SIZE_ORG]; /* name of file being edited */
    long    b0_magic_long;  /* check for byte order of long */
    int     b0_magic_int;   /* check for byte order of int */
    short   b0_magic_short; /* check for byte order of short */
    char_u  b0_magic_char;  /* check for last char */
};

static long char_to_long(char_u *s) {
    return s[3] << 24 | s[2] << 16 | s[1] << 8 | s[0];
}

char *t_str(long ts, char res[32]) {
    struct tm lt;
    lt = *localtime(&ts);
    strftime(res, 32, "%F %T", &lt);
    return res;
}

void dump_b0(struct block0 b0) {
    int wh = -8;
    char res[32];
    long mtime = char_to_long(b0.b0_mtime);
    /* TAB's added to make for easy shell-parsing */
    fprintf(stdout,
        "%*s\t%02x%02x\n"
        "%*s\t%s\n"

        "%*s\t%s\n"
        "%*s\t%s\n"
        "%*s\t%s\n"
        "%*s\t%ld\n"
        "%*s\t%ld\n"

        "%*s\t%ld\n"
        "%*s\t%ld\n"
        "%*s\t%s\n"
        ,
        wh, "b0_id", b0.b0_id[0], b0.b0_id[1],
        wh, "Version", b0.b0_version,

        wh, "User", b0.b0_uname,
        wh, "Host", b0.b0_hname,
        wh, "File", b0.b0_fname,
        wh, "inode", char_to_long(b0.b0_ino),
        wh, "PID", char_to_long(b0.b0_pid),

        wh, "Pagesize", char_to_long(b0.b0_page_size),
        wh, "mtime", mtime,
        wh, "mtime_h", t_str(mtime, res)
    );
}

int read_block0(char *fn, struct block0 *b0) {
    size_t sz_str = sizeof *b0;
    size_t b_read;
    FILE *fh;

    fh = fopen(fn, "rb");
    if (fh == NULL) {
        fprintf(stderr, "Unable to open `%s'\n", fn);
        return 1;
    }
    b_read = fread(b0, 1, sz_str, fh);
    fclose(fh);
    return b_read != sz_str;
}

int dump_block0(char *fn) {
    struct block0 b0 = {0};
    if (read_block0(fn, &b0) != 0)
        return 1;

    dump_b0(b0);

    return 0;
}

int main(int argc, char *argv[])
{
    int ec = 0;

    if (argc != 2) {
        fprintf(stderr, "Usage: %s SWAP_FILE\n", argv[0]);
        ec = 1;
    } else {
        ec = dump_block0(argv[1]);
    }
    return ec;
}
6
  • If I type something in a txt file, that specific row isn't saved in a predictabile position inside the corresponding swp. It's not the first or the last... Can you help with this? thanks
    – gianni
    Oct 1, 2022 at 18:30
  • 1
    @gianni Long time since I looked at this. The swap file is a linked list (tree) of memory backed by swap file. You can traverse it trough pointer blocks. You also have to account for sync (I.e. swp file is not necessarily synced to disk when you look at the file (See :h swapsync)) and general intro to structure: github.com/vim/vim/blob/… Oct 1, 2022 at 19:13
  • 1
    @gianni No ida what you are trying to achieve but a Vim script would perhaps be easier. Oct 1, 2022 at 19:16
  • 1
    @gianni A linked list in this context is simply a list of memory addresses. E.g.. you start with address 0x123. It has a header with various information, amongst others where next "memory chunk / block" is say 0x128. Go to 0x128 and read and it say next is at 0x964 read that etc. etc. To expand on this one can have a binary tree where one can go left or right. (Typically used for faster traversal / lookup or what ever). github.com/vim/vim/blob/… en.wikipedia.org/wiki/Linked_list en.wikipedia.org/wiki/Binary_tree Oct 1, 2022 at 19:32
  • 1
    You could use a Vim script to write current line to a fifo or what ever. Would likely be a lot easier. Have done something similar earlier, except I posted data to a script (i.e. no fifo or tmp file) that in turn communicated with a service for updating browser when working on webpages. (But long ago and no idea where the code is). Oct 1, 2022 at 19:35
1

You can directly examine the actual bit-level file contents of any file using hexdump -C.

The -C flag isn't strictly necessary, but it will display an ASCII view of the file side by side with the hexdump, which makes it nice and easy to spot text strings.

I don't know of any better way to examine a non-text file than that.

2

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.