IIS Insider - July 2005

By Chris Adams, Program Manager, Microsoft Corporation

IIS Insider

Changing Attributes of the Root Partition for a Mounted Volume

Fac1

Q. We have a mounted volume on our Windows Server 2003 and we are directing a site's path to that location (for example, C:\Web). When clients make requests using the fully qualified domain name only (or a virtual directory), they receive a 404 error message. However, making a direct request to the file serves the page. Why am I getting a 404 when not including the file name?

A. The unusual nature of this non-standard disk configuration is the problem because of the way that mounted disk's root partitions are represented by the operating system to services such as IIS.

Consider this scenario for example:

Disk 1: 20 GB NTFS Volume (C:\)
Disk 2: 80 GB NTFS Volume

Let's assume you already have your Web environment set up using the single Disk 1 and a path of C:\Web for all of your Web content. To avoid the overhead of re-doing your Web applications, you would like to extend your storage. However, you want to do this with the least amount of changes to your Web application code.

Thus, you create the Disk 2 configuration and you mount it as part of the C:\ volume you already have created. In this case, you are re-writing the root partition of this volume to be located at another physical directory on another volume. It can be confusing, but not an unlikely configuration for many Web administrators.

Back to the problem: IIS fails when clients do not offer to IIS an actual file to fetch when they make their request. In this scenario, clients are depending on the IIS server to provide the document based on the default document. The default document list can be found on the virtual directory "Documents" tab and usually will contain such items as default.htm, default.html, default.aspx, and so on. When IIS is presented with requests that do not contain a specific file, it will attempt to push through this list of "documents" and open the file. IIS does this in a unique way when it calls a specific API - CreateFile. The CreateFile call will attempt to locate the root partition of the drive and then append the requested Web site's path with the appropriate document.

For example, assume the Default Site had this configuration:

  • Path: C:\inetpub\wwwroot

  • DefaultDoc: index.htm

When a request to the site is made and a file is not specified then IIS will call the API and request the file "\\?\inetpub\wwwroot\index.htm." In most scenarios, this is perfectly acceptable. Not so in this unique configuration where you have mounted a partition to an existing drive. IIS 6.0 will typically overlook the fact that the root partitions for typical partitions are hidden, but not when it is a mounted volume. In this unusual scenario, the lookup for the root partition fails because of the disk behavior in Windows Server 2003.

The actual root cause of your problem is the fact that the failure is caused by the mounted partition's default behavior of making the root partition hidden. IIS 6.0 (and previous versions) will not typically serve hidden content as this would defeat the very purpose of hiding files. The exception is in root partitions. IIS overlooks standard root partitions such as C:, D:, and so on, and works around this normal operating system behavior of hiding root volumes. On the other hand, mounted volumes are different because they are redirections and IIS will not overlook the fact that the root partition is hidden.

To correct this, you could either write a one-time script to change the attributes on the root partition for the mounted volume. Alternatively, you could write an ISAPI filter to re-map requests prior to processing. The first option, changing attributes, is more effective and more efficient because it doesn't affect run-time or production systems. It can be done once and be done. To change file attributes, you need only write a few lines of C code, compile and go.

I have included a sample script for you to use to guide you in the right direction.

#include "precomp.hxx" extern "C" INT __cdecl wmain( INT argc, PWSTR argv[] ) { if (argc != 2) { printf("Usage: %S <mount-directory-name>\n", argv[0]); return 1; } PWSTR pszDir = argv[1]; DWORD cchDir = wcslen(pszDir); if (pszDir[cchDir - 1] != L'\\') { printf("The mount-directory-name should terminate with a \\\n"); return 1; } WCHAR pszVolumeName[50]; if (!GetVolumeNameForVolumeMountPoint(pszDir, pszVolumeName, sizeof(pszVolumeName)/sizeof(WCHAR))) { printf("The directory %S does not seem to be a root mount point\n", pszDir); return 1; } if (!SetFileAttributes(pszDir, FILE_ATTRIBUTE_NORMAL)) { printf("SetFileAttributes failed - error %d\n", GetLastError()); return 1; } return 0; }

If you are interested in option 2, read the IIS SDK. It will help you understand how to build an ISAPI filter designed to modify requests prior to handing off to the IIS handler.

Centralizing Log Management to Reduce Data Mining with IIS 6.0

Fac2

Q. I have a fairly large shared IIS server at my company. We are trying to locate areas where we can simplify some management of the log files since handling over 1000 log files is somewhat tedious. We are running Windows Server 2003 with the latest updates.

A. Windows Server 2003 provides a new feature to use - the very powerful Centralized Binary Logging (CBL).

CBL is part of HTTP.sys (part of IIS 6.0). In Windows Server 2003, all logging except ODBC logging is done by the HTTP.sys kernel driver. This means that IIS 6.0 is no longer responsible for writing to disk the correct log data per request as it did in previous versions. CBL is a highly optimized log format where all request data is logged to one single file in binary. This unformatted binary data is nice for performance but hard on administrators like you. In fact, out of the box you have no effective mechanism to read the log files created when using CBL unless you write your own C++ program.

Log Parser 2.2 is an excellent command-line utility that you can use to both read and format the data. For more information about using Log Parser with CBL logs, watch my webcast: TechNet Webcast: The Ins and Outs of Centralized Binary Logging in IIS 6.0.

The great news I have for you is the even newer feature implemented in HTTP.sys for Service Pack 1. This feature merges two log formats together to create a high performing but well manageable logging mechanism. It is called W3C Centralized Logging. The key to this feature is that it takes advantage of the "centralized" log file in such cases as yours where the sites, directories and files are otherwise unmanageable.

With W3C Centralized logging, an administrator gets exactly one log file created in the W3SVC directory. This file adheres to the W3C standard and allows the logging of standard properties and extended properties, such as cookies presented to IIS and the Win32 error codes. These "extended" properties are not available to users of CBL.

In your situation, switching to W3C Centralized Logging would let you manage a single log file per day, hour, etc., based on your rollover mechanism. This is much better than the one-directory, several-files approach for IIS 6.0. In addition, you wouldn't need any special tools such as Log Parser 2.2 to view the data. The data would be viewable in your favorite text editor. Be warned, however, the file will usually be quite large, so the Notepad text editor may not suffice.

Unfortunately, enabling W3C Centralized logging is a bit more work to set up than a check box in a user interface. Just as with the CBL, W3C Centralized logging is not available in the IIS Manager and must be directly edited in the metabase. To do this, you just modify the CentralW3CLoggingEnabled metabase property, as follows:

cscript adsutil.vbs set w3svc/CentralW3CLoggingEnabled 1

After enabling this value, you can restart the IIS services and, upon restart, IIS will now be logging all requests for all sites using one formatted file that can be read using a text editor.

If your browser does not support inline frames, click here to view on a separate page.

Figure 1: W3C Centralized logging has been enabled

Troubleshooting ASP.NET Page Output Caching

Fac3

Q. During stress testing of our new ASP.NET application, the developers said that we should see a performance increase since they are using ASP.NET's output cache feature (https://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnaspnet/html/asp03282002.asp) to store this dynamic request in the kernel. This doesn't seem to be working according to our testing. After we enable outputcache, it is still failing. What are we missing?

A. The new kernel driver, HTTP.sys, is very robust. This driver avoids the necessary transition from kernel-mode to user-mode and this increases performance significantly - in many cases, up to four times as fast as user-mode cached items.

The key to your problem is whether or not you have successfully followed each minor detail related to making this work. In this answer, I am going to do my best to give you the possible detail you may be missing. However, your problem could have a number of different causes.

The first step is to make sure that your ASP.NET application follows several required rules to be cacheable by the kernel. These are outlined in both the IIS 6.0 documentation and the Microsoft Knowledge Base article 817445.

After you know for certain that your ASP.NET application satisfies these criteria, you should ensure that you have the appropriate settings for any filters you have installed. ISAPI filters are designed to modify requests made to the server and responses sent by the server. This criteria makes kernel-cache problematic since the requests are never processed by IIS. Thus, for kernel caching to work effectively, administrators need to ensure that IIS 6.0's ISAPI filters installed all have the FilterEnableCache metabase property set to True (or non-zero).

To find a list of ISAPI filters currently loaded for your server or sites, do the following:

Use either the command-line utility adsutil.vbs or the IIS 6.0 Resource Kit Tools utility, Metabase Explorer (see Figure 2).

If your browser does not support inline frames, click here to view on a separate page.

Figure 2: FilterEnableCache in the IIS Metabase Explorer

By default, IIS 6.0 ships with only two ISAPI filters. The ASP.NET filter and the FrontPage ISAPI filter. Both of these filters are "cache" friendly filters and are enabled properly by default. This problem would only occur if you have loaded custom applications that are ISAPI filter-based or if you have written your own ISAPI filter.

If FilterEnableCache is set to 0, then all Kernel caching is turned off. However, simply setting it to 1 (non-zero) does not validate that caching is immediately turned on. To enable it and use its advantages, you must ensure that the filters with this property set do not take certain actions. As you know, filters are designed to modify the behavior of the server. They do this by taking actions based on the requests made to the server. The action is determined during the parsing of HTTP headers. It is these actions that could potentially invalidate the fact that you have enabled the filter to be cache-aware. For example, there are some filters that might modify the actual URL requested (/vdir1 is "re-mapped" to /vdir2), or possibly listen for the following notifications "SF_NOTIFY_SEND_RAW_DATA" or "SF_NOTIFY_LOG" . If these actions are not being taken then kernel caching will work as planned.

To summarize, know what filters are loaded on your server and what they do. This will help you better troubleshoot failures such as the one you describe.

The next step moves more into the development aspect of the ASP.NET application. In order for caching to be enabled, the page should be marked as cacheable and also the Web application should be set to use kernel cache.

To enable page-level OutputCache, you could add the following directive at the top of your page:

<%@ outputcache duration="60" varybyparam="*" %>

After enabling this feature you should see a slight performance increase, because ASP.NET will cache the data internally for quicker retrieval by other clients. However, this does not enable kernel caching and still requires the kernel-to-usermode transition. This, as we mentioned, can be four times slower.

To enable your ASP.NET Web application to have responses cached "in kernel," the AppDomains root should modify the Web.config file in the content directory, as shown in Figure 3.

If your browser does not support inline frames, click here to view on a separate page.

Figure 3: Web Config File Modification

ASP.NET has some particular rules that you must followed, as shown in Table 1.

Table 1: ASP.NET Caching Rules

Cacheability

...needs to be Public (<%@ OutputCache Location="Any"%>

Additional Cache Dependencies

There should be NONE

Vary by...

...there should be no VaryByHeaders or VaryByParams (NONE)

Cache fields

Avoid using any custom cache fields

Validation

Do no validation callbacks

Verb usage

There should be no verb other than GET

No Store...

...is not set.

Expiration

...is set to a fixed interval and not a sliding

Compliance with these rules allows the httpRuntime for ASP.NET to permit the kernel driver HTTP.sys to cache the content.

For more information on EnableKernelOutputCache, see:https://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/scalenetchapt06.asp.

As you can see, this isn't a "flip-the-switch" feature because of the several different layers at which dynamic applications often work. You should ensure that each configuration setting is correct to take advantage of this cool IIS 6.0 and ASP.NET feature. I hope this helps you solve the problem!

For More Information

Submit your questions to the IIS Insider. A response is not guaranteed; however, selected questions along with the answers will be posted in a future IIS Insider column.

For a list of previous months' questions and answers on IIS Insider columns, click here.