Contact   •   Products   •   Search

Rick Strahl's Web Log

Wind, waves, code and everything in between...
ASP.NET • C# • HTML5 • JavaScript • AngularJs

Setting File or Directory permissions ACE entries with .NET using CACLS.EXE


Did you ever need to set an ACE (Access Control Entry) on a directory or a file? I’ve had plenty of applications in the past where I needed to set permissions on a directory or file. Most of these were application configuration applications. Usually this is required for automatically installing Web applications that get redistributed in some way.


I recently was working on a generic Web Configuration utility, that performs setup tasks for Web applications in a semi-generic way so that you can ship a generic install utility that can configure your Web application. One of the important features of this tool is the ability to set permissions on a folder.

 

Assigning permissions to a file or directory is one of those black art topics when it comes to doing it via code. I remember looking for examples in C++ for this a few years back as part of the West Wind Web Connection installation. At the time I gave up on it because it took a ton of really ugly code that I couldn’t figure out .

 

That was then, this is now and we’re still not a whole lot better off - .NET also doesn’t include ACE/L support (although .NET 2.0 is rumored to have a class to allow setting of directory permissions).

 

Regardless, it’s possible to fairly easily integrate this functionality by using the CACLS.EXE command line utility which is included in all versions of WinNT forward through XP and Win2003 Server. CACLS allows you to create individual ACE entries. With CACLS it's pretty easy to create ACE entries easily.

 

The following class encapsulates the basics of creating a single ACE entry.

 

///

/// This class sets an individual ACL value on a file or directory.

///

public class DirectoryPermission

{

      public DirectoryPermission()

      {

      }

 

      public DirectoryPermission(string Path, string Username, string UserRights)

      {

            this.Pathname = Path;

            this.Username = Username;

            this.UserRights = UserRights;

      }

 

      ///

      /// Directory Resource to assign ACL to. This is a physical disk path

      /// like d:\temp\test.

      ///

      public string Pathname = "";

 

      ///

      /// Username or group name to assign to this resource

      ///

      public string Username = "";

     

      ///

      /// Rights to assign for the given user or group.

      /// N - None

      /// R - Read

      /// C - Change

      /// F - Full

      ///

      public string UserRights = "R";

 

      ///

      /// Determines whether the permissions walk down all child

      /// directories.

      ///

      public bool InheritSubDirectories = true;

 

      ///

      /// When set overrides the existing ACLs and only attaches this

      /// ACL effectively deleting all other ACLs. Generally you won't

      /// want to do this and only change/add the current ACL.

      ///

      public bool OverrideExistingRights = false;

 

      ///

      /// Error Message set by SetAcl

      ///

      [XmlIgnore]

      public string ErrorMessage = "";

 

      ///

      /// Sets the actual ACL based on the property settings of this class

      ///

      ///

      public bool SetAce()

      {

            if ( this.Pathname == null || this.Pathname == "")

            {

                  ErrorMessage +=  "Path cannot be empty.";

                  return false;

            }

 

            string CommandLine = '"' + System.IO.Path.GetFullPath(this.Pathname) + '"' +

                                 " /C ";

 

            if (this.InheritSubDirectories)

                  CommandLine += @" /T ";

            if (!this.OverrideExistingRights)

                  CommandLine += @" /E ";

 

            CommandLine += @" /P " + this.Username + ":" + this.UserRights;

 

            Process p = new Process();

            p.StartInfo.FileName = "cacls.exe";

            p.StartInfo.Arguments = CommandLine;

            p.StartInfo.RedirectStandardOutput = true;

            p.StartInfo.UseShellExecute = false;

            p.StartInfo.WindowStyle = ProcessWindowStyle.Minimized ;

            p.StartInfo.CreateNoWindow = true;

 

            p.Start();

 

            string Response = p.StandardOutput.ReadToEnd();

 

            // *** If no output gets generated there's an error

            if (Response == null || Response == "")

            {

                  this.ErrorMessage += "Unable to set permissions on " +

                                       this.Pathname + " for " + this.Username ;

                  return false;

            }

 

            return true;

      }

}

 

 

This class works by simply using the Process class to execute the CACLS utility. It maps the various property settings to command line options and then simply executes the command.

 

You need to be an administrator in order to be able to set permissions on directories and to be able to execute external programs out of the application.

 

The routine redirects StandardOutput to an internal stream that’s checked for a result. If the result is empty the CACLS call failed. Otherwise the call worked and the routine returns .t.

 

The command line looks something like this to create a single ACE entry:

 

CACLS.EXE "d:\MyApp\WebPath" /T /P IUSR_RASNOTEBOOK:R

 

The /T switch specifies that subdirectories will inherit the settings. /P plus the user or group name followed by colon and one of the following first letters: Read, Change, Write, Full.

 

This is sort of a brute force way of performing this task but it works well and the class encapsulates the process in such a way that it’s non-intrusive for the calling application. The Console window that CACLS runs in is minimized so it doesn’t affect the desktop other than a quick flash in the taskbar. 

 

How would you use this? In the Web Configuration utility the code figures out the Anonymous Web User by querying IIS through ADSI, and adding this user to the application directory with Read attributes. It also checks for the IIS version and if it's IIS 5 add ASPNET and with IIS 6 NETWORKSERVICE and adds it with Full rights for the directory (this app needs to read the web.config file as well as write a couple of XML files). These rights are automatically added to a listbox and then shown in the UI. The actual routines then pick up the values in the list box and assign it to Configuration object which contains an ArrayList of DirectoryPermission objects. The code then simply loops through the list and calls the SetAce method to set the rights.

 

The assignment logic looks like this:

 

public bool AddDefaultWebPermissions()

{

      if (this.Virtual.Virtual == null || this.Virtual.Virtual == "")

            return true;

 

      VirtualDirectory vd = new VirtualDirectory();

 

      vd.IISPath = this.Virtual.IISPath;

      vd.Virtual = this.Virtual.Virtual;

 

      if (!vd.GetVirtual() )

      {

            if (! vd.GetVirtual("") )

            {

                  this.SetError(vd.ErrorMessage);

                  return false;

            }

      }

 

      // *** Add the Anonymous User

      string AnonymousUser = vd.AnonymousUserName;

      this.DirectoryPermissions.Add( new DirectoryPermission( this.Virtual.Path,AnonymousUser,"R") );

 

      if (vd.ServerType == WebServerTypes.IIS6)

      {

            this.DirectoryPermissions.Add( new DirectoryPermission( this.Virtual.Path,"NETWORKSERVICE","F") );

      }

      else

      {

            this.DirectoryPermissions.Add( new DirectoryPermission( this.Virtual.Path,"ASPNET","F") );

      }

 

      return true;

}

 

and to run the actual permissions:


 

public bool SetDirectoryPermissions()

{

      bool Error = false;

 

      // *** Create Directory Permissions

      foreach( DirectoryPermission dm in this.DirectoryPermissions )

      {

            if (!dm.SetAce() ) 

            {

                  this.SetError( dm.ErrorMessage );

                  Error = true;

            }

      }

 

      return !Error;

}

 

Setting directory permissions isn't something you do every day, but when you need it it usually is critical to avoid manual configuration.

 

You can apply this same logic to other languages easily enough. For example, here's a Visual FoxPro version that is now used as part of the West Wind Web Connection setup:

 

************************************************************************

* wwUtils :: SetACL

****************************************

***  Function:

***    Assume:

***      Pass: lcPath        -   Path or Filename to assign ACE to

***            lcUser        -   The Username to assign

***            lcAccess      -   [N]one, [R]ead, [C]hange, [F]ull

***            llInherit     -   Pass rights down the directory tree

***            llReplace     -   Replace rights on the resource

***                              (deletes all ACL entries except this one)

***    Return:

************************************************************************

FUNCTION SetACL(lcPath,lcUser,lcAccess,llInherit,llReplace)

LOCAL lcCommand, lcFile

 

*** Strip off any trailing backslashes

IF RIGHT(lcPath,1)="\" AND LEN(lcPath) > 1

   lcPath = SUBSTR(lcPath,1,LEN(lcPath)-1)

ENDIF

  

lcCommand = "RUN Cacls.exe " + ShortPath( lcPath)

 

IF llInherit

   lcCommand = lcCommand + " /T "

ENDIF

IF !llReplace

   lcCommand = lcCommand + " /E "

ENDIF

 

lcCommand = lcCommand + " /P " + lcUser + ":" + lcAccess + " > cacls.txt"

&lcCommand

 

lcFile = ""

IF FILE("Cacls.txt")

   lcFile = FILETOSTR("cacls.txt")

   ERASE Cacls.txt

ENDIF

IF EMPTY(lcFile)

   RETURN .F.

ENDIF

 

RETURN .T.

ENDFUNC

*  wwUtils :: SetACL

 

 

Remember, that even though you can do this under full program control, setting permissions on a directory or file is a super high trust operation and as such may affect the system security significantly. It's good form to always let the user know that you are making this sort of change with a dialog. Also, you should only set permissions if absolutely necessary and always try to minimize the paths that are affected by it.

 

Make Donation


Feedback for this Post

 
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
by --sb-- November 06, 2004 @ 7:50am
If WMI is an option, check 'Create Home Directories and Set NTFS Permissions with a Web Script' at http://www.winnetmag.com/WindowsScripting/Articles/ArticleID/22048/pg/1/1.html
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
by Mike November 07, 2004 @ 1:30am
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
by Rick Strahl November 07, 2004 @ 9:41pm
I had looked at the GotDotNet Workspace before but the code was very buggy at the time. The nice thing about CACLS.EXE is that it's easy to deal at least for the base scenario outlined here. If more control is needed and you need to deal more concisely with SIDs and complex manipulation of the ACL then a class like that one (there is one on CodeProject too) will likely be a better choice. For the simple tasks though CACLS works great. As to security rights - well you'll need an admin account to do any of this anyway so that shouldn't be much of a consideration.
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
by David January 15, 2005 @ 9:39pm
I'm using following VB code and it doesn't work...



Imports System
Imports System.Diagnostics

Public Class FileSecurity

Public Shared Function SetSecurity() As String

Dim _cmdLine As String = "c:\dave /t /P ASPNET:F"
Try
Dim p As Process = New Process
p.StartInfo.FileName = "cacls.exe"
p.StartInfo.Arguments = _cmdLine
p.StartInfo.RedirectStandardOutput = True
p.StartInfo.UseShellExecute = False
p.StartInfo.WindowStyle = Diagnostics.ProcessWindowStyle.Minimized
p.StartInfo.CreateNoWindow = True
p.Start()
'Return p.StandardOutput.ReadToEnd.ToString
Catch ex As Exception
Return ex.Message
End Try

End Function

End Class
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
by Rick Strahl January 16, 2005 @ 11:54am
Yes it works in ASP.NET, but you need to have permissions for it. You need admin rights to do this which probably means you need to set up the page with Impersonation (or set up your NETWORK SERVICE/ASPNET account with Admin rights).
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
by Sean Kinsey January 27, 2005 @ 12:49pm
There is one error though, and that is that the cacls utility requires the user to confirm by pressing y.
This can normaly be done by piping y with 'echo y| cacls xx' but this is not something that I have managed to do with the process class.
I am now going to use a bit more time than the 5 minutes I have used so far to find a solution..
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
by Sean Kinsey January 27, 2005 @ 12:55pm
I solved this quite quickly by using this:
p.StartInfo.FileName = "cmd.exe"
p.StartInfo.Arguments = "/c echo y| cacls " & folderPath & " /T /G " & userName & ":F"
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
by Terry February 02, 2005 @ 2:37am
Is there an equivalent application to set group right s?
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
by Prasad Somwanshi March 17, 2005 @ 2:21am
I've similar code in my application.
But it seems it doesn't work on european lang. like german on french.
# re: doesn't work when path contain space in CACLS.EXE
by hhe May 09, 2005 @ 10:56am
Hi,
Do you knlw why cacls doesn't work when path contains a space? I gave the following command in DOS environment:
cacls.exe c:\program files\my company\my product /T /E /G users:C

thanks,
hhe
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
by Bobba Fett May 09, 2005 @ 12:47pm
Try quotes around the path.
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
by blom May 09, 2005 @ 2:30pm
I'm hitting an odd error:
using EITHER xcacls OR cacls, I'm unable to assign rights to a group..if the group name is more then 14 characters...
In these examples...the process fails..until I rename the group with one less character..it is the ONLY change I make.
I can reproduce this consistently.

Any help is appreciated.
blom

C:\xcacls.exe c:\temp\Generally /G domain\Generally_SList:R /E /Y
No mapping between account names and security IDs was done.

C:\xcacls.exe c:\temp\Generally /G domain\Generally_List:R /E /Y
processed dir: c:\temp\Generally
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
by blom May 11, 2005 @ 2:53pm
Never Mind..I figured out the problem..the xcacls, and cacls tools were keying off the "Windows 2000" group name (the truncated version of the full name) ..so until they 'matched'. the tool appeared to fail.
I fixed my problem by altering my script to reference only the truncated version of the name.
Though ya might like to know..
Cheers!
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
by Robert May 16, 2005 @ 8:29am
is it possible to get calcs.exe to set read permissions with out the execute and list folder contents ?
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
by jander May 24, 2005 @ 12:28am
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
by Dude June 08, 2005 @ 2:21pm
cacls only works with 8 character folder names.

i.e.
long file path:
C:\Program Files\TestFolder
short file path:
C:\Progra~1\TestFo~1

vbcode to convert long file name to short:

Private Declare Function GetShortPathName Lib "kernel32.dll" Alias "GetShortPathNameA" _
(ByVal lpszLongPath As String, ByVal lpszShortPath As String, ByVal cchBuffer _
As Integer) As Integer

Private Function ShortPath(ByVal LongPath As String) As String
Dim shrtPath As String = Space(255)
GetShortPathName(LongPath, shrtPath, shrtPath.Length)

Return shrtPath.Substring(0, InStr(shrtPath, Chr(0)) - 1)
End Function
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
by Augustine September 14, 2005 @ 1:32am
Any idea how do i fix the Access is Denied problem?


Public Function SetSecurity() As String

' Dim _cmdLine As String = "c:\dave /t /P ASPNET:F"
Try
Dim p As Process = New Process
p.StartInfo.FileName = "cmd.exe"
p.StartInfo.Arguments = "/c echo y| cacls " & "C:\Test" & " /T /G " & "DOMAIN\5541192" & ":F"
p.StartInfo.RedirectStandardOutput = True
p.StartInfo.UseShellExecute = False
p.StartInfo.WindowStyle = Diagnostics.ProcessWindowStyle.Minimized
p.StartInfo.CreateNoWindow = True
p.Start()
'Return p.StandardOutput.ReadToEnd.ToString
Return "done"

lbxLogging.Items.Add("RP\5541192 added")
Catch ex As Exception
Return ex.Message
lbxLogging.Items.Add(ex.Message)
End Try
Console.ReadLine()
End Function
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
by mona November 17, 2005 @ 3:01am
Realy it's so useful Code but i wanna getting the permission of specified file .i can get is_full_control or not only
what about is_read_only
and is_execute or not
and is_write_only
and is_read_write
Thanks in advance
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
by Rick Strahl November 17, 2005 @ 3:42am
FWIW, in .NET 2.0 there's built in support for ACL/ACE control for both reading and writing.

Take a look here:
http://msdn.microsoft.com/msdnmag/issues/04/11/AccessControlinNET/default.aspx#S8

You can also use CACLS to read existing permissions and parse it out of the result in 1.1.
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
by mona November 17, 2005 @ 5:03am
Many Thanks i parsed the result and it's done
Thanks 4 ur help
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
by Agus November 24, 2005 @ 11:30pm
How can I do this with VS 2003 Framework 1.1?

Thanks,
Agus
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
by Paul Noeldner November 29, 2005 @ 12:29pm
Nobody should have to resort to 1980's command line shell syntax in 2005. The NET object framework should certainly provide a full set of functions against the major objects in the environment including the file system, from within the primary supported languages via properties and methods. Checking and setting security programmatically should no longer be an 'afterthought' left out of the framework.
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
by Rick Strahl November 29, 2005 @ 12:29pm
Paul - It isn't <g> - .NET 2.0 includes classes that allow doing this programmatically. But prior - man using Win32 APIs to do this, or even WMI which does support this - was a royal pain in the ass.

# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
by karunakar.gadde December 18, 2005 @ 2:08am
I am not able to supress the "Are you sure Y/N" Option in Cacls. i want to get is cacls command is successfull or not also.
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
by karunakg@mahindrabt.com December 18, 2005 @ 2:09am
I am not able to supress the "Are you sure Y/N" Option in Cacls. i want to get is cacls command is successfull or not also from my vb.net program.
# Setting ACE/ACL permissions in .NET 2.0
by Rick Strahl's WebLog January 04, 2006 @ 12:26am
Setting ACE/ACLs in .NET 2.0 got a bit easier with managed wrapper support built into the BCL. But these classes are still pretty complex if you're not intricately familiar with how these security constructs work. It takes a fair amount of code to do this seemingly common and simple task, and the question begs why nobody went through the excercise of creating a more user friendly class front end.
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
by Rick Strahl January 04, 2006 @ 1:13am
Regarding surpressing the Y/N option in XP: Youc can use write to Standard "Y\r\n" into the stream to force the letters to be sent. This works with Win2003 as well even though it doesn't use those extra characters.

Here's the updated code:

Process p = new Process();
p.StartInfo.FileName = "cacls.exe";
p.StartInfo.Arguments = CommandLine;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.UseShellExecute = false;
p.StartInfo.WindowStyle = ProcessWindowStyle.Minimized;
p.StartInfo.CreateNoWindow = true;

p.Start();

p.StandardInput.Write("Y\r\n");
string Response = p.StandardOutput.ReadToEnd();

# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
by Mo January 09, 2006 @ 4:15am
I am unable to use cacls.exe when long file names are used for folder. I tried putting quotes around the path and then call cacls.exe but I am still getting "Unable to set permissions on "C:\My Long File Path" for ASPNET\MACHINE
Please help :)
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
by Mo January 09, 2006 @ 6:00am
Never mind. I was putting an extra backslash :)
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
by Anil February 14, 2006 @ 10:46pm
Hi,

I am stuck with a problem where the application which we have depeloed require autoupgrade, but when a limited access user have logged in he is not able to do that as write permissions are not allocated to him on Program Files Foler.
Could this code also be used for the same.

I want some utility so that access to that folder is given for some amount of time till autoupgrade complete.
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
by Walt September 12, 2006 @ 12:59pm
Worked like a charm...just wanted to say thanks.
# Setting ACE/ACL permissions in .NET 2.0 - Rick Strahl's Web Log
by Rick Strahl's Web Log November 14, 2006 @ 2:53pm
Setting ACE/ACLs in .NET 2.0 got a bit easier with managed wrapper support built into the BCL. But these classes are still pretty complex if you're not intricately familiar with how these security constructs work. It takes a fair amount of code to do this seemingly common and simple task, and the question begs why nobody went through the excercise of creating a more user friendly class front end.
# Jamie’s Blog » Blog Archive » VFP Database Security
by Jamie's Blog March 15, 2007 @ 12:23am
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
by granadaCoder January 13, 2010 @ 10:30am
Great write up! This is very helpful in continuous integration as well.

A couple of "hints":

/E Edit ACL instead of replacing it.
That's a very important argument! I am just pointing it out and bringing attention to it and your code ( !llReplace ) . I accidentally replaced mine instead of updating them, and had to go back and put back the ones I removed. :<


icacls.exe may be the Windows Server 2008 "update". I think 2003 SP2 includes icacls.exe as well.

Thanks for another great write up!
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
by Narendra February 04, 2010 @ 3:46am
Hi Rick
i followed this process to create full control to a perticular user.
what i observed is
i have a folder path 'E:\testing', initially on rightclick-->properties-->security tab i have 5 user names.
i ran this command
"CACLS.EXE "'E:\testing" /T /P nreddy:F"
then again i opened security tab but there i hav only one user which i mentioned in command(nreddy) with full control

what happen to remaining 4 users?
those 4 users got deleted for this perticular path???

is there any way to give full control to a perticular user without deleting other users permissions?
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
by mr_3ntropy February 09, 2010 @ 10:33pm
Narendra

Yes. You forgot the /E switch.
E for Edit. If you don't use it, that means you want to replace everything.
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
by vijay sharma January 24, 2011 @ 11:11pm
hello sir,
This is vijay from Mumbai,INDIA.This is the nice things that you wrote.Sir ,same thing i was try to run,but its not woking for my web-site.But its working fine for offline.I have a some.dat file & some.EXE.By using the some.EXE,i have to use some.dat.At my local machine its working good,as we want.But when we are going to put the things at server,there is no error at runtime,but the the some.exe is not enable to call the some.dat , as i think.Kindly guide me.Waiting for your response.

Have a nice time & good day ahead.

Thanks with Regards,

Vijay Sharma
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
by leomark February 01, 2012 @ 11:40am
# re: Setting File or Directory permissions ACE entries with .NET using CACLS.EXE
by Tharindu March 08, 2012 @ 12:16am
               string Pathname = folder;
                EventLog.WriteEntry(Pathname);
                string Username = @"BUILTIN\Users";
                string UserRights = "N";
 
                if (Pathname == null || Pathname == "")
                {
                    EventLog.WriteEntry("No File");
                }
 
                string CommandLine = '"' + System.IO.Path.GetFullPath(Pathname) + '"' + " /C ";
                CommandLine += @" /T ";
                CommandLine += @" /P " + Username + ":" + UserRights;
 
                Process p = new Process();
                p.StartInfo.FileName = "cacls.exe";
                p.StartInfo.Arguments = CommandLine;
                p.StartInfo.RedirectStandardOutput = true;
                p.StartInfo.RedirectStandardInput = true;
                p.StartInfo.UseShellExecute = false;
                p.StartInfo.WindowStyle = ProcessWindowStyle.Minimized;
                p.StartInfo.CreateNoWindow = true;
 
                p.Start();
                p.StandardInput.Write("Y\r\n"); 
                string Response = p.StandardOutput.ReadToEnd();
                EventLog.WriteEntry(Response);
                if (Response == null || Response == "")
                {
                    EventLog.WriteEntry("Error");
                }

tell me why this not working in windows service?????
 


West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2014