O que acontece quando eu truncar um arquivo que está em uso?

6

Na Web, muitas pessoas dizem que você pode truncar um arquivo usando > filename ou truncate -s0 filename enquanto o arquivo é usado

Eu sei que toda vez que um processo escreve em um arquivo, o processo usa um deslocamento para escrever em um arquivo, fazendo um teste com um script como este.

#!/usr/bin/env python

import os, time

with open("passwd","w") as f: #copy of passwd file in my current directory
        f.seek(0)
        for x in xrange(1,1000):
                f.write("hello world \n" + time.ctime() + "\n")
                f.flush()
                time.sleep(2)

sempre que meu script faz um write syscall o deslocamento no campo /proc/pid_number/fdinfo/3 pos é alterado, mas quando eu tento truncar o arquivo usando o método listado acima, no meu arquivo eu vejo muitos caracteres como este ^@ quando eu abra o arquivo usando vim ou menos -u e o tipo de arquivo é alterado de ASCII text para data e quando eu uso ls -l filename o tamanho não é alterado

Então, quando truncar o arquivo, o deslocamento do arquivo não é reportado de volta, estou testando isso em Centos 7 e em Redhat 5 , então posso dizer que o tamanho do arquivo foi alterado enquanto o arquivo está em uso por processos não libera espaço e suja meu arquivo.

Então, minhas perguntas são, se meu processo tiver um arquivo aberto em pos 1000 e eu fiz truncate -s0 filename , se o truncamento funcionar, o que está acontecendo no próximo processo?

strace truncate -s0 passwd
open("passwd", O_WRONLY|O_CREAT|O_NONBLOCK, 0666) = 3
ftruncate(3, 0)                         = 0
close(3)                                = 0
close(1)                                = 0
close(2)                                = 0
exit_group(0)                           = ?

 ls -l passwd 
 -rw-rw-r--. 1 user91 users 13832 Feb 23 17:16 passwd

Como você pode ver, meu arquivo não foi truncado

Este problema não acontece se eu abrir o modo de anexação, por exemplo com este código.

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


int main(){
        int range = 1000;
        int x; x = open("passwd", O_WRONLY|O_CREAT|O_APPEND);
        int i = 0;
        for( i = 0; i <= range; range++)
                write(x,"hello world\n",12);
                sleep(2);
}
    
por c4f4t0r 23.02.2015 / 17:18

1 resposta

9

Note que, embora a chamada do sistema seja chamada de truncada, na verdade, é melhor interpretá-la como dizendo "Fazer com que o meu arquivo relate esse número de bytes em tamanho". De acordo com a manpage do sistema:

The truncate() and ftruncate() functions cause the regular file named by path or referenced by fd to be truncated to a size of precisely length bytes.

If the file previously was larger than this size, the extra data is lost. If the file previously was shorter, it is extended, and the extended part reads as null bytes ('

So my questions is, if my process has an opened file in pos 1000 and i did truncate -s0 filename, if the truncate works, what happening in the next process write?

').

Assim, pode-se truncar um arquivo e torná-lo maior, em vez de menor.

%bl0ck_qu0te%
  • Você truncou. O tamanho do arquivo nesse estágio é de 0 bytes. O deslocamento é de 1000.
  • A gravação na posição 1001 ocorre.
  • O tamanho do arquivo é de 1002 bytes. Bytes 0-1000 contêm '\ 0' (nulo). Os bytes 1001+ contêm dados gravados.

Quando você grava em um arquivo de uma posição maior que o próprio arquivo, os dados entre o final do arquivo e a nova gravação se tornam bytes nulos e os dados do arquivo entre esses dois pontos são referidos como esparsos .

De fato, você pode fazer o seguinte e produzir o mesmo efeito.

import os, sys

f = open('data.txt','w')
f.seek(1048576)
f.write('a')
f.flush()
f.close()

Você também mencionou que a abertura no modo de anexação evita esse comportamento. Isso é verdade porque você está instruindo o kernel nesse caso para "gravar no final real do arquivo toda vez". Se você truncar, o final do arquivo será alterado. Ao acrescentar, você não pode reposicionar seu ponteiro de arquivo.

Aqui está um exemplo de programa que demonstra o que acontece com o arquivo, os deslocamentos e os dados em um arquivo que foi truncado.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <err.h>

#define FPATH "/tmp/data.txt"
#define FSIZE 65536

int main() {
  int a,b;
  char buf[FSIZE];
  char byte;
  struct stat st;
  memset(buf, 'A', FSIZE);

  a = open(FPATH, O_WRONLY|O_CREAT);
  b = open(FPATH, O_RDONLY);

  if (a < 0 || b < 0)
    errx(EXIT_FAILURE, "Could not open file");

  printf("Writing %d * 'A' into file\n", FSIZE);
  /* Write some bytes */
  if(write(a, buf, FSIZE) != FSIZE)
    errx(EXIT_FAILURE, "Couldn't write complete size out");

  /* Seek to a  new position in the file */
  lseek(b, FSIZE/2, SEEK_SET);

  printf("Current position of handle 'a': %d\n", lseek(a, 0, SEEK_CUR));
  printf("Current position of handle 'b': %d\n", lseek(b, 0, SEEK_CUR));
  stat(FPATH, &st);
  printf("Reported size on filesystem of %s: %d\n", FPATH, st.st_size);

  /* OK -- now, read the byte at the position */
  if (read(b, &byte, 1) < 0)
    err(EXIT_FAILURE, "Could not read file");
  printf("Character at current position of handle 'b': '%c'\n", byte);

  /* Truncate the file in the 'a' handle */
  printf("Performing truncate...\n");
  if (ftruncate(a, 0) < 0)
    err(EXIT_FAILURE, "Cannot truncate file");

  printf("Current position of handle 'a': %d\n", lseek(a, 0, SEEK_CUR));
  printf("Current position of handle 'b': %d\n", lseek(b, 0, SEEK_CUR));
  stat(FPATH, &st);
  printf("Reported size on filesystem of %s: %d\n", FPATH, st.st_size);

  printf("Writing one byte via handle 'a'\n");
  if (write(a, buf, 1) < 0)
    err(EXIT_FAILURE, "Cannot perform second write");

  printf("Current position of handle 'a': %d\n", lseek(a, 0, SEEK_CUR));
  printf("Current position of handle 'b': %d\n", lseek(b, 0, SEEK_CUR));
  stat(FPATH, &st);
  printf("Reported size on filesystem of %s: %d\n", FPATH, st.st_size);

  if (read(b, &byte, 1) < 0)
    err(EXIT_FAILURE, "Could not read file");
  printf("Character at current position of handle 'b': '%c'\n", byte);


  close(a);
  close(b);
  exit(0);
}

Isso resulta na seguinte saída;

Writing 65536 * 'A' into file
Current position of handle 'a': 65536
Current position of handle 'b': 32768
Reported size on filesystem of /tmp/data.txt: 65536
Character at current position of handle 'b': 'A'
Performing truncate...
Current position of handle 'a': 65536
Current position of handle 'b': 32769
Reported size on filesystem of /tmp/data.txt: 0
Writing one byte via handle 'a'
Current position of handle 'a': 65537
Current position of handle 'b': 32769
Reported size on filesystem of /tmp/data.txt: 65537
Character at current position of handle 'b': ''
    
por 23.02.2015 / 20:18