Pages

Tuesday, January 8, 2013

Internals: Editing files using vim

http://stackoverflow.com/questions/607435/why-does-vim-save-files-with-a-extension/607474#607474
http://stackoverflow.com/questions/13563627/how-does-vim-save-files

When you want to modify a file, you have two options, each with its benefits and drawbacks.

1) You can overwrite the file in place. This does not use any extra space, and conserves the hard links, permissions and any other attribute beyond the content of the existing file. The major drawback of doing this is that if anything happens while the file is being written (the application crashes, or the power goes out), you end up with a partially written file.

2) You can write the new version of the file to a new file with a different name, then move it into place. This uses more space and breaks hard links, and if you have write permissions on a file but not on the directory it contains, you can't do it at all. On the flip side, the old version of the file is atomically replaced by the new version, so at every point in time the file name points to a valid, complete version of the file.

Advanced editors such as Vim or Emacs can choose between the two methods. When they use the first method, they normally make a backup file first, which can be recovered if the new file contents cannot be written correctly.

Classical vi overwrites the file in place. So the inode is unchanged.

In Vim, the choice is controlled by the backup, backupcopy and writebackup options. By default, Vim renames the old file, then writes a new file with the original name, if it thinks it can re-create the original file's attributes. If you want to reuse the existing inode (and so risk losing data, or waste more time making a backup copy), add set backupcopy yes to your .vimrc..

Read file block address from an inode:


#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/fs.h>
#include <linux/hdreg.h>
#include <assert.h>
#include <string.h>
#include <errno.h>

int main(int argc, char **argv) {
        int             fd,
                        i,
                        block,
                        blocksize,
                        bcount;
        struct stat     st;

        assert(argv[1] != NULL);

        assert(fd=open(argv[1], O_RDONLY));

        assert(ioctl(fd, FIGETBSZ, &blocksize) == 0);

        assert(!fstat(fd, &st));

        bcount = (st.st_size + blocksize - 1) / blocksize;

        printf("File: %s Size: %d Blocks: %d Blocksize: %d\n",
                argv[1], st.st_size, bcount, blocksize);

        for(i=0;i < bcount;i++) {
                block=i;
                if (ioctl(fd, FIBMAP, &block)) {
                        printf("FIBMAP ioctl failed - errno: %s\n",
                                        strerror(errno));
                }
                printf("%3d %10d\n", i, block);
        }

        close(fd);
}


Example:



root@ubuntu:/home/bruce/Desktop# vim sample
root@ubuntu:/home/bruce/Desktop# ls -i sample     
923616 sample
root@ubuntu:/home/bruce/Desktop# stat sample 
  File: `sample'
  Size: 6         Blocks: 8          IO Block: 4096   regular file
Device: 801h/2049d Inode: 923616      Links: 1
Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2013-01-08 22:17:19.632803587 -0500
Modify: 2013-01-08 22:17:18.608803500 -0500
Change: 2013-01-08 22:17:18.628803498 -0500
root@ubuntu:/home/bruce/Desktop# ./ff sample      
File: sample Size: 6 Blocks: 1 Blocksize: 4096
  0    1625587
root@ubuntu:/home/bruce/Desktop# vim sample     // Made some changes to the file
root@ubuntu:/home/bruce/Desktop# stat sample 
  File: `sample'
  Size: 11         Blocks: 8          IO Block: 4096   regular file
Device: 801h/2049d Inode: 923619      Links: 1
Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2013-01-08 22:17:56.564803245 -0500
Modify: 2013-01-08 22:17:55.548803207 -0500
Change: 2013-01-08 22:17:55.564803224 -0500
root@ubuntu:/home/bruce/Desktop# ls -i sample   // Inode changes
923619 sample
root@ubuntu:/home/bruce/Desktop# ./ff sample     // Block changes
File: sample Size: 11 Blocks: 1 Blocksize: 4096
  0    1625597
root@ubuntu:/home/bruce/Desktop# vim sample 
root@ubuntu:/home/bruce/Desktop# ./ff sample 
File: sample Size: 6256 Blocks: 2 Blocksize: 4096
  0    3847685
  1    3847686
root@ubuntu:/home/bruce/Desktop# ls -i sample   // Inode changes again
923616 sample
root@ubuntu:/home/bruce/Desktop# vim sample  // Made some more changes
root@ubuntu:/home/bruce/Desktop# ls -i sample  // Inode changes
923619 sample
root@ubuntu:/home/bruce/Desktop# ./ff sample    // Blocks change
File: sample Size: 6255 Blocks: 2 Blocksize: 4096
  0    3847687
  1    3847688
root@ubuntu:/home/bruce/Desktop# ln sample sample1
root@ubuntu:/home/bruce/Desktop# vim sample  // Made changes to file
root@ubuntu:/home/bruce/Desktop# ls -i sample  // Inode does not change
923619 sample
root@ubuntu:/home/bruce/Desktop# ./ff sample   // Blocks change
File: sample Size: 6254 Blocks: 2 Blocksize: 4096
  0    3847689
  1    3847690
root@ubuntu:/home/bruce/Desktop# unlink sample1
root@ubuntu:/home/bruce/Desktop# ls -i sample
923619 sample
root@ubuntu:/home/bruce/Desktop# vim sample   // Made changes to file
root@ubuntu:/home/bruce/Desktop# ls -i sample   // Inode changes
923969 sample
root@ubuntu:/home/bruce/Desktop# ./ff sample     // Blocks change
File: sample Size: 225 Blocks: 1 Blocksize: 4096
  0    3847700
root@ubuntu:/home/bruce/Desktop# unlink sample 


Another example:


[bruce@opus testing]$ ll
total 34
-rw-r--r--. 1 bruce grad 12 Jan  8 23:38 sample
[bruce@opus testing]$ ls -i sample
4785234842 sample
[bruce@opus testing]$ cat sample
Hello World
[bruce@opus testing]$ vim sample
[bruce@opus testing]$ ls -i sample   // Inode changes
4785726686 sample
[bruce@opus testing]$ cat sample
Hllo World
[bruce@opus testing]$ ll
total 34
-rw-r--r--. 1 bruce grad 11 Jan  8 23:40 sample
[bruce@opus testing]$ vim sample   // Now I do :set backup and :set writebackup in vim before exiting
[bruce@opus testing]$ ll
total 68
-rw-r--r--. 1 bruce grad 10 Jan  8 23:41 sample
-rw-r--r--. 1 bruce grad 11 Jan  8 23:40 sample~       // A backup file is created
[bruce@opus testing]$ cat sample
Hlo World
[bruce@opus testing]$ cat sample~
Hllo World
[bruce@opus testing]$ ls -i sample   // Inode changes
4785815659 sample
[bruce@opus testing]$ ls -i sample~ // Same as old file
4785726686 sample~
[bruce@opus testing]$ rm sample~
rm: remove regular file `sample~'? y
[bruce@opus testing]$ vim sample  // This time I do :set backupcopy=yes (auto default) before exiting
[bruce@opus testing]$ cat sample
Ho World
[bruce@opus testing]$ ls -i sample  // Inode does not change. The original file is overwritten.
4785815659 sample
[bruce@opus testing]$ ll            // No backups also
total 34
-rw-r--r--. 1 bruce grad 12 Jan  8 23:38 sample

[bruce@opus testing]$ vim sample

[bruce@opus testing]$ ls -i sample  // Inode changes
4784511684 sample
[bruce@opus testing]$ cat sample
H World

In the above example the new blocks are allocated after each edit.


No comments:

Post a Comment