↩️

Linuxなら一時ファイルはunlinkで実現可能

に公開

一時ファイルを実現する方法としてunlinkを使った方法がある。

openでファイルディスクリプタを取得した状態でunlinkすると、ファイルディスクリプタをcloseしたタイミングでファイルの削除されるのだ。

unlinkはOSが持っているファイルへの参照を消すイメージで、そのファイルへの参照カウントが1減る。すでにopenしていた場合は、そのファイルディスクリプタが最後の参照になり、closeでファイルの参照カウントが0になるとファイルは削除される。

以下、実験を行った。unlinkしておいた一時ファイルは自動的に消去されていることがわかる。

$ ls 
Makefile  main.c
$ make
gcc main.c -g -o unlink-technique
$ ./unlink-technique 
> touch(./not_unlink.tmp) < './not_unlink.tmp'
> fd = open(./not_unlink.tmp)
// > unlink(./not_unlink.tmp)
> content = read(fd)
> print(content)
./not_unlink.tmp
> is_exist(./not_unlink.tmp)
YES

> touch(./unlinked.tmp) < './unlinked.tmp'
> fd = open(./unlinked.tmp)
> unlink(./unlinked.tmp)
> content = read(fd)
> print(content)
./unlinked.tmp
> is_exist(./unlinked.tmp)
NO

$ ls
Makefile  main.c  not_unlink.tmp  unlink-technique
main.c
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

void touch_and_write(const char *file, const char *content) {
  int fd = open(file, O_RDWR | O_CREAT, 0644);
  if (fd < 0) {
    perror("open");
    exit(1);
  }

  write(fd, content, strlen(content));

  close(fd);
}

int main() {
  const char *tmpfiles[] = {"./not_unlink.tmp", "./unlinked.tmp"};

  for (int i = 0; i < sizeof(tmpfiles) / sizeof(*tmpfiles); ++i) {
    const char *tmp = tmpfiles[i];
    const char do_unlink = (i == 1);

    touch_and_write(tmp, tmp);
    printf("> touch(%s) < '%s'\n", tmp, tmp);

    int fd = open(tmp, O_RDONLY);
    if (fd < 0) {
      perror("open");
      exit(1);
    }
    printf("> fd = open(%s)\n", tmp);

    if (do_unlink) {
      char c;

      unlink(tmp);
      printf("> unlink(%s)\n", tmp);
    } else {
      printf("// > unlink(%s)\n", tmp);
    }

    char buffer[1024];
    int size = read(fd, buffer, sizeof(buffer) - 1);
    if (size < 0) {
      perror("read");
      close(fd);
      exit(1);
    }
    buffer[size] = '\0';
    printf("> content = read(fd)\n");

    close(fd);

    printf("> print(content)\n");
    printf("%s\n", buffer);
    printf("> is_exist(%s)\n", tmp);
    printf("%s\n", access(tmp, F_OK) == 0 ? "YES" : "NO");
    printf("\n");
  }
}

Discussion