Abstract

This article shows how to perform tasks involving reading and writing files from various partitions by using the C#.net programming API. In particular, it covers exploring the directory structure, finding out what files and folders are present, and performing other file-related operations, such as moving, copying, and deleting objects from the disk. The core motive behind this article is to explore types defined in the System.IO namespace and to promote an understanding of various ways to read from and write to character-based, binary-based, and string-based data stores.

The Anatomy of a File System

The System.IO provides four classes that allow you to manipulate individual files and interact with a machine directory structure. The Directory and File directly extends the System.Object to allow creating, copying, moving, and deleting using various static methods only, never instantiated. The FileInfo and DirectoryInfo types are derived from the abstract class FileSystemInfo type and they are typically, employed for obtaining full details of a file or directory because their members tend strongly to return type objects. They implement roughly the same public methods as Directory and File, but they are stateful and the members of these classes are not static.


In the .NET framework, the System.IO namespace is the region for the base class libraries devoted to file-based input and output services. Like any namespace, the System.IO namespace defines a set of classes, interface, enumeration, structure, and delegates. The following table outlines the core members of this namespace as following;

Class Types Description
Directory/ DirectoryInfo These classes are used to manipulate the system directory structure.
DriveInfo This class contains detailed information regarding the drives that a given machine uses.
FileStream This represents random file access data as a stream of bytes.
File/FileInfo These classes are used to manipulate computer files.
Path This performs operations on System.String types that contain file or directory path information in a platform-neutral manner.
BinaryReader/BinaryWriter These classes allow you to store and retrieve primitive data types as binary values.
StreamReader/StreamWriter This is used to store textual information in a file.
StringReader/StringWriter These classes also work with textual information. However, the underlying storage is a string buffer rather than a physical file.
BufferedStream This class provides temp storage for a stream of bytes that you can commit to storage at a later time.

Reading Disk Partitions

The System.IO provides a class DriveInfo in order to manipulate system drive-related tasks. The DriveInfo class provides numerous details, such as displaying the total number of drives and calculating total hard disk space, available space, drive name, ready status, types, etc. Consider the following program, which showing the total disk drives

DriveInfo[] di = DriveInfo.GetDrives();
Console.WriteLine("Total Partitions");

foreach(DriveInfo items in di)
{
   Console.WriteLine(items.Name);
}

The following code snippets perform the rest of the DriveInfo class methods operation in detail. Full information about a particular drive is displayed like this:

using System;
using System.IO;

namespace DiskPartition
{
    class Program
    {
        static void Main(string[] args)
        {
            DriveInfo[] di = DriveInfo.GetDrives();

            Console.WriteLine("Total Partitions");
            Console.WriteLine("---------------------");
            foreach(DriveInfo items in di)
            {
                Console.WriteLine(items.Name);
            }
            Console.Write("nEnter the Partition::");
            string ch = Console.ReadLine();

            DriveInfo dInfo = new DriveInfo(ch);

            Console.WriteLine("n");

            Console.WriteLine("Drive Name::{0}",dInfo.Name);
            Console.WriteLine("Total Space::{0}", dInfo.TotalSize);
            Console.WriteLine("Free Space::{0}", dInfo.TotalFreeSpace);
            Console.WriteLine("Drive Format::{0}", dInfo.DriveFormat);
            Console.WriteLine("Volume Label::{0}", dInfo.VolumeLabel);
            Console.WriteLine("Drive Type::{0}", dInfo.DriveType);
            Console.WriteLine("Root dir::{0}", dInfo.RootDirectory);
            Console.WriteLine("Ready::{0}", dInfo.IsReady);

            Console.ReadKey();
        }
    }
}

After compiling this program, it displays almost every detail of disk drives and a particular drive, as seen below:

Working with Directory

The .NET framework stipulates two rudimentary classes, DirectoryInfo and Directory, in order to perform directory- related operations such creation, deletion.

DirectoryInfo Class

The DirectoryInfo class contains a set of members used for creation, deletion, moving, and enumeration over directories and subdirectories. The following code sample displays the information related to the temp directory in D drive:

DirectoryInfo di=new DirectoryInfo(@"D:temp");

Console.WriteLine("*******Directory Informations*******nn");
Console.WriteLine("Full Name={0}",di.FullName);
Console.WriteLine("Root={0}",di.Root);
Console.WriteLine("Attributes={0}", di.Attributes);
Console.WriteLine("Creation Time={0}", di.CreationTime);
Console.WriteLine("Name={0}", di.Name);
Console.WriteLine("Parent={0}", di.Parent);

The output follows:


Typically, we make the assumption that the path passed in the constructor of DirectoryInfo class physically exists. However, if you attempt to interact with a nonexistent directory, then the CLR will throw an exception. So we need to create a directory first in order to handle exceptions:

      DirectoryInfo di=new DirectoryInfo(@"D:tempxyz");
      di.Create();

We can also programmatically extend a directory structure using the CreateSubdirectory() method. The following code sample firs, creates a subdirectory in D drive, then in D:ajay:

DirectoryInfo di=new DirectoryInfo(@"D:");
di.CreateSubdirectory("ajay");
di.CreateSubdirectory(@"ajayajay11");

Directory Class

The Directory class provides almost the same functionality as DirectoryInfo. The Directory typically returns string data rather than strongly typed DirectoryInfo objects. The following sample deletes the directory and subdirectory in D drive:

static void Main(string[] args)
{
            DirectoryInfo di = new DirectoryInfo(@"d:abc");
            Console.WriteLine("Name:{0}",di.FullName);

            Console.Write("Are you sure to Delete:");
            string str=Console.ReadLine();
            if (str == "y")
            {
                Directory.Delete(@"d:abc", true);
            }
            Console.Write("Deleted.....");
}

Reading and Writing to Files

Reading and writing operations are done through File objects. The following code snippet reads a text file that is located on machine somewhere:

private void button1_Click(object sender, EventArgs e)
        {
            try
            {
                textBox2.Text = File.ReadAllText(txtPath.Text);
            }
            catch (FileNotFoundException)
            {
                MessageBox.Show("File not Found....");
            }
        }

First the user interface asks the user to enter the path of the file that he wants to display. Then that path is passed to the File method ReadAllText() method, which reads all the text integrated in the file and displays it in the text box.

Besides reading a file, we can write some contents over an existing text file by using the File class WriteAllTest() method, as follows:

File.WriteAllText(@"d:test.txt", textBox2.Text);

It takes a path to save the file and a content input method medium, such as a text box or any other control. The following image depicts a text file reading by entering its corresponding path:


Want to learn more?? The InfoSec Institute Reverse Engineering course teaches you everything from reverse engineering malware to discovering vulnerabilities in binaries. These skills are required in order to properly secure an organization from today's ever evolving threats. In this 5 day hands-on course, you will gain the necessary binary analysis skills to discover the true nature of any Windows binary. You will learn how to recognize the high level language constructs (such as branching statements, looping functions and network socket code) critical to performing a thorough and professional reverse engineering analysis of a binary. Some features of this course include:

  • CREA Certification
  • 5 days of Intensive Hands-On Labs
  • Hostile Code & Malware analysis, including: Worms, Viruses, Trojans, Rootkits and Bots
  • Binary obfuscation schemes, used by: Hackers, Trojan writers and copy protection algorithms
  • Learn the methodologies, tools, and manual reversing techniques used real world situations in our reversing lab.

Stream

The .NET provides many objects such as FileStream, StreamReader/Writer, and BinaryReader/Writer to read and write data to a file. A stream basically represents a chunk of data flowing between a source and a destination. It provides a common way to interact with a sequence of bytes regardless of what kinds of devices store or display the bytes. The following table shows common stream member functions:

Methods Description
Read()/ ReadByte() Reads a sequence of bytes from the current stream.
Write()/WriteByte() Writes a sequence of bytes to the current stream.
Seek() Sets the position in the current stream.
Position() Determines the current position in the current stream.
Length() Returns the length of the stream in bytes.
Flush() Updates the underlying data source with the current state of the buffer and then clears the buffer.
Close() Closes the current stream and release any associated stream resources.

FileStream

A FileStream instance is used to read or write data to or from a file. In order to construct a FileStream, first we need a file that we want to access; second, the mode that indicates how we want to open the file; third, the access that indicates how we want to access a file; and finally, the share access, which specifies whether you want exclusive access to the file.

Enumeration Values
FileMode Create, Append, Open, CreateNew, Truncate, OpenOrCreate
FileAccess Read, Write, ReadWrite
FileShare Inheritable, Read, None, Write, ReadWrite

The FileStream can read or write only a single byte or an array of bytes. You will be required to encode the System.String type into a corresponding byte array. The System.Text namespace defines a type named encoding that provides members that encode and decode strings to an array of bytes. Once encoded, the byte array persists with the FileStream.Write() method. To read the bytes back into memory, you must reset the internal position of the stream and call the ReadByte() method. Finally, you display the raw byte array and the decoded string to the console:

using(FileStream fs=new FileStream(@"d:ajay123.doc",FileMode.Create))
           {
               string msg = "first program";
               byte[] byteArray = Encoding.Default.GetBytes(msg);
               fs.Write(byteArray, 0, byteArray.Length);
               fs.Position = 0;

               byte[] rFile = new byte[byteArray.Length];

               for (int i = 0; i < byteArray.Length; i++)
               {
                   rFile[i] = (byte)fs.ReadByte();
                   Console.WriteLine(rFile[i]);
               }

               Console.WriteLine(Encoding.Default.GetString(rFile));
           }

BinaryReader and BinaryWriter

The BinaryReader and BinaryWriter classes allow you to read and write discrete data types to an underlying stream in a compact binary format. The BinaryWriter class defines a highly overloaded Write() method to place a data type in the underlying stream.

Members Description Class
Write Writes the value to current stream BinaryWriter
Seek Sets the position in the current stream BinaryWriter
Close Closes the binary reader BinaryWriter
Flush Flushes the binary stream BinaryWriter
PeekChar Returns the next available character without advancing the position in the stream BinaryReader
Read Reads a given set of bytes or characters and stores them in the incoming array. BinaryReader

The following sample first writes data contents to a new champu.dat file by using BinaryWriter. Later, to read the, BinaryReader class employs a number of methods, as follows:

class Program
    {
        static void Main(string[] args)
        {
            // writing
            FileInfo fi = new FileInfo("champu.dat");
            using (BinaryWriter bw = new BinaryWriter(fi.OpenWrite()))
            {
                int x = 007;
                string str = "hello champu ,one day you will become doomkatu";

                bw.Write(x);
                bw.Write(str);
            }

            //Reading
            FileInfo f = new FileInfo("champu.dat");
            using (BinaryReader br = new BinaryReader(fi.OpenRead()))
            {
                Console.WriteLine(br.ReadInt32());
                Console.WriteLine(br.ReadString());
            }
            Console.ReadLine();

        }
    }

StringReader and StringWriter

We can use StringWriter and StringReader to treat textual information as a stream of in-memory characters. This can prove helpful when you wish to append character-based information to an underlying buffer. The following code sample illustrates this by writing a block of string data to a StringWriter object, rather than to a file on the local hard drive:

	static void Main(string[] args)
        {
            // writing
            using (StringWriter sw = new StringWriter())
            {
                sw.WriteLine("helloooooooooooooooooooo");

                // Reading
                using (StringReader sr = new StringReader(sw.ToString()))
                {
                    string input = null;
                    while((input = sr.ReadLine())!=null)
                    {
                        Console.WriteLine(input);
                    }
                }
            }
         }

Summary

This article began by introducing the .NET file system and its detailed class hierarchy. We have learned to manipulate a physical file or directory on the hard drive by using File and Directory class. Next, we examined the Stream class in detail. The System.IO namespace provides numerous writer and reader types, for instance FileStream, BinaryStream, StringStream, etc. So this article gives you a full understanding of how to access data from the hard drive and write it back.