Konvertieren einer Datei in ein Verzeichnis

14

Wie wir wissen, ist "Everything in Linux" eine Datei, und außerdem ist das Verzeichnis nur eine Datei, die andere Dateien enthält.

Also, ich weiß nicht, ob diese "verrückte Idee" möglich ist, aber es sollte irgendwie nach dem oben genannten Prinzip sein.

In einfachen Worten, wie könnte ich eine vorhandene leere Datei in ein Verzeichnis ändern. Ist es möglich?

Als ein Brainstorming dachte ich, eine Modifikation in den Dateimetadaten und die Verzeichnismetadaten sollten es schaffen !!

Alle Informationen werden geschätzt.


UPDATE: Natürlich möchte ich keine Datei löschen und stattdessen das Verzeichnis erstellen! Ich versuche nur zu wissen, inwieweit die oben genannte Philosophie anwendbar ist, wenn Sie mit einigen Dateimetadaten spielen können.

Maythux
quelle
1
Was könnte ein Grund dafür sein?
Pilot6
1
Der einzig richtige Weg ist, eine Datei zu löschen und ein Verzeichnis zu erstellen. Andernfalls kann das Dateisystem beschädigt werden. Sie können dies auf niedriger Ebene tun, dies hängt jedoch vom Dateisystem ab. In ext4 sollte inode bearbeitet werden, denke ich.
Pilot6
2
Beim "Datei" -Konzept geht es nicht darum. Geräte werden ebenfalls als Dateien behandelt. Dies bedeutet jedoch nicht, dass Sie eine Datei in ein Gerät konvertieren können. :))
Pilot6
5
debugfs verfügt über einen Befehl modify_inode, mit dem Sie einen Inode direkt bearbeiten können, um das Dateiflag auf ein Verzeichnis zu setzen. Es hat auch einen Befehl mkdir <inode>. Ich habe nichts davon getan und werde es auch nicht versuchen.
Tallus
4
Diese Annahme "Wie wir wissen" Alles in Linux "ist eine Datei" ist falsch, so dass Ihre ganze Frage auseinanderfällt. Wie wir wissen "Alles in Linux ist ein Dateideskriptor". Macht eine Welt voller Unterschiede.
Rinzwind

Antworten:

21

Die Umstellung erreichen

Erstellen eines Testdateisystems

Um unser Hauptdateisystem nach dem Ausführen dieses Experiments vor möglichen Schäden zu schützen, erstellen wir zu Testzwecken ein kleines Dateisystem in einer normalen Datei.

  1. Erstellen Sie eine mit Nullen gefüllte Datei testmit einer Größe von 10 Megabyte:

    dd if=/dev/zero of=~/test bs=10M count=1
    
  2. Erstellen Sie ein Ext4-Dateisystem in der Datei, als wäre es eine Partition:

    mkfs.ext4 ~/test
    

Erstellen einiger Dateien und Verzeichnisse

Jetzt haben wir ein voll funktionsfähiges Dateisystem in der testDatei, also werden wir einige Dateien und Verzeichnisse darin erstellen.

  1. Hängen Sie das neu erstellte Dateisystem ein /mnt:

    sudo mount ~/test /mnt
    
  2. Erstellen Sie eine Datei und ein Verzeichnis:

    sudo mkdir /mnt/folder
    echo "contents" | sudo tee /mnt/file
    
  3. Überprüfen Sie den Inhalt des Dateisystems:

    ls -l /mnt
    

    Die Ausgabe sollte ungefähr so ​​aussehen:

    total 2
    -rw-r--r-- 1 root root     0 may 21 18:53 file
    drw-r--r-- 2 root root  1024 may 21 18:55 folder
    
  4. Hängen Sie das Test-Dateisystem aus:

    sudo umount /mnt
    

Tauschen Sie die Datei und den Ordner aus

  1. Führen Sie debugfsgegen die testDatei mit Schreibrechten ( -wflag):

    debugfs -w ~/test
    
  2. fileIn einen Ordner konvertieren :

    • debugfsGeben Sie an der Eingabeaufforderung Folgendes ein:

      modify_inode file
      
    • Es erscheint eine Eingabeaufforderung, in der Sie nach einem Modus gefragt werden. tippe dies:

      040644
      
    • Halten Sie gedrückt return, um die restlichen Daten unverändert zu lassen, bis die Eingabeaufforderung erneut angezeigt wird.

  3. folderIn eine Datei konvertieren :

    • debugfsGeben Sie an der Eingabeaufforderung Folgendes ein:

      modify_inode folder
      
    • Es erscheint eine Eingabeaufforderung, in der Sie nach einem Modus gefragt werden. tippe dies:

      0100644
      
    • Halten Sie gedrückt return, um die restlichen Daten unverändert zu lassen, bis die Eingabeaufforderung erneut angezeigt wird.

  4. Um die debugfsEingabeaufforderung zu verlassen, drücken Sie einfach qund dannreturn

Überprüfen Sie den Erfolg der Operation

  1. Hängen Sie das Test-Dateisystem erneut ein:

    sudo mount ~/test /mnt
    
  2. Überprüfen Sie den Inhalt des Dateisystems:

    ls -l /mnt
    

    Jetzt sollte die Datei so angezeigt werden, als wäre sie ein Verzeichnis und umgekehrt :

    total 2
    drw-r--r-- 1 root root     0 may 21 18:53 file
    -rw-r--r-- 2 root root  1024 may 21 18:55 folder
    

Skript zur Berechnung der Inode-Modi

#!/bin/bash

#### See https://ext4.wiki.kernel.org/index.php/Ext4_Disk_Layout#Inode_Table

## Terminal measures:
x="$(( $(tput cols) / 2 ))"   # Width of the terminal
y="$(( $(tput lines) /  2 ))" # Height of the terminal

## File descriptors:
declare -A types       # Declare an associative array with file descriptors
types[f]='0x8000'      # File
types[l]='0xA000'      # Link
types[s]='0xC000'      # Socket
types[d]='0x4000'      # Directory
types[p]='0x1000'      # Named pipe
types[b]='0x6000'      # Block device
types[c]='0x2000'      # Character device

## Permissions:
declare -A permission  # Declare an associative array with permissions
permission[user_S]='0x800'  # UID
permission[user_s]='0x840'  # UID and user can execute
permission[user_r]='0x100'  # User can read
permission[user_w]='0x80'   # User can write
permission[user_x]='0x40'   # User can execute
permission[group_S]='0x400' # GID
permission[group_s]='0x408' # GID and group can execute
permission[group_r]='0x20'  # Group can read
permission[group_w]='0x10'  # Group can write
permission[group_x]='0x8'   # Group can execute
permission[other_T]='0x200' # Sticky bit
permission[other_t]='0x201' # Sticky bit and other can execute
permission[other_r]='0x4'   # Other can read
permission[other_w]='0x2'   # Other can write
permission[other_x]='0x1'   # Other can execute

## Cleanup function:
function cleanup() {
    tput cvvis        # Make the cursor visible
    tput rmcup        # Restore saved terminal contents
    stty sane         # Fix problems caused by read -s
    exit 0            # Exit gracefully
}

## Function to print at a specified position:
function pprint() {
    tput cup $1 $2
    printf "${@:3}"
}

## Function to clear the notification area:
function reset() {
    pprint $((y+2)) $((x-40)) ' %.0s' {1..25} # Print 25 spaces
}

## Function to notify something to the user:
function notify() {
    reset                          # Clear the notification area
    pprint $((y+2)) $((x-40)) "$@" # Print the notification text
}

## If the terminal is smaller than 100x8, exit gracefully (self-explainatory):
if [ $x -lt 50 ] || [ $y -lt 5 ]; then
    echo 'Error, I need a minimum of 100x10 lines to run'
    exit 0
fi

## Initialize the terminal:
trap cleanup EXIT SIGHUP SIGINT SIGTERM # Call cleanup function after receiving ^C
stty -echo  cbreak                      # Put terminal in silent mode
tput smcup                              # Save terminal contents
tput civis                              # Make the cursor inisible

## Draw the big box:
printf '\033[1;37m'                            # Color
pprint $((y-3)) $((x-48)) '\u2500%.0s' {1..97} # Upper line
pprint $((y+4)) $((x-48)) '\u2500%.0s' {1..97} # Lower line
for ((i=4;i>-4;i--)); do                       # Sides:
    pprint $((y+i)) $((x-49)) '\u2502'             # Left line
    pprint $((y+i)) $((x+49)) '\u2502'             # Right line
done                                           # End sides
pprint $((y-3)) $((x-49)) '\u256D'             # Upper-left corner
pprint $((y+4)) $((x-49)) '\u2570'             # Lower-left corner
pprint $((y-3)) $((x+49)) '\u256E'             # Upper-right corner
pprint $((y+4)) $((x+49)) '\u256F'             # Lower-right corner

## Draw the small box:
printf '\033[1;35m'                             # Color
pprint $((y+1)) $((x-10)) '\u2501%.0s' {1..10}  # Upper line
pprint $((y+3)) $((x-10)) '\u2501%.0s' {1..10}  # Lower line
pprint $((y+2)) $((x-11)) '\u2503'              # Left line
pprint $((y+2)) $((x+00)) '\u2503'              # Right line
pprint $((y+1)) $((x-11)) '\u250F'              # Upper-left corner
pprint $((y+3)) $((x-11)) '\u2517'              # Lower-left corner
pprint $((y+1)) $((x+00)) '\u2513'              # Upper-right corner
pprint $((y+3)) $((x+00)) '\u251B'              # Lower-right corner

## Print type help:
pprint $((y-2)) $((x-44)) '\033[0;37mInode type: \033[1;37mf\033[0;37mile, \033[1;37md\033[0;37mirectory, \033[1;37ml\033[0;37mink, named \033[1;37mp\033[0;37mipe, \033[1;37ms\033[0;37mocket, \033[1;37mc\033[0;37mharacter device or \033[1;37mb\033[0;37mlock device.'

## Print permission help:
pprint $((y-1)) $((x-40)) '\033[0;36mPermission (\033[1;32mu\033[0;32mser\033[0;36m, \033[1;33mg\033[0;33mroup\033[0;36m or \033[1;31mo\033[0;31mther\033[0;36m): \033[1;36mr\033[0;36mead, \033[1;36mw\033[0;36mrite, e\033[1;36mx\033[0;36mecute, \033[1;36mhyphen\033[0;36m or \033[1;36mspace\033[0;36m to skip.'
pprint $((y+0)) $((x+8)) 's\033[1;36mt\033[0;36micky bit and executable, '
pprint $((y+1)) $((x+8)) 's\033[1;36mT\033[0;36micky bit not executable, '
pprint $((y+2)) $((x+8)) '\033[1;36ms\033[0;36metuid/setgid and executable, '
pprint $((y+3)) $((x+8)) '\033[1;36mS\033[0;36metuid/setgid not executable. '

## Endless loop:
while :; do

    ## Clear the input area:
    pprint $((y+2)) $((x-10)) '% *s\n' 10         # Print 16 spaces

    ## Print mask in the input area:
    printf '\033[1;37m'                           # Color for the type
    pprint $((y+2)) $((x-10)) '\u2588'            # Block for the type
    printf '\033[1;36m'                           # Color for the permision
    pprint $((y+2)) $((x- 9)) '\u2588%.0s' {1..9} # Blocks for the permission

    ## Loop through all variables to make a proper input:
    for var in type {user,group,other}_{r,w,x}; do

        ## Assign colors and regex to fields:
        case "$var" in
            (type)    color='\033[1;37m';     regex='^[fdlpscb]$'    ;;

            (other_x)                         regex='^[-xtT]$'       ;;&
            (user_x|group_x)                  regex='^[-xsS]$'       ;;&
            (user_[rw]|group_[rw]|other_[rw]) regex="^[-${var: -1}]$";;&

            (user*)   color='\033[1;32m'                             ;;
            (group*)  color='\033[1;33m'                             ;;
            (other*)  color='\033[1;31m'                             ;;
        esac

        ## Change the pointer position:
        pprint $((y+3)) $(((x-10)+pointer)) "${color}\u2501"           # Print the pointer on its new position
        if (( pointer > 0 )); then                                     # If the pointer is not in the first position:
            pprint $((y+3)) $(((x-10)+(pointer-1))) '\033[1;35m\u2501'     # Clear the old pointer         
        fi

        ## Infinite loop until there is a valid input for the current character:
        while :; do
            printf "$color"                       # Set the character color
            IFS= read -rn 1 $var                  # Read a character (even if it's a space)

            declare $var="${!var// /-}"           # Convert spaces to hyphens.
            if [[ "$var" == "type" ]]; then       # If the current variable is type:
                declare $var="${!var//-/f}"           # Convert "-" to "f"
            fi

            if [[ "${!var}"  =~ $regex ]]; then   # If there is a valid input:
                reset                                 # Clear error notification if any
                break                                 # Exit from this loop
            else                                  # Else:
                notify "\033[1;31mWrong input!"       # Print the error message
            fi
        done

        ## Print the entered value:
        pprint $((y+2)) $(((x-10)+pointer)) "${!var}"

        ## Sum the current permission:
        ((mode+=permission[${var%_*}_${!var}]))

        ## Increment the pointer:
        ((pointer++))
    done

    ## Post-read:
    unset pointer                                 # Reset the pointer
    pprint $((y+3)) $((x-1)) "\033[1;35m\u2501"   # Clear the pointer
    read -n 1                                     # Wait for Return or another character

    ## Sum file descriptor type:
    ((mode+=${types[$type]}))

    ## Final commands:
    mode=$(printf "%o" $mode)                      # Convert mode to octal (before this was decimal)
    notify "\033[1;32mOctal mode:\033[1;34m $mode" # Print the octal mode
    unset mode                                     # Reset the mode
done

Skript auf GitHub anzeigen

Handicaps

  • Der Ordner öffnet sich nicht. Sie können es nur öffnen, wenn Sie die "unformatierten Ordnerdaten" darauf ablegen, in denen es ursprünglich enthalten war.

Weitere Lektüre

https://ext4.wiki.kernel.org/index.php/Ext4_Disk_Layout#Inode_Table


Vielen Dank an @tallus . Er gab mir einen tollen Hinweis:

debugfs verfügt über einen Befehl modify_inode, mit dem Sie einen Inode direkt bearbeiten können, um das Dateiflag auf ein Verzeichnis zu setzen.

Helio
quelle
2
+1 Schöne Antwort, aber nur Notizen 0100755müssen sein, 0100644um Berechtigungen einer Datei nicht zu ändern, da 755 Ausführung für die konvertierte Datei geben wird ...
Maythux
Außerdem wird das neu konvertierte Verzeichnis nicht geöffnet. Es zeigt, dass die Datei zum Ordner wird, der Ordner jedoch nicht geöffnet werden kann. Haben Sie eine Lösung dafür?
Maythux
Ich werde es für einige Zeit offen halten, sonst, wenn es keine andere gute Antwort gibt, werde ich Ihre markieren
Maythux
3
Verdammt, das ist nett @helio: D
Rinzwind
1
Was für eine geniale Antwort! Okay, es ist letztendlich nutzlos, aber immer noch sehr beeindruckend, also +1 von mir.
Carl H.,