1

I am learning, on how to create holes in files using lseek.

This is the code that I have written thus far...

#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <string.h>

int main()
{
    int fd;
    char name[20] = "Harry Potter";

    // Creating a file
    if( (fd = open( "book.txt", O_RDWR | O_CREAT , S_IWRITE | S_IREAD ) < 0 )) {
        printf("\ncreat error");    
    }

    // Seeking 100th byte, from the begining of the file
    if ( lseek(fd, 100, SEEK_SET) == -1 ) {
        if (errno != 0) {
            perror("lseek");
        } 
    }

    // Writing to the 100th byte, thereby creating a hole
    if( write(fd, name, sizeof(char)*strlen(name)) != sizeof(char)*strlen(name) ) {
        if (errno != 0) {
            perror("write");
        }
    }

    // closing the file
    if ( close(fd) == -1 ) {
        if (errno != 0)
            perror("close"); 
    }

    return 0;
}

and when I compile and execute this code I get an lseek error and also the name 'Harry Potter' is not being inserted into the file. This is the output when I execute the above code :

lseek: Illegal seek
Harry Potter

I am even trying to catch all errors. Kindly help me further.

Shao
  • 537
  • 3
  • 7
Swaroop
  • 1,219
  • 3
  • 16
  • 32
  • 1
    as far as I know, you cannot lseek over the end of the file, if you want to make an hole , you should fwrite 0 bytes... an "hole" is something a file system does not consider valid ... ;) – Leonardo Bernardini Aug 14 '14 at 10:45
  • 1
    open, lseek, write, close functions are POSIX (not in standard C), so you might add that tag. – Shao Aug 14 '14 at 10:46
  • 3
    @LeonardoBernardini Nothing that you wrote is true except the "as far as I know" part -- consider that you *don't* know, and haven't bothered to find out ... simply reading the lseek man page would tell yout that one creates holes in files by seeking past written data, just as the OP is trying to do ... but there's a little syntax mistake; see my answer. – Jim Balter Aug 14 '14 at 11:19
  • my bad and apologises, I was almost sure this wasn't doable... – Leonardo Bernardini Aug 14 '14 at 12:40
  • @Shao: They are supported in Windows C too, with minor changes to the headers. – david.pfx Aug 14 '14 at 14:03
  • @david.pfx does Windows C lseek create file with hole? – Ernest Sep 20 '18 at 02:09
  • @Ernest: Files do not have holes - you can always read every byte to EOF even if they were never written. Windows _lseek allows seek past EOF. Windows also has sparse files, in which some disk blocks are not allocated (but still readable). – david.pfx Sep 21 '18 at 03:54

1 Answers1

3
if( (fd = open( "book.txt", O_RDWR | O_CREAT , S_IWRITE | S_IREAD ) < 0 )) {

This sets fd to 0 if the open succeeds and 1 if it fails. Because you set it to 0, which is your console, that's where it wrote "Harry Potter", rather than to the disk. And you can't lseek on a terminal. You want

if( (fd = open( "book.txt", O_RDWR | O_CREAT , S_IWRITE | S_IREAD )) < 0 ) {

Also

a) There's no need to check that errno != 0 after a system call fails.

b) You should exit upon error rather than falling through.

c) sizeof(char) is always 1 so there's no need to multiply by it.

d) main should have a prototype, e.g., int main(void)

Jim Balter
  • 16,163
  • 3
  • 43
  • 66
  • Well spotted. I guess that's why some compilers warn about embedded assignments... – david.pfx Aug 14 '14 at 11:42
  • @david.pfx Not hard for me to spot ... as soon as I saw that Harry Potter came out on the terminal, I was 99% certain I would find that precedence mistake. And the compiler the OP is using will issue such warnings, if only the OP would ask it to (e.g., gcc -Wall). – Jim Balter Aug 14 '14 at 11:44
  • Interestingly 0 is the file descriptor for standard input, not output. On Windows that triggers an error. – david.pfx Aug 14 '14 at 14:37
  • @david.pfx Yes, it's counterintuitive to see output on fd 0, but IIRC from ancient days of UNIX programming, terminals are opened read/write and then the fd is dup'ed to yield 0, 1, and 2. This allows sending prompts to stdin even if stdout and stderr are redirected (these days there's /dev/tty for that). – Jim Balter Aug 14 '14 at 19:56
  • @JimBalter Thanks. I will also remember the additional 4 points which were mentioned. I have the practice of checking for the size of any type, so thats why I did sizeof(char). I did this because I was thinking sizeof(char) may be different on different systems. – Swaroop Aug 15 '14 at 09:50
  • @GuruSwaroop It's a good idea to be type-independent when the type of an object might vary, but sizeof(char)*strlen is quite unnecessary. The C standard mandates (by implication, but it has been firmly established) that sizeof(char) is always 1 ... this does *not* mean that a char is always 8 bits; e.g., a system can have 16-bit chars, but then one "byte" must be 16 bits. – Jim Balter Aug 15 '14 at 10:22