237 lines
9.9 KiB
TeX
237 lines
9.9 KiB
TeX
\documentclass{article}
|
|
|
|
\usepackage{graphicx}
|
|
\usepackage{xcolor}
|
|
\usepackage{minted}
|
|
\usepackage{hyperref}
|
|
|
|
\title{CS3502 Project 3: File Manager}
|
|
\author{Kiana Sheibani}
|
|
|
|
\begin{document}
|
|
|
|
\maketitle
|
|
\newpage
|
|
|
|
\section{Introduction}
|
|
|
|
Circumstance compels us to program a GUI file manager from scratch, as it so
|
|
often does.
|
|
|
|
\subsection{Objective}
|
|
|
|
A file manager is a tool for reading and manipulating a file-system, typically
|
|
the primary file-system of the machine. It allows the user to perform various
|
|
operations on the files and directories (\emph{entries} in aggregate) of the
|
|
file-system, such as to:
|
|
|
|
\begin{itemize}
|
|
\item \textbf{Create} new entries;
|
|
\item \textbf{Read} and \textbf{Update} the contents of entries;
|
|
\item \textbf{Delete} entries.
|
|
\end{itemize}
|
|
|
|
Commonly, file managers are designed to allow this manipulation by having the
|
|
user traverse the filesystem one directory at a time, moving from a single
|
|
working directory to its sub- or parent directories.
|
|
|
|
\subsection{Resources}
|
|
|
|
Implementing a file manager is not a trivial task. Properly navigating a
|
|
file-system requires quite a bit of engineering around complex systems,
|
|
unintuitive edge cases and strange errors, and some languages and frameworks are
|
|
better equipped to handle this than others.
|
|
|
|
After considering this fact, I decided on \href{https://rust-lang.org/}{Rust} as
|
|
my language of choice. Not only do I have significant prior experience with it,
|
|
but its low-level nature and near-slavish dedication to correctness make it
|
|
ideal for working with file-systems.
|
|
|
|
Once I had my language picked, I searched for GUI libraries I could use for the
|
|
front-end. It was not long before I found
|
|
\href{https://crates.io/crates/xilem/}{Xilem}, an experimental cross-platform UI
|
|
toolkit. It was still somewhat unstable, but I decided it would be perfectly
|
|
fine for my purposes.%
|
|
\footnote{This assumption turned out to be rather short-sighted, but I made it
|
|
work as best I could.}
|
|
|
|
\section{Design and Architecture}
|
|
|
|
\subsection{Front-End Design}
|
|
|
|
\begin{figure}[tbh]
|
|
\includegraphics[width=\linewidth]{assets/gui.png}
|
|
\caption{The UI of the file manager.}
|
|
\label{fig:gui}
|
|
\end{figure}
|
|
|
|
The UI design of the file manager is somewhat inspired by
|
|
\href{https://github.com/ranger/ranger}{{\texttt{ranger}}}, though heavily
|
|
simplified. The current directory path is displayed at the top, and its entries
|
|
are listed in the main panel below it. When a file is opened, its contents
|
|
appear on a panel to the right, and remain there until another file is opened.
|
|
|
|
The file-system can be navigated by either double-clicking on a subdirectory, or
|
|
selecting it and then clicking the \emph{Open} button at the bottom. To move up
|
|
the directory tree, a component of the topmost path display can be clicked on.
|
|
There are also several other action buttons along the bottom; these operate on
|
|
the currently selected entry (aside from the \emph{New File} and \emph{New Dir}
|
|
actions) and using them pops up a dialog box to confirm the action. The dialog
|
|
box popup is also used to display errors whenever they occur.
|
|
|
|
\begin{figure}[tbh]
|
|
\includegraphics[width=\linewidth]{assets/dialog.png}
|
|
\caption{The file manager showing a file renaming dialog.}
|
|
\label{fig:dialog}
|
|
\end{figure}
|
|
|
|
\subsection{Program Architecture}
|
|
|
|
The structure of the program can be conceptually divided into two halves: the
|
|
file-system management subroutines, and the GUI logic.
|
|
|
|
\subsubsection{File-System Management}
|
|
|
|
Internally, an entry in the current directory is stored as an \texttt{EntryData}
|
|
object (Listing~\ref{listing:EntryData}), which holds the entry's name and other
|
|
metadata. The basic file-system operations, such as renaming and deleting
|
|
entries, all operate on this data structure.
|
|
|
|
\begin{listing}[!ht]
|
|
\inputminted[
|
|
firstline=7, lastline=17,
|
|
fontsize=\footnotesize,
|
|
linenos,
|
|
frame=lines,
|
|
framesep=2mm,
|
|
baselinestretch=1.1,
|
|
]{rust}{../src/files.rs}
|
|
\vspace*{-\baselineskip}
|
|
\caption{\texttt{files.rs} --- Definition of \texttt{EntryData}}
|
|
\label{listing:EntryData}
|
|
\end{listing}
|
|
|
|
Reading and editing a file, on the other hand, requires a different structure to
|
|
store and manipulate the file handle. This is defined as \texttt{FileData}
|
|
(Listing~\ref{listing:FileData}). When a file is opened, the file handle is
|
|
retained for the entire duration the file is viewed, only being closed when the
|
|
file is exited. The \texttt{files} module of the program implements an
|
|
\texttt{open} function to create this data structure and a \texttt{save}
|
|
function to write to the file handle that it holds.
|
|
|
|
\begin{listing}[!ht]
|
|
\inputminted[
|
|
firstline=72, lastline=85,
|
|
fontsize=\footnotesize,
|
|
linenos,
|
|
frame=lines,
|
|
framesep=2mm,
|
|
baselinestretch=1.1,
|
|
]{rust}{../src/files.rs}
|
|
\vspace*{-\baselineskip}
|
|
\caption{\texttt{files.rs} --- Definition of \texttt{FileData}}
|
|
\label{listing:FileData}
|
|
\end{listing}
|
|
|
|
\subsubsection{GUI \& Error Handling}
|
|
|
|
Xilem is a reactive GUI framework based on composable widgets, similar to Qt.
|
|
The system is based around a data structure representing the app's state; in the
|
|
case of this program, the \texttt{AppState} type includes the current directory,
|
|
selected entry, and state for any open panels, such as the \texttt{FileData} for
|
|
the open file.
|
|
|
|
\texttt{AppState} also includes the \texttt{DialogState}, which is an enum with
|
|
one value per type of dialog that can be opened: \texttt{NewFile},
|
|
\texttt{NewDir}, \texttt{Rename}, \texttt{Delete}, and \texttt{Error}, where the
|
|
last value also includes an error message to display. Determining when to show
|
|
these errors is simple: thanks to Rust's aforementioned obsession with
|
|
correctness, every file-system routine it provides that could ever conceivably
|
|
result in any sort of error wraps its return type in a \texttt{std::io::Result}
|
|
data structure, which forces the programmer to handle the error case before the
|
|
actual output can be retrieved. This is why I chose Rust for this project, and
|
|
it led to very simple and easy-to-understand file management code.
|
|
|
|
\section{Testing \& Edge Cases}
|
|
|
|
In order to ensure the robustness of my work, I regularly tested it manually to
|
|
make sure that it functioned properly and handled any of the weird edge cases
|
|
file-system code is prone to. Some of these issues I was able to fully
|
|
eliminate, while others were a bit more stubborn.
|
|
|
|
\subsection{File-System Race Conditions}
|
|
|
|
The file manager is only one of many processes that can access the file-system,
|
|
which can lead to issues if two processes are trying to modify the same
|
|
directory at the same time. For example, if another process deletes a file in
|
|
the current directory of the file manager, an orphan entry may be left over that
|
|
no longer points to an existing file. While the file manager will not
|
|
automatically detect that this has occurred, it will throw an error if the user
|
|
tries to access an entry that no longer exists.
|
|
|
|
\subsection{Uncommon Entry Types}
|
|
|
|
So far, the only two entry types mentioned have been files and subdirectories,
|
|
the two most common types. There are others, however; symbolic links, or
|
|
``symlinks'', and more esoteric file types like named pipes, device files, etc.
|
|
Symlinks are of particular concern, as they violate the file-system's otherwise
|
|
perfect tree structure.
|
|
|
|
To avoid hard-to-debug issues down the line, I decided on a policy for handling
|
|
symlinks from the start:
|
|
|
|
\begin{itemize}
|
|
\item In the main panel, symlinks are not followed and are displayed
|
|
distinctly from the entries that they point to, including their
|
|
metadata. This is done for performance reasons.
|
|
\item Opening a symlink follows it to its target, with the caveat that
|
|
if a symlink is followed into a subdirectory, the displayed path is
|
|
canonicalized. This simplifies the navigation.
|
|
\end{itemize}
|
|
|
|
All other entry types simply throw an error when the user tries to open them, as
|
|
do any files that do not contain UTF-8 text.
|
|
|
|
\subsection{Unusual Entry Names}
|
|
|
|
While most entry names are simple Latin letters and numbers, depending on the
|
|
operating system there are many other possibilities. Unix-based systems
|
|
typically have UTF-8 entry names, though they technically allow any string of
|
|
bytes except for \texttt{0x00} (NUL) or \texttt{0x2F} (/). Windows, on the other
|
|
hand, has historically used UTF-16 for its entry names, as it predated the wide
|
|
adoption of the superior UTF-8.
|
|
|
|
To allow cross-platform programs to navigate this confusion, the Rust standard
|
|
library provides the \texttt{OsString} type, which represents a string in the
|
|
format the target OS prefers. Due to this and some careful programming, the file
|
|
manager will never crash due to unsupported entry names (at least on Linux; I
|
|
have not personally tested Windows). Unfortunately, while non-Unicode entry
|
|
names can be read, they cannot be entered into dialog text fields. This is an
|
|
inherent limitation of Xilem that is unlikely to change.
|
|
|
|
\subsection{Large File Sizes}
|
|
|
|
This is the largest issue that still remains in the file manager. Due to the
|
|
fact that a full buffer of an open file's contents must be maintained, the file
|
|
editor is exceedingly slow with files over a few kilobytes, and trying to open
|
|
anything larger than a megabyte has a chance of causing the application to
|
|
crash. Ideally, the program would use some sort of line buffering scheme to
|
|
avoid having to update the file all at once, but I was not able to implement
|
|
anything that worked well.
|
|
|
|
\section{Conclusion}
|
|
|
|
After roughly a week's effort, a basic file manager has been achieved. Though
|
|
the visual aesthetic and the file editing feature may be improved, this
|
|
serves as a strong foundation for a well-programmed application.
|
|
|
|
\subsection{Learning Outcomes}
|
|
|
|
While I most likely started from a more knowledgeable place on this subject than
|
|
most in this class, I did learn some new things from this process. I was not
|
|
nearly as knowledgeable on the minutiae of OS file-systems when I started this
|
|
project as I am now, nor was I as experienced in writing GUI applications that
|
|
properly interface with that OS minutia. At the end of it all, I am glad that
|
|
something of some use came out of this.
|
|
|
|
\end{document}
|