http://www.perlmonks.org?node_id=588396


in reply to Re: maddening system call gzip/gunzip problems
in thread maddening system call gzip/gunzip problems

Although, gunzip reports that the file has no .gz extension it did before the script was run. That file was a gzip file before the script started. Similarly, the files are all unzipped before the gzip part is run. It seems that the script attempts to gunzip or gzip some files twice. I would love to rewrite all of the processing code for this script. Alas, time constraints do not permit (the rest of the code is very poor, undocumented, and not mine).

Neil Watson
watson-wilson.ca

  • Comment on Re^2: maddening system call gzip/gunzip problems

Replies are listed 'Best First'.
Re^3: maddening system call gzip/gunzip problems
by idsfa (Vicar) on Dec 07, 2006 at 17:31 UTC

    You are using readdir() to iterate over all of the files in the directory, but adding files to the directory inside of your loop. Remember that g(un)zip creates a new file to receive the output of the (de)compression. This is why things appear to be getting processed twice.


    The intelligent reader will judge for himself. Without examining the facts fully and fairly, there is no way of knowing whether vox populi is really vox dei, or merely vox asinorum. — Cyrus H. Gordon
      I know that system does fork but the parent waits. How can the script continue before the fork of gzip finishes clean up?

      Neil Watson
      watson-wilson.ca

        The problem is that opendir does not just take a snapshot of the current directory. Any files added to the directory after the call to opendir may or may not show up when you iterate over the entries with readdir

        So say you have a.gz,b.gz,c.gz in your directory. You do an opendir and then start iterating.

        The first entry returned will be a.gz, so you call gunzip. This will first create a file a, then remove a.gz.

        Depending on the filesystem, this new file a may now be returned by one of your following readdir calls, even though it was not in the directory when you did the opendir

        Here's a _possible_ way how this could be implemented internally.

        directory MYDIR block: 0: [a.gz] => inode x 1: [b.gz] => inode y 2: [c.gz] => inode z opendir pointer: MYDIR:0

        So a directory has a number of records consisting of a filename and an inode number. When you do an opendir the opendir code will remember what directory it's iterating over, as well as the position it's at. Then after you do a readdir, which returns a.gz, the pointer will point to item 1 instead of item 0.

        Now the gunzip happens, and creates the new file:

        directory MYDIR block: 0: [a.gz] => inode x 1: [b.gz] => inode y 2: [c.gz] => inode z 3: [a] => inode v opendir pointer: MYDIR:1

        Then the gunzip finishes and removes a.gz

        directory MYDIR block: 0: empty 1: [b.gz] => inode y 2: [c.gz] => inode z 3: [a] => inode v opendir pointer: MYDIR:1

        The next readdir will return b.gz and move the pointer to item 2. Then gzip creates a new file for that item..

        directory MYDIR block: 0: [b] => inode h 1: [b.gz] => inode y 2: [c.gz] => inode z 3: [a] => inode v opendir pointer: MYDIR:2

        And after removing the original...

        directory MYDIR block: 0: [b] => inode h 1: empty 2: [c.gz] => inode z 3: [a] => inode v opendir pointer: MYDIR:2

        The next readdir returns c.gz in the same fashion.

        After that however, readdir finds the new item a and returns it, which is what causes your problem. b and c are never returned cause they occupy positions that readdir already processed.

        All this is just a long way of explaining why readdir may or may not return files that get created or deleted while you are iterating over the directory.

        How can the script continue before the fork of gzip finishes clean up?

        Every successful system call in the loop modifies the contents of the directory, potentially changing what readdir will return on the next loop iteration.