DedSec Project

Ασφάλεια αρχείων και ατομική αποθήκευση — DedSec Project

Ασφαλής αντικατάσταση αρχείου — διατήρηση της τελευταίας σωστής αναφοράς Σενάριο και κριτήρια επιτυχίας Ένα εργαλείο χωρίς διαδίκτυο εξάγει μία αναφορά σε συγκεκριμένο αρχείο. Αν η εκτέλεση…

Ασφαλής αντικατάσταση αρχείου — διατήρηση της τελευταίας σωστής αναφοράς

Σενάριο και κριτήρια επιτυχίας

Ένα εργαλείο χωρίς διαδίκτυο εξάγει μία αναφορά σε συγκεκριμένο αρχείο. Αν η εκτέλεση διακοπεί αφού γραφτεί προσωρινό περιεχόμενο, η προηγούμενη αναφορά πρέπει να διατηρείται. Η επιτυχία πρέπει να αφήνει μόνο τη νέα έκδοση, χωρίς βοηθητικά αρχεία. Δημιούργησε το προσωρινό αρχείο στον ίδιο κατάλογο με τον προορισμό, ώστε η αντικατάσταση να μην απαιτεί μεταφορά μεταξύ συστημάτων αρχείων.

Εκτελέσιμο εργαστήριο — ενσωματωμένη βιβλιοθήκη Python 3

Αποθήκευσε τον κώδικα ως 06_filesystems.py και εκτέλεσέ τον με Python 3. Χρησιμοποιεί μόνο τεχνητά δεδομένα και εμφανίζει PASS αν περάσουν οι έλεγχοι. Ο κώδικας είναι ίδιος και στις δύο γλωσσικές εκδόσεις.

import os, tempfile
from pathlib import Path
def replace_text(target, text, fail_before_replace=False):
    target = Path(target)
    if target.is_symlink():
        raise ValueError("symbolic-link target refused")
    temp_name = None
    try:
        with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8",
                dir=target.parent, prefix=".new-", delete=False) as out:
            temp_name = out.name
            out.write(text)
            out.flush()
            os.fsync(out.fileno())
        if fail_before_replace:
            raise OSError("injected interruption")
        os.replace(temp_name, target)
        temp_name = None
    finally:
        if temp_name is not None:
            Path(temp_name).unlink(missing_ok=True)
with tempfile.TemporaryDirectory() as location:
    target = Path(location) / "report.txt"
    target.write_text("old", encoding="utf-8")
    try: replace_text(target, "partial", True)
    except OSError: pass
    assert target.read_text(encoding="utf-8") == "old"
    replace_text(target, "new")
    assert target.read_text(encoding="utf-8") == "new"
    assert not list(Path(location).glob(".new-*"))
print("PASS file replacement")

Τεχνική εξήγηση

Η NamedTemporaryFile, η flush και η fsync χειρίζονται διαφορετικά στάδια της εγγραφής. Η os.replace αλλάζει το όνομα με μία λειτουργία, αλλά δεν ισοδυναμεί πάντα με εγγύηση αντοχής σε ξαφνική διακοπή ρεύματος: μπορεί να χρειάζεται συγχρονισμός και του καταλόγου. Ο έλεγχος is_symlink αποτρέπει μία απλή λανθασμένη διαδρομή, όχι όμως κάθε ταυτόχρονη επίθεση αλλαγής συνδέσμου μεταξύ ελέγχου και χρήσης.

Περιορισμένες ασκήσεις

Προκάλεσε την τεχνητή διακοπή και βεβαιώσου ότι το report.txt παραμένει 'old'. Στη συνέχεια δοκίμασε επιτυχημένη αντικατάσταση, ελληνικό κείμενο, μη εγγράψιμο κατάλογο και συμβολικό σύνδεσμο σε προσωρινό περιβάλλον. Για μη έμπιστους καταλόγους μελέτησε λειτουργίες σχετικές με ανοικτούς περιγραφείς αρχείων αντί για απλό έλεγχο αρχής συμβολοσειράς.

Διάγνωση και τεκμήρια επιτυχίας

Αν εμφανιστεί EXDEV, το προσωρινό και το τελικό αρχείο μπορεί να βρίσκονται σε διαφορετικά συστήματα αρχείων. Μην καταφεύγεις σιωπηλά σε καταστροφική αντιγραφή. Σε αποτυχία της fsync, κράτησε το παλιό αρχείο και δήλωσε το σφάλμα. Το παράδειγμα αφορά ένα αρχείο και όχι ατομική αλλαγή πολλών αρχείων.

Πρόσθετη τεχνική ανάλυση

Σε εργασίες συνέχισης, το όριο αντικατάστασης αρχείου πρέπει να συμφωνεί με την ολοκληρωμένη εργασία. Πρόωρη πρόοδος χάνει εργασία, ενώ καθυστερημένη μπορεί να την επαναλάβει. Προτίμησε αποτελέσματα ανθεκτικά σε επανάληψη και διακοπή σε κάθε βήμα αποθήκευσης αντί να θεωρείς ολόκληρη την εργασία συναλλακτική.

Ανθεκτικά σημεία επανεκκίνησης και αντικατάσταση αρχείων