{"id":15766723,"url":"https://github.com/0x2b3bfa0/ext4-imc","last_synced_at":"2026-02-25T17:33:42.288Z","repository":{"id":32502609,"uuid":"36083370","full_name":"0x2b3bfa0/ext4-imc","owner":"0x2b3bfa0","description":"Bash script to calculate inode modes for the Ext4 filesystem.","archived":false,"fork":false,"pushed_at":"2021-09-25T20:59:18.000Z","size":34,"stargazers_count":3,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-05-05T19:18:00.660Z","etag":null,"topics":["bash","calculate-inode-modes","command-line","ext4-filesystem","filesystem","inode","mount","proof-of-concept"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/0x2b3bfa0.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-05-22T16:39:34.000Z","updated_at":"2020-10-06T14:51:13.000Z","dependencies_parsed_at":"2022-07-16T18:00:30.366Z","dependency_job_id":null,"html_url":"https://github.com/0x2b3bfa0/ext4-imc","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0x2b3bfa0%2Fext4-imc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0x2b3bfa0%2Fext4-imc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0x2b3bfa0%2Fext4-imc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0x2b3bfa0%2Fext4-imc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/0x2b3bfa0","download_url":"https://codeload.github.com/0x2b3bfa0/ext4-imc/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252561059,"owners_count":21768063,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bash","calculate-inode-modes","command-line","ext4-filesystem","filesystem","inode","mount","proof-of-concept"],"created_at":"2024-10-04T13:02:22.309Z","updated_at":"2026-02-25T17:33:37.259Z","avatar_url":"https://github.com/0x2b3bfa0.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Ext4 inode mode calculator\nBash script to calculate inode modes for the Ext4 filesystem. Originally made for an [AskUbuntu](https://askubuntu.com/questions/626634/convert-a-file-to-directory/626731) answer.\n\n***\n# Achieving the conversion\n\n### Creating a test filesystem\nIn order to preserve our main filesystem from any possible damage after running this experiment, we're going to create a small filesystem inside a normal file for test purposes.\n\n1. Create a zero-filled file called `test` with a size of 10 megabytes:\n\n        dd if=/dev/zero of=~/test bs=10M count=1\n\n2. Create an Ext4 filesystem inside the file, as if it were a partition:\n\n        mkfs.ext4 ~/test\n\n### Creating some files and directories\nNow we hace a fully functional filesystem inside the `test` file, so we're going to create some files and directories inside it.\n\n1. Mount the newly created filesystem inside `/mnt`:\n\n        sudo mount ~/test /mnt\n\n2. Create a file and a directory:\n\n        sudo mkdir /mnt/folder\n        echo \"contents\" | sudo tee /mnt/file\n\n3. Check the contents of the filesystem:\n\n        ls -l /mnt\n\n       Output should be something like this:\n\n        total 2\n        -rw-r--r-- 1 root root     0 may 21 18:53 file\n        drw-r--r-- 2 root root  1024 may 21 18:55 folder\n\n4. Unmount the test filesystem:\n\n        sudo umount /mnt\n\n### Swapping the file and the folder\n\n1. Run `debugfs` against the `test` file with write permission (`-w` flag):\n\n        debugfs -w ~/test\n\n2. Convert `file` into a folder:\n\n   * At the `debugfs` prompt, type this:\n\n            modify_inode file\n\n   * A prompt will appear asking you a mode; type this:\n  \n            040644\n\n   * Keep pressing **\u003ckbd\u003ereturn\u003c/kbd\u003e** to leave the remaining data as-is until the prompt appears again.\n\n3. Convert `folder` into a file:\n\n   * At the `debugfs` prompt, type this:\n\n            modify_inode folder\n\n   * A prompt will appear asking you a mode; type this:\n  \n            0100644\n\n   * Keep pressing **\u003ckbd\u003ereturn\u003c/kbd\u003e** to leave the remaining data as-is until the prompt appears again.\n\n4. To exit `debugfs` prompt, simply hit **\u003ckbd\u003eq\u003c/kbd\u003e** and then **\u003ckbd\u003ereturn\u003c/kbd\u003e**\n\n### Checking the success of the operation\n\n1. Mount the test filesystem again:\n\n        sudo mount ~/test /mnt\n\n2. Check the filesystem contents:\n\n        ls -l /mnt\n\n       Now, it should show the file as if it were a directory and *vice versa*:\n\n        total 2\n        drw-r--r-- 1 root root     0 may 21 18:53 file\n        -rw-r--r-- 2 root root  1024 may 21 18:55 folder\n\n---\n\n\t# Script to calculate inode modes\n\n\t#!/bin/bash\n\n\t#### See https://ext4.wiki.kernel.org/index.php/Ext4_Disk_Layout#Inode_Table\n\n\t## Terminal measures:\n\tx=\"$(( $(tput cols) / 2 ))\"   # Width of the terminal\n\ty=\"$(( $(tput lines) /  2 ))\" # Height of the terminal\n\n\t## File descriptors:\n\tdeclare -A types       # Declare an associative array with file descriptors\n\ttypes[f]='0x8000'      # File\n\ttypes[l]='0xA000'      # Link\n\ttypes[s]='0xC000'      # Socket\n\ttypes[d]='0x4000'      # Directory\n\ttypes[p]='0x1000'      # Named pipe\n\ttypes[b]='0x6000'      # Block device\n\ttypes[c]='0x2000'      # Character device\n\n\t## Permissions:\n\tdeclare -A permission  # Declare an associative array with permissions\n\tpermission[user_S]='0x800'  # UID\n\tpermission[user_s]='0x840'  # UID and user can execute\n\tpermission[user_r]='0x100'  # User can read\n\tpermission[user_w]='0x80'   # User can write\n\tpermission[user_x]='0x40'   # User can execute\n\tpermission[group_S]='0x400' # GID\n\tpermission[group_s]='0x408' # GID and group can execute\n\tpermission[group_r]='0x20'  # Group can read\n\tpermission[group_w]='0x10'  # Group can write\n\tpermission[group_x]='0x8'   # Group can execute\n\tpermission[other_T]='0x200' # Sticky bit\n\tpermission[other_t]='0x201' # Sticky bit and other can execute\n\tpermission[other_r]='0x4'   # Other can read\n\tpermission[other_w]='0x2'   # Other can write\n\tpermission[other_x]='0x1'   # Other can execute\n\n\t## Cleanup function:\n\tfunction cleanup() {\n\t    tput cvvis        # Make the cursor visible\n\t    tput rmcup        # Restore saved terminal contents\n\t    stty sane         # Fix problems caused by read -s\n\t    exit 0            # Exit gracefully\n\t}\n\n\t## Function to print at a specified position:\n\tfunction pprint() {\n\t    tput cup $1 $2\n\t    printf \"${@:3}\"\n\t}\n\n\t## Function to clear the notification area:\n\tfunction reset() {\n\t    pprint $((y+2)) $((x-40)) ' %.0s' {1..25} # Print 25 spaces\n\t}\n\n\t## Function to notify something to the user:\n\tfunction notify() {\n\t    reset                          # Clear the notification area\n\t    pprint $((y+2)) $((x-40)) \"$@\" # Print the notification text\n\t}\n\n\t## If the terminal is smaller than 100x8, exit gracefully (self-explainatory):\n\tif [ $x -lt 50 ] || [ $y -lt 5 ]; then\n\t    echo 'Error, I need a minimum of 100x10 lines to run'\n\t    exit 0\n\tfi\n\n\t## Initialize the terminal:\n\ttrap cleanup EXIT SIGHUP SIGINT SIGTERM # Call cleanup function after receiving ^C\n\tstty -echo  cbreak                      # Put terminal in silent mode\n\ttput smcup                              # Save terminal contents\n\ttput civis                              # Make the cursor inisible\n\n\t## Draw the big box:\n\tprintf '\\033[1;37m'                            # Color\n\tpprint $((y-3)) $((x-48)) '\\u2500%.0s' {1..97} # Upper line\n\tpprint $((y+4)) $((x-48)) '\\u2500%.0s' {1..97} # Lower line\n\tfor ((i=4;i\u003e-4;i--)); do                       # Sides:\n\t    pprint $((y+i)) $((x-49)) '\\u2502'             # Left line\n\t    pprint $((y+i)) $((x+49)) '\\u2502'             # Right line\n\tdone                                           # End sides\n\tpprint $((y-3)) $((x-49)) '\\u256D'             # Upper-left corner\n\tpprint $((y+4)) $((x-49)) '\\u2570'             # Lower-left corner\n\tpprint $((y-3)) $((x+49)) '\\u256E'             # Upper-right corner\n\tpprint $((y+4)) $((x+49)) '\\u256F'             # Lower-right corner\n\n\t## Draw the small box:\n\tprintf '\\033[1;35m'                             # Color\n\tpprint $((y+1)) $((x-10)) '\\u2501%.0s' {1..10}  # Upper line\n\tpprint $((y+3)) $((x-10)) '\\u2501%.0s' {1..10}  # Lower line\n\tpprint $((y+2)) $((x-11)) '\\u2503'              # Left line\n\tpprint $((y+2)) $((x+00)) '\\u2503'              # Right line\n\tpprint $((y+1)) $((x-11)) '\\u250F'              # Upper-left corner\n\tpprint $((y+3)) $((x-11)) '\\u2517'              # Lower-left corner\n\tpprint $((y+1)) $((x+00)) '\\u2513'              # Upper-right corner\n\tpprint $((y+3)) $((x+00)) '\\u251B'              # Lower-right corner\n\n\t## Print type help:\n\tpprint $((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.'\n\n\t## Print permission help:\n\tpprint $((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.'\n\tpprint $((y+0)) $((x+8)) 's\\033[1;36mt\\033[0;36micky bit and executable, '\n\tpprint $((y+1)) $((x+8)) 's\\033[1;36mT\\033[0;36micky bit not executable, '\n\tpprint $((y+2)) $((x+8)) '\\033[1;36ms\\033[0;36metuid/setgid and executable, '\n\tpprint $((y+3)) $((x+8)) '\\033[1;36mS\\033[0;36metuid/setgid not executable. '\n\n\t## Endless loop:\n\twhile :; do\n\n\t    ## Clear the input area:\n\t    pprint $((y+2)) $((x-10)) '% *s\\n' 10         # Print 16 spaces\n\n\t    ## Print mask in the input area:\n\t    printf '\\033[1;37m'                           # Color for the type\n\t    pprint $((y+2)) $((x-10)) '\\u2588'            # Block for the type\n\t    printf '\\033[1;36m'                           # Color for the permision\n\t    pprint $((y+2)) $((x- 9)) '\\u2588%.0s' {1..9} # Blocks for the permission\n\n\t    ## Loop through all variables to make a proper input:\n\t    for var in type {user,group,other}_{r,w,x}; do\n\n\t\t## Assign colors and regex to fields:\n\t\tcase \"$var\" in\n\t\t    (type)    color='\\033[1;37m';     regex='^[fdlpscb]$'    ;;\n\n\t\t    (other_x)                         regex='^[-xtT]$'       ;;\u0026\n\t\t    (user_x|group_x)                  regex='^[-xsS]$'       ;;\u0026\n\t\t    (user_[rw]|group_[rw]|other_[rw]) regex=\"^[-${var: -1}]$\";;\u0026\n\n\t\t    (user*)   color='\\033[1;32m'                             ;;\n\t\t    (group*)  color='\\033[1;33m'                             ;;\n\t\t    (other*)  color='\\033[1;31m'                             ;;\n\t\tesac\n\n\t\t## Change the pointer position:\n\t\tpprint $((y+3)) $(((x-10)+pointer)) \"${color}\\u2501\"           # Print the pointer on its new position\n\t\tif (( pointer \u003e 0 )); then                                     # If the pointer is not in the first position:\n\t\t    pprint $((y+3)) $(((x-10)+(pointer-1))) '\\033[1;35m\\u2501'     # Clear the old pointer         \n\t\tfi\n\n\t\t## Infinite loop until there is a valid input for the current character:\n\t\twhile :; do\n\t\t    printf \"$color\"                       # Set the character color\n\t\t    IFS= read -rn 1 $var                  # Read a character (even if it's a space)\n\n\t\t    declare $var=\"${!var// /-}\"           # Convert spaces to hyphens.\n\t\t    if [[ \"$var\" == \"type\" ]]; then       # If the current variable is type:\n\t\t\tdeclare $var=\"${!var//-/f}\"           # Convert \"-\" to \"f\"\n\t\t    fi\n\n\t\t    if [[ \"${!var}\"  =~ $regex ]]; then   # If there is a valid input:\n\t\t\treset                                 # Clear error notification if any\n\t\t\tbreak                                 # Exit from this loop\n\t\t    else                                  # Else:\n\t\t\tnotify \"\\033[1;31mWrong input!\"       # Print the error message\n\t\t    fi\n\t\tdone\n\n\t\t## Print the entered value:\n\t\tpprint $((y+2)) $(((x-10)+pointer)) \"${!var}\"\n\n\t\t## Sum the current permission:\n\t\t((mode+=permission[${var%_*}_${!var}]))\n\n\t\t## Increment the pointer:\n\t\t((pointer++))\n\t    done\n\n\t    ## Post-read:\n\t    unset pointer                                 # Reset the pointer\n\t    pprint $((y+3)) $((x-1)) \"\\033[1;35m\\u2501\"   # Clear the pointer\n\t    read -n 1                                     # Wait for Return or another character\n\n\t    ## Sum file descriptor type:\n\t    ((mode+=${types[$type]}))\n\n\t    ## Final commands:\n\t    mode=$(printf \"%o\" $mode)                      # Convert mode to octal (before this was decimal)\n\t    notify \"\\033[1;32mOctal mode:\\033[1;34m $mode\" # Print the octal mode\n\t    unset mode                                     # Reset the mode\n\tdone\n\n\n[View script on GitHub](https://github.com/0x2b3bfa0/ext4-imc)\n\n# Handicaps\n\n* The folder doesn't open. You can't open it *unless* you put on it the \"raw folder data\" that contained it originally.\n\n# Further reading\nhttps://ext4.wiki.kernel.org/index.php/Ext4_Disk_Layout#Inode_Table\n\n***\n\n**Thanks to [@tallus](https://askubuntu.com/users/80756/tallus).** He gave me a great hint:\n\t\n\n\u003e debugfs has a modify_inode command that allows you to edit an inode\n\u003e directly which would allow you to set the file flag to a dir.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F0x2b3bfa0%2Fext4-imc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F0x2b3bfa0%2Fext4-imc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F0x2b3bfa0%2Fext4-imc/lists"}