Last active
April 3, 2016 21:47
-
-
Save chrrrisw/765ee229d046c12b0310 to your computer and use it in GitHub Desktop.
Getting short filenames (8.3 format) from a FAT partition on Linux in Python
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <fcntl.h> | |
#include <linux/msdos_fs.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <sys/ioctl.h> | |
#include <unistd.h> | |
#include <stddef.h> | |
/* | |
* This file prints the constants for both VFAT_IOCTL_READDIR_BOTH | |
* and VFAT_IOCTL_READDIR_SHORT, as well as the buffer length required | |
* for the __fat_dirent structure (multiplied by 2). | |
* The output is suitable for direct inclusion into a python module. | |
* Also output is a format string suitable for use by struct.unpack(). | |
* | |
* The accompanying Makefile builds this C file into an executable and | |
* generates the vfat_ioctl.py Python module from it. This is then | |
* imported into the test_short.py program for use in determining | |
* the short (8.3 format) filenames. | |
*/ | |
int main(int argc, char *argv[]) | |
{ | |
struct __fat_dirent entry; | |
printf("VFAT_IOCTL_READDIR_BOTH = %ld\n", VFAT_IOCTL_READDIR_BOTH); | |
printf("VFAT_IOCTL_READDIR_SHORT = %ld\n", VFAT_IOCTL_READDIR_SHORT); | |
long int buffer_size = sizeof(struct __fat_dirent); | |
printf("BUFFER_SIZE = %ld\n", buffer_size * 2); | |
long int d_reclen_offset = offsetof(struct __fat_dirent, d_reclen); | |
long int d_reclen_size = sizeof(entry.d_reclen); | |
char d_reclen_type; | |
switch (d_reclen_size) { | |
case 1: | |
d_reclen_type = 'B'; | |
break; | |
case 2: | |
d_reclen_type = 'H'; | |
break; | |
case 4: | |
d_reclen_type = 'I'; | |
break; | |
} | |
long int d_name_offset = offsetof(struct __fat_dirent, d_name); | |
long int d_name_size = sizeof(entry.d_name); | |
if (d_reclen_offset + d_reclen_size != d_name_offset) { | |
printf("Oops!\n"); | |
} | |
long int end_padding_size = buffer_size - (d_name_offset + d_name_size); | |
printf("BUFFER_FORMAT = '=%ldx%c%lds%ldx%ldx%c%lds%ldx'\n", | |
d_reclen_offset, d_reclen_type, d_name_size, end_padding_size, | |
d_reclen_offset, d_reclen_type, d_name_size, end_padding_size); | |
exit(EXIT_SUCCESS); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
all: generate_ioctl vfat_ioctl.py | |
generate_ioctl: generate_ioctl.c | |
vfat_ioctl.py: generate_ioctl | |
./generate_ioctl > vfat_ioctl.py |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env /usr/bin/python3 | |
import argparse | |
import os | |
import fcntl | |
import struct | |
import vfat_ioctl | |
''' | |
Prints the directory tree below the given FAT partition path. | |
Outputs both the long and short (8.3 format) file and directory names. | |
''' | |
class FATParser(object): | |
def __init__(self, topdir): | |
# Store the top directory | |
self.topdir = topdir | |
# Create an empty dictionary to store the paths | |
self.paths = {} | |
# A buffer to hold the IOCTL data | |
self.buffer = bytearray(vfat_ioctl.BUFFER_SIZE) | |
# Walk the directory tree | |
for root, dirs, files, rootfd in os.fwalk(topdir): | |
self.get_directory_entries(root, rootfd) | |
def get_directory_entries(self, directory_name, directory_fd): | |
# Get the path relative to the top level directory | |
relative_path = os.path.relpath(directory_name, self.topdir) | |
# If we don't have it, it's the top level directory so we | |
# seed the paths dictionary. If we have it, get the shortname | |
# for the directory (collected one level up). | |
if relative_path not in self.paths: | |
self.paths[relative_path] = { | |
'shortname': relative_path, | |
'files': []} | |
current_path_shortname = relative_path | |
else: | |
current_path_shortname = self.paths[relative_path]['shortname'] | |
while True: | |
# Get both names for the next directory entry using a ioctl call. | |
result = fcntl.ioctl( | |
directory_fd, | |
vfat_ioctl.VFAT_IOCTL_READDIR_BOTH, | |
self.buffer) | |
# Have we finished? | |
if result < 1: | |
break | |
# Interpret the resultant bytearray | |
# sl = length of shortname | |
# sn = shortname | |
# ll = length of longname | |
# ln = longname | |
sl, sn, ll, ln = struct.unpack( | |
vfat_ioctl.BUFFER_FORMAT, | |
self.buffer) | |
# Decode the bytearrays into strings | |
# If longname has zero length, use shortname | |
shortname = sn[:sl].decode() | |
if ll > 0: | |
filename = ln[:ll].decode() | |
else: | |
filename = shortname | |
# Don't process . or .. | |
if (filename != '.') and (filename != '..'): | |
# Check whether it's a directory | |
fullname = os.path.join(directory_name, filename) | |
if os.path.isdir(fullname): | |
# Create the paths entry | |
self.paths[os.path.relpath(fullname, self.topdir)] = { | |
'shortname': os.path.join( | |
current_path_shortname, shortname), | |
'files': []} | |
else: | |
self.paths[relative_path]['files'].append({ | |
'shortname': shortname, | |
'fullname': fullname, | |
'filename': filename}) | |
def display(self): | |
for path in self.paths: | |
print('{} --> {}'.format(path, self.paths[path]['shortname'])) | |
for f in self.paths[path]['files']: | |
print('\t {} --> {}'.format(f['filename'], f['shortname'])) | |
def main(): | |
parser = argparse.ArgumentParser(description="List a FAT directory") | |
parser.add_argument( | |
dest="inputdir", | |
action="store", | |
default=None, help="specify input directory") | |
args = parser.parse_args() | |
if os.path.isdir(args.inputdir): | |
fat_parser = FATParser(args.inputdir) | |
fat_parser.display() | |
else: | |
print('Please enter a directory name on a FAT partition') | |
if __name__ == '__main__': | |
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
VFAT_IOCTL_READDIR_BOTH = 2184212993 | |
VFAT_IOCTL_READDIR_SHORT = 2184212994 | |
BUFFER_SIZE = 560 | |
BUFFER_FORMAT = '=16xH256s6x16xH256s6x' |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment