GNU/Linux >> Tutoriales Linux >  >> Linux

¿Por qué se escriben datos en un archivo abierto con el indicador O_APPEND, siempre se escriben al final, incluso con `lseek`?

Cuando abres un archivo con O_APPEND , todos los datos se escriben hasta el final, independientemente del puntero del archivo actual desde la última llamada a lseek(2) o la última operación de lectura/escritura. Desde el open(2) documentación:

O_APPEND
El archivo se abre en modo de adición. Antes de cada write(2) , el desplazamiento del archivo se coloca al final del archivo, como si tuviera lseek(2) .

Si desea escribir datos al final del archivo y luego al principio, ábralo sin O_APPEND , usa fstat(2) para obtener el tamaño del archivo (st_size miembro dentro de struct stat ), y luego busca ese desplazamiento para escribir el final.


En efecto, O_APPEND solo afecta el comportamiento de write , pero no la de read . Independientemente de cómo se cambie la posición actual de un archivo por lseek , write siempre será append-only .

Cuando open un archivo con O_RDWR | O_APPEND , read aún comenzará desde el principio del archivo.

En el manual de open (man 2 open ),

O_APPENDEl archivo se abre en modo de adición. Antes de cada escribir (2), el desplazamiento del archivo se coloca al final del archivo.

En el manual de write (man 2 write ),

Si se establece el indicador O_APPEND de los indicadores de estado del archivo, el desplazamiento del archivo se establecerá al final del archivo antes de cada escritura .

En el kernel de Linux fs/ext4 syscall write -> vfs_write -> ext4_file_write_iter ,el ext4_file_write_iter llamará al ext4_write_checks

luego llama al generic_write_checks

encontrará el lugar donde configurar el pos =file.size

/* FIXME: this is for backwards compatibility with 2.4 */
if (iocb->ki_flags & IOCB_APPEND)
    iocb->ki_pos = i_size_read(inode);
pos = iocb->ki_pos;

La siguiente demostración puede verificarlo.

cat open_append.cc
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>

#include <string>
#include <iostream>

int main(int argc, char *argv[]) {
  std::string path = "./test.txt";
  std::string content = "hello_world";
  std::string read_buf(content.size(), 0x0);
  struct stat st_buf;
  ssize_t bytes_read = -1;
  ssize_t bytes_write = -1;
  int ret = -1;
  off_t cur_off = -1;
  int fd = ::open(path.c_str(), O_CREAT | O_RDWR | O_TRUNC, 0644);
  if (fd < 0) {
    std::cerr << "open err path " << path
              << " errno " << errno << std::endl;
    return -1;
  }
  std::cout << "open ok path " << path
            << " fd " << fd << std::endl;

  // Step 1 write some data into an empty file
  bytes_write = ::write(fd, content.data(), content.size());
  if (bytes_write < 0) {
    std::cerr << "write err fd " << fd
              << " errno " << errno << std::endl;
    goto out;
  }
  std::cout << "write ok fd " << fd
            << " data " << content
            << " nbytes " << bytes_write << std::endl;
  ::close(fd);

  // Step 2 open the file again with O_APPEND
  fd = -1;
  fd = ::open(path.c_str(), O_CREAT | O_RDWR | O_APPEND, 0644);
  if (fd < 0) {
    std::cerr << "open again err path " << path
              << " errno " << errno << std::endl;
    return -1;
  }
  std::cout << "open again ok path " << path
            << " fd " << fd << std::endl;

  // Step 3 the current position of the file NOT affected by O_APPEND
  cur_off = ::lseek(fd, 0, SEEK_CUR);
  if (cur_off < 0) {
    std::cerr << "lseek err SEEK_CUR fd " << fd
              << " errno " << errno << std::endl;
    goto out;
  }
  // cur_off expected to be 0
  std::cout << "lseek ok SEEK_CUR fd " << fd
            << " cur_off " << cur_off << std::endl;

  // Step 4  the read will start from the beginning of the file
  bytes_read = read(fd, (char*)read_buf.data(), content.size());
  if (bytes_read < 0) {
    std::cerr << "read err fd " << fd
              << " errno " << errno << std::endl;
    goto out;
  }
  std::cout << "read ok fd " << fd
            << " data " << read_buf
            << " nbytes " << bytes_read << std::endl;

  // Step 5 change the position to the half of the file size
  cur_off = ::lseek(fd, content.size() / 2, SEEK_SET);
  if (cur_off < 0) {
    std::cerr << "lseek err SEEK_SET fd " << fd
              << " errno " << errno << std::endl;
    goto out;
  }
  // cur_off expected to be content.size() / 2
  std::cout << "lseek ok SEEK_SET fd " << fd
            << " cur_off " << cur_off << std::endl;

  // Step 6 write will append data from the end of the file
  // the current position is ignored
  bytes_write = ::write(fd, content.data(), content.size());
  if (bytes_write < 0) {
    std::cerr << "append write err fd " << fd
              << " errno " << errno << std::endl;
    goto out;
  }
  std::cout << "append write ok fd " << fd
            << " append data " << content
            << " append nbytes " << bytes_write << std::endl;

  // Step 7 the file size is double content.size()
  memset((void*)&st_buf, 0x0, sizeof(struct stat));
  ret = lstat(path.c_str(), &st_buf);
  if (ret < 0) {
    std::cerr << "lstat err path " << path
              << " errno " << errno << std::endl;
    goto out;
  }
  std::cout << "lstat ok path " << path
            << " st_size " << st_buf.st_size << std::endl;
  ret = 0;

out:
  if (fd >= 0) {
    close(fd);
  }
  return ret;
}

Resultado de salida

open ok path ./test.txt fd 3
write ok fd 3 data hello_world nbytes 11
open again ok path ./test.txt fd 3
lseek ok SEEK_CUR fd 3 cur_off 0
read ok fd 3 data hello_world nbytes 11
lseek ok SEEK_SET fd 3 cur_off 5
append write ok fd 3 append data hello_world append nbytes 11
lstat ok path ./test.txt st_size 22

Linux
  1. Primeros pasos con el comando tac de Linux

  2. ¿Cómo agregar una nueva línea al final de un archivo?

  3. ¿Por qué falla el apagado de net rpc con las credenciales correctas?

  4. ¿Por qué se permite incluso el terrible 'rm -rf /'?

  5. Quiero cambiar DPI con ImageMagick sin cambiar el tamaño de byte real de los datos de la imagen

Encuentra archivos y directorios en Linux con el comando de búsqueda

Lea y escriba datos desde cualquier lugar con redirección en la terminal de Linux

Cree un escritorio retro de Apple con Linux MLVWM

Asegure Linux con el archivo Sudoers

¿Qué significa un + al final de los permisos de ls -l?

¿Determinar si el archivo está en proceso de escritura?