Convertir un archivo a directorio

14

Como sabemos "Everything in Linux" es un archivo, y además el directorio es solo un archivo que contiene otros archivos.

Entonces, no sé si es posible esta "idea loca", pero debería ser de alguna manera de acuerdo con el principio anterior.

En palabras simples, ¿cómo podría cambiar un archivo vacío existente en un directorio? ¿Es posible?

¡Como una tormenta de ideas, pensé que debería haber alguna modificación en los metadatos del archivo y hacerlo como metadatos de directorio!

Cualquier información es apreciada.


ACTUALIZACIÓN: ¡ Seguro que no quiero eliminar un archivo y crear el directorio en su lugar! Solo estoy tratando de saber cuánto es aplicable la filosofía anterior si puedes jugar con algunos metadatos de archivo.

Maythux
fuente
1
¿Cuál puede ser una razón para hacer eso?
Piloto6
1
La única forma correcta es eliminar el archivo y crear un directorio. De lo contrario, el sistema de archivos puede estar roto. Puede hacerlo a bajo nivel pero depende del sistema de archivos. En ext4 inode debería ser editado, creo.
Piloto6
2
El concepto de "archivo" no se trata de eso. Los dispositivos también se tratan como archivos, pero eso no significa que pueda convertir un archivo a un dispositivo. :))
Pilot6
55
debugfs tiene un comando modify_inode que le permite editar un inodo directamente, lo que le permitiría establecer el indicador de archivo en un directorio. También tiene un comando mkdir <inode>. No he hecho nada de esto y no estoy a punto de intentarlo.
tallus
44
Esta suposición "Como sabemos" Todo en Linux "es un archivo", es incorrecta, por lo que toda su pregunta se desmorona. Como sabemos "Todo en Linux es un archivo DESCRIPTOR". Hace un mundo de diferencia.
Rinzwind

Respuestas:

21

Logrando la conversión

Crear un sistema de archivos de prueba

Para preservar nuestro sistema de archivos principal de cualquier daño posible después de ejecutar este experimento, vamos a crear un pequeño sistema de archivos dentro de un archivo normal para fines de prueba.

  1. Cree un archivo lleno de cero llamado testcon un tamaño de 10 megabytes:

    dd if=/dev/zero of=~/test bs=10M count=1
    
  2. Cree un sistema de archivos Ext4 dentro del archivo, como si fuera una partición:

    mkfs.ext4 ~/test
    

Crear algunos archivos y directorios

Ahora tenemos un sistema de archivos completamente funcional dentro del testarchivo, por lo que vamos a crear algunos archivos y directorios dentro de él.

  1. Monte el sistema de archivos recién creado dentro /mnt:

    sudo mount ~/test /mnt
    
  2. Crea un archivo y un directorio:

    sudo mkdir /mnt/folder
    echo "contents" | sudo tee /mnt/file
    
  3. Verifique el contenido del sistema de archivos:

    ls -l /mnt
    

    La salida debería ser algo como esto:

    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. Desmontar el sistema de archivos de prueba:

    sudo umount /mnt
    

Intercambiando el archivo y la carpeta

  1. Ejecutar debugfscontra el testarchivo con permiso de escritura ( -wmarca):

    debugfs -w ~/test
    
  2. Convierte fileen una carpeta:

    • En el debugfsindicador, escriba esto:

      modify_inode file
      
    • Aparecerá un mensaje preguntándole un modo; escribe esto:

      040644
      
    • Siga presionando returnpara dejar los datos restantes tal como están hasta que aparezca el mensaje nuevamente.

  3. Convierte folderen un archivo:

    • En el debugfsindicador, escriba esto:

      modify_inode folder
      
    • Aparecerá un mensaje preguntándole un modo; escribe esto:

      0100644
      
    • Siga presionando returnpara dejar los datos restantes tal como están hasta que aparezca el mensaje nuevamente.

  4. Para salir del debugfsindicador, simplemente presione qy luegoreturn

Comprobando el éxito de la operación

  1. Monte el sistema de archivos de prueba nuevamente:

    sudo mount ~/test /mnt
    
  2. Verifique el contenido del sistema de archivos:

    ls -l /mnt
    

    Ahora, debería mostrar el archivo como si fuera un directorio y viceversa :

    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
    

Script para calcular modos de inodo

#!/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

Ver guión en GitHub

Desventajas

  • La carpeta no se abre. No puede abrirlo a menos que ponga los "datos de carpeta sin procesar" que lo contenían originalmente.

Otras lecturas

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


Gracias a @tallus . Me dio una gran pista:

debugfs tiene un comando modify_inode que le permite editar un inodo directamente, lo que le permitiría establecer el indicador de archivo en un directorio.

Helio
fuente
2
+1 Buena respuesta, pero solo las notas 0100755deben ser 0100644para no cambiar los permisos de un archivo ya que 755 dará ejecución para el archivo convertido ...
Maythux
Además, el directorio recién convertido no se abrirá. Muestra que el archivo se convirtió en carpeta pero la carpeta no se puede abrir. ¿Tienes alguna solución para eso?
Maythux
Lo mantendré abierto durante un tiempo, de lo contrario, si no hay otra gran respuesta, marcaré la suya
Maythux
3
Maldita sea, esto es bueno @helio: D
Rinzwind
1
¡Qué respuesta tan brillante! De acuerdo, en última instancia es inútil, pero sigue siendo muy impresionante, así que +1 de mi parte.
Carl H