Convertendo um arquivo em diretório

14

Como sabemos, "Tudo no Linux" é um arquivo e, além disso, o diretório é apenas um arquivo que contém outros arquivos.

Então, eu não sei se essa "idéia maluca" é possível, mas deve estar de alguma forma, de acordo com o princípio acima.

Em palavras simples, como eu poderia alterar um arquivo vazio existente em um diretório. É possível?

Como uma tempestade cerebral, pensei que alguma modificação nos metadados do arquivo e fizesse isso como metadados do diretório deveriam fazê-lo !!

Qualquer informação é apreciada.


UPDATE: Com certeza, não quero excluir um arquivo e criar o diretório! Só estou tentando saber o quanto a filosofia acima é aplicável se você puder jogar com alguns metadados de arquivo.

Maythux
fonte
1
O que pode ser uma razão para fazer isso?
precisa saber é o seguinte
1
A única maneira correta é excluir o arquivo e criar um diretório. Caso contrário, o sistema de arquivos pode estar quebrado. Você pode fazer isso em nível baixo, mas isso depende do sistema de arquivos. No ext4 inode deve ser editado, eu acho.
Pilot6
2
O conceito de "arquivo" não é sobre isso. Os dispositivos também são tratados como arquivos, mas isso não significa que você pode converter um arquivo em um dispositivo. :))
Pilot6
5
debugfs possui um comando modify_inode que permite editar um inode diretamente, o que permitiria definir o sinalizador de arquivo como um diretório Ele também possui um comando mkdir <inode>. Eu não fiz nada disso e não vou tentar.
tallus 21/05
4
Essa suposição "Como sabemos" Tudo no Linux "é um arquivo" está errada, então toda a sua pergunta se desfaz. Como sabemos "Tudo no Linux é um arquivo DESCRIPTOR". Faz um mundo de diferença.
Rinzwind 22/15/15

Respostas:

21

Atingindo a conversão

Criando um sistema de arquivos de teste

Para preservar nosso sistema de arquivos principal de qualquer dano possível após a execução desse experimento, criaremos um pequeno sistema de arquivos dentro de um arquivo normal para fins de teste.

  1. Crie um arquivo preenchido com zero chamado testcom um tamanho de 10 megabytes:

    dd if=/dev/zero of=~/test bs=10M count=1
    
  2. Crie um sistema de arquivos Ext4 dentro do arquivo, como se fosse uma partição:

    mkfs.ext4 ~/test
    

Criando alguns arquivos e diretórios

Agora temos um sistema de arquivos totalmente funcional dentro do testarquivo, então vamos criar alguns arquivos e diretórios dentro dele.

  1. Monte o sistema de arquivos recém-criado dentro /mnt:

    sudo mount ~/test /mnt
    
  2. Crie um arquivo e um diretório:

    sudo mkdir /mnt/folder
    echo "contents" | sudo tee /mnt/file
    
  3. Verifique o conteúdo do sistema de arquivos:

    ls -l /mnt
    

    A saída deve ser algo como isto:

    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. Desmonte o sistema de arquivos de teste:

    sudo umount /mnt
    

Trocando o arquivo e a pasta

  1. Execute debugfsno testarquivo com permissão de gravação ( -wsinalizador):

    debugfs -w ~/test
    
  2. Converta fileem uma pasta:

    • No debugfsprompt, digite isto:

      modify_inode file
      
    • Um prompt aparecerá solicitando um modo; digite isto:

      040644
      
    • Continue pressionando returnpara deixar os dados restantes como estão até que o prompt apareça novamente.

  3. Converta folderem um arquivo:

    • No debugfsprompt, digite isto:

      modify_inode folder
      
    • Um prompt aparecerá solicitando um modo; digite isto:

      0100644
      
    • Continue pressionando returnpara deixar os dados restantes como estão até que o prompt apareça novamente.

  4. Para sair do debugfsprompt, basta pressionar qe, em seguida,return

Verificando o sucesso da operação

  1. Monte o sistema de arquivos de teste novamente:

    sudo mount ~/test /mnt
    
  2. Verifique o conteúdo do sistema de arquivos:

    ls -l /mnt
    

    Agora, ele deve mostrar o arquivo como se fosse um diretório e vice-versa :

    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 inode

#!/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 script no GitHub

Handicaps

  • A pasta não abre. Você não pode abri-lo, a menos que coloque os "dados brutos da pasta" que os continham originalmente.

Leitura adicional

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


Graças a @tallus . Ele me deu uma ótima dica:

debugfs possui um comando modify_inode que permite editar um inode diretamente, o que permitiria definir o sinalizador de arquivo como um diretório

Helio
fonte
2
+1 agradável resposta, mas apenas notas 0100755deve ser 0100644para não alterar as permissões de um arquivo desde 755 dará execução para o arquivo convertido ...
Maythux
Além disso, o diretório recém-convertido não será aberto. Mostra o arquivo se tornar pasta, mas a pasta não pode ser aberta. Você tem alguma solução para isso?
Maythux
Eu vou mantê-la aberta por algum tempo, outra coisa se não houver outra grande resposta que eu vou a sua marca
Maythux
3
Porra, isso é legal @helio: D
Rinzwind
1
Que resposta brilhante! Ok, no final das contas é inútil, mas ainda é muito impressionante, então +1 de mim.
Carl H