The Windows File Management 5




File Attributes and Directories


File attributes are part of the metadata associated with a file or directory, each with its own purpose and rules on how it can be set and changed. Some of these attributes apply only to files, and some only to directories. For example, the FILE_ATTRIBUTE_DIRECTORY attribute applies only to directories: It is used by the file system to determine whether an object on disk is a directory, but it cannot be changed for an existing file system object. Some file attributes can be set for a directory but have meaning only for files created in that directory, acting as default attributes. For example, FILE_ATTRIBUTE_COMPRESSED can be set on a directory object, but because the directory object itself contains no actual data, it is not truly compressed; however, directories marked with this attribute tell the file system to compress any new files added to that directory. Any file attribute that can be set on a directory and will also be set for new files added to that directory is referred to as an inherited attribute.

The CreateFile() function provides a parameter for setting certain file attributes when a file is created. In general, these attributes are the most common for an application to use at file creation time, but not all possible file attributes are available to CreateFile(). Some file attributes require the use of other functions, such as SetFileAttributes(), DeviceIoControl(), or DecryptFile() after the file already exists. In the case of FILE_ATTRIBUTE_DIRECTORY, the CreateDirectory() function is required at creation time because CreateFile() cannot create directories. The other file attributes that require special handling are FILE_ATTRIBUTE_REPARSE_POINT and FILE_ATTRIBUTE_SPARSE_FILE, which require DeviceIoControl(). As stated previously, file attribute inheritance occurs when a file is created with file attributes read from the directory attributes where the file will be located. The following table summarizes these inherited attributes and how they relate to CreateFile() capabilities.


Directory attribute state

CreateFile inheritance override capability for new files


No control. Use DeviceIoControl() to unset.


No control. Use DeviceIoControl() to set.


No control. Use DecryptFile().


Can be set using CreateFile().


No control. Use SetFileAttributes() to unset.


No control. Use SetFileAttributes() to set.


Moving and Replacing Files


Before a file can be copied, it must be closed or opened only for reading. No thread can have the file opened for writing. To copy an existing file to a new one, use the CopyFile() or CopyFileEx() function. Applications can specify whether CopyFile() and CopyFileEx() fail if the destination file already exists. If the file does exist and is open, it must have been opened with applicable sharing permissions. The CopyFileEx() function also allows an application to specify the address of a callback function that is called each time another portion of the file has been copied. The application can use this information to display an indicator that shows the total number of bytes copied as a percent of the total file size.

The ReplaceFile() function replaces one file with another file, with the option of creating a backup copy of the original file. The function preserves attributes of the original file, such as its creation time, ACLs, and encryption attribute. A file must also be closed before an application can move it. The MoveFile() and MoveFileEx() functions copy an existing file to a new location and deletes the original. The MoveFileEx() function also allows an application to specify how to move the file. The function can replace an existing file, move a file across volumes, and delay moving the file until the operating system is restarted.


Closing and Deleting Files


To use operating system resources efficiently, an application should close files when they are no longer needed by using the CloseHandle() function. If a file is open when an application terminates, the system closes it automatically. The DeleteFile() function can be used to delete a file on close. A file cannot be deleted until all handles to it are closed. If a file cannot be deleted, its name cannot be reused. To reuse a file name immediately, rename the existing file. If you are deleting an open file or directory on a remote machine and it has already been opened on the remote machine without the read share permission set, do not call CreateFile() or OpenFile() to open the file or directory for deletion first. Doing so will result in a sharing violation.


Defragmenting Files


When a file is written to a disk, the file cannot be written in contiguous clusters. Noncontiguous clusters slow down the process of reading and writing a file. The further apart on a disk the noncontiguous clusters are, the worse the issue, because of the time it takes to move the read/write head of a hard drive. A file with noncontiguous clusters is fragmented. To optimize files for fast access, a volume can be defragmented. Defragmentation is the process of moving portions of files around on a disk to defragment files, that is, the process of moving file clusters on a disk to make them contiguous. In a simple single-tasking operating system, the defragmentation software is the only task, and there are no other processes to read from or write to the disk. However, in a multitasking operating system, some processes can be reading from and writing to a hard drive while another process is defragmenting that hard drive. The trick is to avoid writes to a file that is being defragmented without stopping the writing process for very long. Solving this problem is not trivial, but it is possible. To allow defragmentation without requiring detailed knowledge of a file system disk structure, a set of three control codes is provided. The control codes provide the following functionality.

  1. Enable applications to locate empty clusters.
  2. Determine the disk location of file clusters.
  3. Move clusters on a disk.


The control codes also transparently handle the problem of inhibiting and allowing other processes to read from and write to files during moves. These operations can be performed without inhibiting other processes from running. However, the other processes have slower response times while a disk drive is being defragmented. To defragment a file:


  1. Use the FSCTL_GET_VOLUME_BITMAP control code to find a place on the volume that is large enough to accept an entire file.
  2. If necessary, move other files to make a place that is large enough. Ideally, there is enough unallocated clusters after the first extent of the file that you can move subsequent extents into the space after the first extent.
  3. Use the FSCTL_GET_RETRIEVAL_POINTERS control code to get a map of the current layout of the file on the disk.
  5. Use the FSCTL_MOVE_FILE control code to move each cluster as you walk the structure.
  6. You may need to renew either the bitmap or the retrieval structure, or both at various times as other processes write to the disk.


Two of the operations that are used in the defragmentation process require a handle to a volume. Only administrators can obtain a handle to a volume, so only administrators can defragment a volume. An application should check the rights of a user who attempts to run defragmentation software, and it should not allow a user to defragment a volume if the user does not have the appropriate rights. When using CreateFile() to open a directory during defragmentation of a FAT or FAT32 file system volume, specify the GENERIC_READ access mask value. Do not specify the MAXIMUM_ALLOWED access mask value. Access to the directory is denied if that is done. Do not attempt to move allocated clusters in an NTFS file system that extend beyond the cluster rounded file size, because the result is an error. Re-parse points, bitmaps, and attribute lists in NTFS file system volumes can be defragmented, opened for reading and synchronization, and named using the file:name:type syntax; for example, $i30:$INDEX_ALLOCATION, mrp::$DATA, mrp::$REPARSE_POINT, and mrp::$ATTRIBUTE_LIST. When defragmenting NTFS file system volumes, defragmenting a virtual cluster beyond the allocation size of a file is allowed.


Minimizing interactions between de-fragmentation and shadow copies


When possible, move data in blocks aligned relative to each other in 16-kilobyte (KB) increments. This reduces copy-on-write overhead when shadow copies are enabled, because shadow copy space is increased and performance is reduced when the following conditions occur:


  1. The move request block size is less than or equal to 16 KB.
  2. The move delta is not in increments of 16 KB.


The move delta is the number of bytes between the start of the source block and the start of the target block. In other words, a block starting at offset X (on-disk) can be moved to a starting offset Y if the absolute value of X minus Y is an even multiple of 16 KB. So, assuming 4-KB clusters, a move from cluster 3 to cluster 27 will be optimized, but a move from cluster 18 to cluster 24 will not. Note that mod(3,4) = 3 = mod(27,4). Mod 4 is chosen because four clusters at 4 KB each is equivalent to 16 KB. Therefore, a volume formatted to a 16-KB cluster size will result in all move files being optimized.


Limitations under Windows 2000


The following list identifies the limitations of the file defragmentation API under Windows 2000, and they do not apply to later versions of Windows.

  1. The NTFS file system defragments data by using the system cache, therefore, encrypted files must be opened with read access.
  2. The NTFS file system defragments uncompressed files at the page boundary.
  3. The NTFS file system cannot defragment the master file table (MFT), re-parse points, bitmaps, and NTFS file system attribute lists.
  4. The NTFS file system cannot defragment the space between the valid data length and the end of the file.


Retrieving File Type Information


The GetFileType() function retrieves the type of a file: disk, character (such as a console), pipe, or unknown. The GetBinaryType() function determines whether a file is executable, and if so, the type of executable file it is. See GetBinaryType() section for a list of the supported executable types.


Determining the Size of a File


The GetFileSize() function retrieves the size of a file. Because the NTFS file system implementation of files allows for multiple streams within a file, any application you write that accesses files must account for the possibility that the creator of the file can include multiple streams in the file. If multiple streams are not checked for in a file, the application can underestimate the total size of the file, among other errors.


Searching for One or More Files


An application can search the current directory for all file names that match a given pattern by using the FindFirstFile(), FindFirstFileEx(), FindNextFile(), and FindClose() functions. The pattern must be a valid file name and can include wildcard characters. The FindFirstFile() and FindFirstFileEx() functions create handles that FindFirstFileEx() uses to search for other files with the same pattern. All functions return information about the file that was found. This information includes the file name, size, attributes, and time.

The FindFirstFileEx() function also allows an application to search for files based on additional search criteria. The function can limit searches to device names or directory names. The FindClose() function destroys handles created by FindFirstFile() and FindFirstFileEx(). An application can search for a single file on a specific path by using the SearchPath() function.


Setting and Getting the Timestamp of a File


Applications can retrieve and set the date and time a file is created, last modified, or last accessed by using the GetFileTime() and SetFileTime() functions. Note that not all file systems can record creation and last access times, and not all file systems record them in the same manner. For example, on FAT file system, create time has a resolution of 10 milliseconds, write time has a resolution of 2 seconds, and access time has a resolution of 1 day (really, the access date). The NTFS file system delays update to the last access time for a file by up to one hour after the last access.




< Windows Files 4 | Win32 Programming | Win32 File Index | Windows Files 6 >