Thư mục tạm thời tự động bị hủy sau khi thoát quá trình


Câu trả lời:


12

Trong trường hợp tệp tạm thời, ví dụ của bạn trong câu hỏi sẽ tạo tệp đó, sau đó hủy liên kết tệp khỏi thư mục (làm cho nó "biến mất") và khi tập lệnh đóng tệp filedescriptor (có thể là khi chấm dứt), khoảng trống được lấy bởi tệp sẽ được hệ thống thu hồi. Đây là một cách phổ biến để xử lý các tệp tạm thời bằng các ngôn ngữ như C.

Theo như tôi biết, không thể mở một thư mục theo cùng một cách, ít nhất là không theo bất kỳ cách nào có thể làm cho thư mục có thể sử dụng được.

Một cách phổ biến để xóa các tệp và thư mục tạm thời khi chấm dứt tập lệnh là cài đặt EXITbẫy dọn dẹp . Các ví dụ mã được đưa ra dưới đây để tránh phải hoàn toàn tung hứng các bộ lọc.

tmpdir=$(mktemp -d)
tmpfile=$(mktemp)

trap 'rm -f "$tmpfile"; rm -rf "$tmpdir"' EXIT

# The rest of the script goes here.

Hoặc bạn có thể gọi một chức năng dọn dẹp:

cleanup () {
    rm -f "$tmpfile"
    rm -rf "$tmpdir"
}

tmpdir=$(mktemp -d)
tmpfile=$(mktemp)

trap cleanup EXIT

# The rest of the script goes here.

Các EXITbẫy sẽ không được thực hiện sau khi nhận được KILLtín hiệu (mà không thể bị mắc kẹt), có nghĩa là sẽ không có dọn dẹp biểu diễn sau đó. Tuy nhiên nó sẽ thực thi khi chấm dứt do một INThoặc TERMtín hiệu (nếu chạy với bashhoặc ksh, trong vỏ khác mà bạn có thể muốn thêm những tín hiệu sau EXITtrong trapdòng lệnh), hoặc khi thoát bình thường do đến ở phần cuối của kịch bản hoặc thực hiện một exitgọi.


5
Nó không chỉ là shell mà không thể sử dụng các thư mục tạm thời đã được liên kết - các chương trình C cũng không thể. Vấn đề là các thư mục không liên kết không thể có tệp trong đó. Bạn có thể có một thư mục trống chưa được liên kết làm thư mục làm việc của mình, nhưng mọi nỗ lực tạo tệp sẽ gây ra lỗi.
derobert

1
@derobert Và một thư mục không liên kết như vậy thậm chí không có ...các mục. (Đã thử nghiệm trên Linux, tôi không biết liệu điều đó có nhất quán trên các nền tảng hay không.)
kasperd


1
Lưu ý rằng bẫy EXIT không được thực thi nếu tập lệnh gọi exec another-commandrõ ràng.
Stéphane Chazelas


6

Viết hàm shell sẽ được thực thi khi tập lệnh của bạn nếu kết thúc. Trong ví dụ dưới đây, tôi gọi nó là 'dọn dẹp' và đặt bẫy để được thực thi ở các mức thoát, như: 0 1 2 3 6

trap cleanup 0 1 2 3 6

cleanup()
{
  [ -d $TMP ] && rm -rf $TMP
}

Xem bài đăng này để biết thêm.


Đó không phải là "mức thoát" mà là số tín hiệu và câu trả lời cho câu hỏi bạn đang liên kết để giải thích điều đó. Bẫy sẽ chạy cleanuptrước lối thoát sạch (0) và khi nhận SIGHUP (1), SIGINT (2), SIGQUIT (3) và SIGABRT (6). nó sẽ không chạy cleanupkhi tập lệnh thoát vì SIGTERM, SIGSEGV, SIGKILL, SIGPIPE, v.v ... Điều này rõ ràng là thiếu.
mosvy

6

Bạn có thể chdir vào nó và sau đó loại bỏ nó, với điều kiện bạn không cố gắng sử dụng các đường dẫn bên trong nó sau đó:

#! /bin/sh
dir=`mktemp -d`
cd "$dir"
exec 4>file 3<file
rm -fr "$dir"

echo yes >&4    # OK
cat <&3         # OK

cat file        # FAIL
echo yes > file # FAIL

Tôi chưa kiểm tra, nhưng rất có thể đó là vấn đề tương tự khi sử dụng openat (2) trong C với một thư mục không còn tồn tại trong hệ thống tệp.

Nếu bạn đã root và trên Linux, bạn có thể chơi với một không gian tên riêng biệt và mount -t tmpfs tmpfs /dirbên trong nó.

Các câu trả lời chính tắc (đặt bẫy trên EXIT) sẽ không hoạt động nếu tập lệnh của bạn bị buộc vào một lối thoát ô uế (ví dụ: với SIGKILL); có thể để lại dữ liệu nhạy cảm treo xung quanh.

Cập nhật:

Đây là một tiện ích nhỏ thực hiện cách tiếp cận không gian tên. Nó nên được biên dịch với

cc -Wall -Os -s chtmp.c -o chtmp

và cho CAP_SYS_ADMINkhả năng tập tin (như root) với

setcap CAP_SYS_ADMIN+ep chtmp

Khi chạy (như một người dùng bình thường) như

./chtmp command args ...

nó sẽ bỏ chia sẻ không gian tên hệ thống tập tin của nó, gắn hệ thống tập tin tmpfs trên /proc/sysvipc, chdir vào nó và chạy commandvới các đối số đã cho. commandsẽ không kế thừa các CAP_SYS_ADMINkhả năng.

Hệ thống tập tin đó sẽ không thể truy cập được từ một quá trình khác không được bắt đầu commandvà nó sẽ biến mất một cách kỳ diệu (với tất cả các tệp được tạo bên trong nó) khi commandvà con của nó chết, bất kể điều đó xảy ra như thế nào. Lưu ý rằng đây chỉ là không giới hạn không gian tên gắn kết - không có rào cản cứng giữa commandvà các quy trình khác được điều hành bởi cùng một người dùng; họ vẫn có thể lẻn vào trong không gian tên của nó hoặc thông qua ptrace(2), /proc/PID/cwdhoặc bằng cách khác.

Tất nhiên, việc chiếm quyền điều khiển "vô dụng" /proc/sysvipclà ngớ ngẩn, nhưng giải pháp thay thế sẽ là spam /tmpvới các thư mục trống sẽ phải được gỡ bỏ hoặc làm phức tạp đáng kể chương trình nhỏ này bằng dĩa và chờ đợi. Ngoài ra, dircó thể được thay đổi thành ví dụ. /mnt/chtmpvà có nó được tạo bởi root khi cài đặt; không làm cho nó có thể được cấu hình bởi người dùng và không đặt nó thành một đường dẫn thuộc sở hữu của người dùng vì điều đó có thể khiến bạn gặp bẫy liên kết và các công cụ lông khác không đáng để dành thời gian.

chtmp.c

#define _GNU_SOURCE
#include <err.h>
#include <sched.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/mount.h>
int main(int argc, char **argv){
        char *dir = "/proc/sysvipc";    /* LOL */
        if(argc < 2 || !argv[1]) errx(1, "usage: %s prog args ...", *argv);
        argv++;
        if(unshare(CLONE_NEWNS)) err(1, "unshare(CLONE_NEWNS)");
        /* "modern" systemd remounts all mount points MS_SHARED
           see the NOTES in mount_namespaces(7); YUCK */
        if(mount("none", "/", 0, MS_REC|MS_PRIVATE, 0))
                err(1, "mount(/, MS_REC|MS_PRIVATE)");
        if(mount("tmpfs", dir, "tmpfs", 0, 0)) err(1, "mount(tmpfs, %s)", dir);
        if(chdir(dir)) err(1, "chdir %s", dir);
        execvp(*argv, argv);
        err(1, "execvp %s", *argv);
}

1
Ngay cả khi bạn không root, bạn có thể thực hiện việc này với các không gian tên bằng cách tạo một không gian tên người dùng mới và thực hiện việc gắn tmpfs bên trong nó. Truy cập buôn lậu vào thư mục mới ra thế giới bên ngoài là một chút khó khăn nhưng nên có thể.
R .. GitHub DỪNG GIÚP ICE

Điều đó vẫn đòi hỏi CAP_SYS_ADMIN. Tôi có ý tưởng về một tiện ích kích hoạt setcap nhỏ sẽ làm điều đó, tôi sẽ cập nhật câu trả lời với nó.
qubert

1
Trừ khi kernel đã bị khóa để không cho phép nó, việc tạo không gian tên người dùng không phải là một hoạt động đặc quyền. Thiết kế cơ bản sao cho an toàn khi cho phép người dùng bình thường làm mà không cần bất kỳ khả năng đặc biệt nào. Tuy nhiên, có đủ khả năng tấn công bề mặt / rủi ro mà nhiều distro vô hiệu hóa nó, tôi nghĩ vậy.
R .. GitHub DỪNG GIÚP ICE

Tôi đã thử trong thiết bị đầu cuối. Trong một số thư mục tạm thời, rm $PWDcông việc, shell vẫn nằm trong thư mục đó. Nhưng không có tập tin mới nào có thể được đưa vào "thư mục" này. Chỉ bạn có thể làm là đọc / ghi với tệp & 3, & 4. Vì vậy, đây vẫn là "tệp tạm thời", không phải "thư mục tạm thời".
Bob Johnson

@BobJohnson Điều đó không khác với những gì tôi đã nói trong câu trả lời của mình ;-)
qubert

0

Bạn có yêu cầu một vỏ cụ thể?

Nếu zsh là một tùy chọn, vui lòng đọc zshexpn(1):

Nếu = (...) được sử dụng thay vì <(...), thì tệp được truyền dưới dạng đối số sẽ là tên của tệp tạm thời chứa đầu ra của quy trình danh sách. Điều này có thể được sử dụng thay vì <mẫu cho một chương trình dự kiến lseek(xem lseek(2)) trên tệp đầu vào.

[...]

Một vấn đề khác phát sinh bất cứ khi nào một công việc thay thế yêu cầu một tệp tạm thời bị từ chối bởi shell, bao gồm cả trường hợp &!hoặc &|xuất hiện ở cuối lệnh có chứa thay thế. Trong trường hợp đó, tệp tạm thời sẽ không được dọn sạch vì shell không còn bất kỳ bộ nhớ nào của công việc. Một cách giải quyết là sử dụng một subshell, ví dụ,

(mycmd =(myoutput)) &!

vì subshell rẽ nhánh sẽ đợi lệnh kết thúc rồi gỡ tệp tạm thời.

Một cách giải quyết chung để đảm bảo quá trình thay thế quá trình tồn tại trong một khoảng thời gian thích hợp là chuyển nó dưới dạng tham số cho hàm shell ẩn danh (một đoạn mã shell được chạy ngay lập tức với phạm vi hàm). Ví dụ: mã này:

() {
   print File $1:
   cat $1
} =(print This be the verse)

xuất ra một cái gì đó tương tự như sau

File /tmp/zsh6nU0kS:
This be the verse

Ví dụ, tôi sử dụng điều này trong súng trường (một phần của trình quản lý tệp kiểm lâm) để giải mã một tệp và sau đó chạy súng trường trên tệp tạm thời, sẽ bị xóa khi kết thúc phụ. (đừng quên thiết lập $TERMCMD)

# ~/.config/ranger/rifle.conf
...
!ext exe, mime octet-stream$, has gpg, flag t = () { rifle -f F "$1" } =(gpg -dq "$1")
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.