Отправляет email-рассылки с помощью сервиса Sendsay
  Все выпуски  

NTFS : Управление квотами (часть I)


Информационный Канал Subscribe.Ru

Программирование для Windows NT на Delphi
Выпуск номер : 4 [ 30 июля 2002 года]


Здравствуйте, уважаемые подписчики.

Сначала хочу напомнить еще не подписавшимся получателям пробных выпусков рассылки : это последний пробный выпуск и если вы хотите подписаться на эту рассылку, то сделайте это прямо сейчас (форма для подписки должна быть вверху).

Этот выпуск немного уменьшенный (хотел написать больше). Это связано с тем, что в конце письма находится мой вариант перевода заголовочного файла dskquota.h на Pascal. Хотел его выложить на сайте, но там сейчас технические трудности. Если вы обнаружите в нем ошибки, то пожалуйста напишите мне flint@vtc.ru.
И еще - все, кто писал мне (примерно с конца позапрошлой недели) и не получил ответа, повторите пожалуйста ваше письмо (а то были технические проблемы) на flint@vtc.ru.

Сегодня в рассылке:
- Файловые системы в Windows NT
- dskquota.pas


Статья
Файловые системы в Windows NT


В Windows NT в основном используются файловые системы FAT (FAT16 и с Win2000 FAT32) и NTFS. Просто перечислю несколько достоинств и недостатков каждой.

FAT16 самая универсальная файловая система - она поддерживается DOS и всеми известными Windows. Платой за универсальность является - низкая эффективность (размер кластера для диска размером 1024Mb равен 32Kb), максимальный размер тома ограничен 4Гб и ограничение на кол-во файлов, находящихся в корне диска (максимум 256).

FAT32 лишена многих недостатков FAT16 : максимальный размер в Windows 2000 - 32Гб (теоретически - до 8 терабайт), размер кластера для диска в 1024Mb - 4Kb и нет ограничения на кол-во файлов в корне диска + она хранит вторую копию загрузочного сектора. Но она не будет работать в старых версиях DOS :)) и в Windows NT 4 и ниже (Хотя существуют драйвера FAT32 для NT4 и при чем неплохо работают, но загрузить с FAT32 NT4 не удастся.

NTFS - родная для NT файловая система. Максимальный размер тома - 128 терабайт (я даже не знаю, проверяли ли это на практике :). Размер кластера по умолчанию меньше, чем в FAT32 - 1Kb для диска в 1024Mb (хотя это некоторым не нравится ;-). Но в NTFS есть ряд дополнительных возможностей - ограничение доступа к файлам, сжатие файлов, индексация фалов, полная поддержка Unicode, поддержка POSIX (ну это так, к слову). Также в NTFS5 (Win2000 и далее) поддерживаются следующие возможности - шифрование, отслеживание ссылок, разряженные файлы, поддержка дефрагментации, жесткие ссылки, точки соединения, переназначение плохих секторов, множественные потоки данных и квоты на размер дискового пространства. В этом и нескольких следующих выпусках мы разберем, как работать с этими возможностями. А пока начнем с квот.

Квоты позволяют вам ограничивать размер дискового пространства для определенных пользователей. При этом возможно при превышении квоты запретить пользователю записывать на диск (здесь и далее под словом диск имеется в виду логический диск (раздел)). Использование квот возможно только на Win2000 и далее. Хотя есть решение (а возможно даже не одно) и для NT4, но оно от сторонних разработчиков, несколько кривовато и легко обходится. Но здесь будет описываться именно квоты в NTFS5.

Управлять квотами можно с помощью закладки Квота в свойствах диска. Но видна эта закладка будет только администраторам, и вообще квотами могут управлять только они.

Для управления квотами используется COM-объект и COM-интерфейсы : IDiskQuotaControl, IDiskQuotaUser и еще 3 штуки. Работе с COM я учить не буду (меня самого можно этому учить :) - по этой теме можно найти много материала. И еще - чтобы не вдаваться в подробности интерфейса(внешнего вида) программы представьте, что бы пишем эту самую закладку Квоты. И еще маленькое дополнение - в C++ все COM-интерфейсы описаны в файле dskquota.h . Аналогичного файла для Delphi я не нашел - пришлось перевести все руками. Найти мой вариант можно в конце рассылки (буду очень рад, если укажите на допущенные в нем ошибки).

В uses добавляем dskquota{это мой файл},ComObj,ActiveX;

Quota : IDiskQuotaControl; // COM-интерфейс - сделаем его глобальной переменной

При открытии нашей закладки выполняем инициализацию COM.
var
 Res : HRESULT;

Res:=CoCreateInstance(CLSID_DiskQuotaControl,nil,
      CLSCTX_INPROC_SERVER,IDiskQuotaControl,Quota);
if FAILED(Res) then ShowMessage('Error in CoCreateInstance'); 

В общем, каждая функция интерфейсов возвращает результат своего выполнения. Проверить его можно, например, функцией FAILED(), возвращающей TRUE в случае ошибки. Так как такие проверки занимают огромное место, то я их здесь приводить не буду.

Теперь нам надо инициализировать объект и сопоставить ему диск. Например, диск E: Quota.Initialize('e:\',true);
Теперь любые операции с Quota будут выполняться с диском e:. true во втором параметре обозначает полный доступ. И еще - все строки только Unicode!!! т.е. типа PWideChar, а не PChar.

Теперь нам надо расставить все данные о состоянии.Сначала получим эти данные -
var State : Cardinal;

Quota.GetQuotaState(State);
В State теперь находится информация о состоянии.
"Включить управление квотами" = not DiskQuotaIsDisabled(State);
Данная строка показывает, что галочка напротив "Включить управление квотами" должна появится если результат функции DiskQuotaIsDisabled будет равен FALSE. Функция DiskQuotaIsDisabled есть в dskquota.pas

"Не выделять место на диске при превышении квоты" = DiskQuotaIsEnforced(State);

Теперь получим "квоты по умолчанию для нового пользователя этого тома".
var
 Limit : Int64;

Quota.GetDefaultQuotaLimit(Limit);
if Limit=-1 //т.е. квота на ограничена
 then "Не ограничивать выделение места на диске"
  else begin
   "Выделять на диске не более" := Format('%g',[Limit/1024]); //килобайт.

   Quota.GetDefaultQuotaThreshold(Limit);
   "Порог выдачи предупреждений":= Format('%g',[Limit/1024]); //килобайт.
  end;
Если нам надо получить эти значения в тексте, например, "243 Mb" или "не ограничено", то воспользуемся соответственно GetDefaultQuotaLimitText и GetDefaultQuotaThresholdText. Пример,
var LimitTxt : array[0..255] of WideChar;

Quota.GetDefaultQuotaLimitText(LimitTxt,255);
LimitText.Caption:=LimitTxt;
И еще остались два checkbox'а в "Протоколирование превышения квоты для этого тома".
var LogState : Cardinal;

Quota.GetQuotaLogFlags(LogState);
"Регистрация превышения квоты пользователем":=DiskQuotaIsLoggedUserLimit(LogState);
"Регистрация превышения порога предупреждения":=DiskQuotaIsLoggedUserThreshold(LogState);


Все это красиво организовывается в функцию, например, GetQuotaInfo. Про SetQuotaInfo чуть позже, а пока коротко о некоторых вопросах :
Выключенная "Не выделять место на диске при превышении квоты" означает то, что пользователь может записывать больше положенного. При включенной опции и попытке перерасхода квоты юзер получает сообщение "Нет места на диске". При чем при расчете занятого места используется номинальный размер, а не фактический (т.е. 10 мегабайтный файл, сжатый (средствами NTFS) до 5Mb при расчете квоты будет считаться как 10 мегабайтный).

Квота по умолчанию применяется к пользователям, первый раз записавшим данные на диск.

Результаты протоколирования будут в журнале системы (eventvwr.exe). Например, такие:
Источник : ntfs  Категория : диск
Пользователь : COMPUTER\User
Пользователь достиг предела по квоте на томе E:

Обратная операция - устанавливаем измененные данные.
var
 State,LogState : Cardinal;
 Res : HRESULT;
 Limit : Int64;
begin

 State:=0;
 if not "Включить управление квотами"
  then DiskQuotaSetDisabled(State)
   else begin
    if "Не выделять место на диске при превышении квоты"
   then DiskQuotaSetEnforced(State)
       else DiskQuotaSetTracked(State);
   end;

 Res:=Quota.SetQuotaState(State);
 if FAILED(Res) then ShowMessage('FAILED');

 if  "Не ограничивать выделение места на диске"
  then Limit:=-1
   else Limit:=StrToInt(LimitSize.Text)*1024; // т.к. у нас в килобайтах

 Quota.SetDefaultQuotaLimit(Limit);

 //"Порог выдачи предупреждений"
 //устанавливается Quota.SetDefaultQuotaThreshold()

 DiskQuotaSetLogUserTreshold(LogState,CheckBox2.Checked);
 DiskQuotaSetLogUserLimit(LogState,CheckBox1.Checked);
 Quota.SetQuotaLogFlags(LogState);
end;

Продолжение будет в следующем выпуске.


Заголовочный файл dskquota.pas
Перевод файла dskquota.h из VC7 на Pascal

unit dskquota;

interface
uses Windows,ActiveX;

const
 CLSID_DiskQuotaControl : TGUID = '{7988B571-EC89-11cf-9C00-00AA00A14F56}';

type
 LONGLONG = int64;
 ULONG    = Cardinal;

//
// Definitions for value and bits in DWORD returned by
// IDiskQuotaControl::GetQuotaState.
//
const
  DISKQUOTA_STATE_DISABLED            = $00000000;
  DISKQUOTA_STATE_TRACK               = $00000001;
  DISKQUOTA_STATE_ENFORCE             = $00000002;
  DISKQUOTA_STATE_MASK                = $00000003;
  DISKQUOTA_FILESTATE_INCOMPLETE      = $00000100;
  DISKQUOTA_FILESTATE_REBUILDING      = $00000200;
  DISKQUOTA_FILESTATE_MASK            = $00000300;

//
// Definitions for bits in DWORD returned by
// IDiskQuotaControl::GetQuotaLogFlags.
//
const
  DISKQUOTA_LOGFLAG_USER_THRESHOLD    = $00000001;
  DISKQUOTA_LOGFLAG_USER_LIMIT        = $00000002;

//
// Values for fNameResolution argument to:
//
//      IDiskQuotaControl::AddUserSid
//      IDiskQuotaControl::AddUserName
//      IDiskQuotaControl::FindUserSid
//      IDiskQuotaControl::CreateEnumUsers
//
  DISKQUOTA_USERNAME_RESOLVE_NONE     = 0;
  DISKQUOTA_USERNAME_RESOLVE_SYNC     = 1;
  DISKQUOTA_USERNAME_RESOLVE_ASYNC    = 2;

//
// Values for status returned by IDiskQuotaUser::GetAccountStatus.
//
  DISKQUOTA_USER_ACCOUNT_RESOLVED     = 0;
  DISKQUOTA_USER_ACCOUNT_UNAVAILABLE  = 1;
  DISKQUOTA_USER_ACCOUNT_DELETED      = 2;
  DISKQUOTA_USER_ACCOUNT_INVALID      = 3;
  DISKQUOTA_USER_ACCOUNT_UNKNOWN      = 4;
  DISKQUOTA_USER_ACCOUNT_UNRESOLVED   = 5;


type
 TDiskQuotaUserInformation = record
   QuotaUsed      : LONGLONG;
   QuotaThreshold : LONGLONG;
   QuotaLimit     : LONGLONG;
 end;
 PDISKQUOTA_USER_INFORMATION  = ^PDISKQUOTA_USER_INFORMATION;
 DISKQUOTA_USER_INFORMATION   = TDiskQuotaUserInformation;


//
// IDiskQuotaUser represents a single user quota record on a particular
// NTFS volume.  Objects using this interface are instantiated
// through several IDiskQuotaControl methods.
//
type
 PPDiskQuotaUser = ^PDiskQuotaUser;
 PDiskQuotaUser = ^IDiskQuotaUser;
 IDiskQuotaUser = interface
  ['{7988B574-EC89-11cf-9C00-00AA00A14F56}']
   function GetID(var pulID : ULONG):HRESULT;stdcall;
   function GetName(
     pszAccountContainer : PWideChar;
     cchAccountContainer : DWORD;
     pszLogonName        : PWideChar;
     cchLogonName        : DWORD;
     pszDisplayName      : LPWSTR;
     cchDisplayName      : DWORD):HRESULT;stdcall;

    function GetSidLength(var pdwLength : DWORD):HRESULT;

    function GetSid (
        bSidBuffer : Pointer;
        cbSidBuffer : DWORD):HRESULT;stdcall;

    function GetQuotaThreshold (
        var pllThreshold : LONGLONG):HRESULT;stdcall;

    function GetQuotaThresholdText (
        pszText : LPWSTR;
        cchText : DWORD):HRESULT;stdcall;

    function GetQuotaLimit (
        var pllLimit : LONGLONG):HRESULT;stdcall;

    function GetQuotaLimitText (
        pszText : LPWSTR;
        cchText : DWORD):HRESULT;stdcall;

    function GetQuotaUsed (
        var pllUsed : LONGLONG):HRESULT;stdcall;

    function GetQuotaUsedText (
        pszText : LPWSTR;
        cchText : DWORD):HRESULT;stdcall;

    function GetQuotaInformation (
        pbQuotaInfo : Pointer;
        cbQuotaInfo : DWORD):HRESULT;stdcall;

    function SetQuotaThreshold (
        llThreshold   : LONGLONG;
        fWriteThrough : BOOL):HRESULT;stdcall;

    function SetQuotaLimit (
        llLimit : LONGLONG;
        fWriteThrough : BOOL):HRESULT;stdcall;

    function Invalidate():HRESULT;stdcall;

    function GetAccountStatus (
        var pdwStatus : DWORD):HRESULT;stdcall;
 end;

//
// IEnumDiskQuotaUsers represents an enumerator created by
// IDiskQuotaControl for the purpose of enumerating individual user quota
// records on a particular volume.  Each record is represented through
// the IDiskQuotaUser interface.
//
type
 PPEnumDiskQuotaUsers = ^PEnumDiskQuotaUsers;
 PEnumDiskQuotaUsers = ^IEnumDiskQuotaUsers;
 IEnumDiskQuotaUsers = interface(IUnknown)
  ['{7988B577-EC89-11cf-9C00-00AA00A14F56}']
    function Next(
      cUsers  : DWORD;
      rgUsers  : PPDiskQuotaUser;
      var pcUsersFetched : DWORD):HRESULT;stdcall;

    function Skip(cUsers : DWORD):HRESULT;stdcall;
    function Reset():HRESULT;stdcall;
    function Clone(ppEnum : PPEnumDiskQuotaUsers):HRESULT;stdcall;
 end;

//
// IDiskQuotaUserBatch represents a collection of IDiskQuotaUser
// pointers for the purpose of grouping updates to quota information.
//
type
 PPDiskQuotaUserBatch = ^PDiskQuotaUserBatch;
 PDiskQuotaUserBatch = ^IDiskQuotaUserBatch;
 IDiskQuotaUserBatch = interface
   ['{7988B576-EC89-11cf-9C00-00AA00A14F56}']
    function Add (pUser : PDiskQuotaUser):HRESULT;stdcall;
    function Remove (pUser : PDiskQuotaUser):HRESULT;stdcall;
    function RemoveAll ():HRESULT;stdcall;
    function FlushToDisk ():HRESULT;stdcall;
 end;

//
// IDiskQuotaControl represents a disk volume, providing query and
// control of that volume's quota information.
//
type
 PDiskQuotaControl = ^IDiskQuotaControl;
 IDiskQuotaControl = interface (IConnectionPointContainer)
  ['{7988B572-EC89-11cf-9C00-00AA00A14F56}']
    function Initialize (
        pszPath    : LPWSTR;
        bReadWrite : LongBOOL):HRESULT;stdcall;

    function SetQuotaState (dwState : DWORD):HRESULT;stdcall;
    function GetQuotaState (var pdwState : DWORD):HRESULT;stdcall;
    function SetQuotaLogFlags (dwFlags : DWORD):HRESULT;stdcall;
    function GetQuotaLogFlags (var pdwFlags : DWORD):HRESULT;stdcall;
    function SetDefaultQuotaThreshold (llThreshold : LONGLONG):HRESULT;stdcall;
    function GetDefaultQuotaThreshold (var pllThreshold : LONGLONG):HRESULT;stdcall;

    function GetDefaultQuotaThresholdText (
        pszText : LPWSTR;
        cchText : DWORD):HRESULT;stdcall;

    function SetDefaultQuotaLimit(llLimit : LONGLONG):HRESULT;stdcall;
    function GetDefaultQuotaLimit (var pllLimit : LONGLONG):HRESULT;stdcall;

    function GetDefaultQuotaLimitText(
        pszText : LPWSTR;
        cchText : DWORD):HRESULT;stdcall;

    function AddUserSid (
        pUserSid : PSID;
        fNameResolution : DWORD;
        var ppUser : PDiskQuotaUser):HRESULT;stdcall;

    function AddUserName (
        pszLogonName : LPCWSTR;
        fNameResolution : DWORD;
        var ppUser : PDiskQuotaUser):HRESULT;stdcall;

    function DeleteUser (
        ppUser : PDiskQuotaUser):HRESULT;stdcall;

    function FindUserSid (
        pUserSid : PSID;
        fNameResolution :DWORD;
        var ppUser : PDiskQuotaUser):HRESULT;stdcall;

    function FindUserName (
        pszLogonName : LPCWSTR;
        var ppUser : PDiskQuotaUser):HRESULT;stdcall;

    function CreateEnumUsers (
        rgpUserSids : Pointer;
        cpSids : DWORD;
        fNameResolution : DWORD;
        ppEnum : PPEnumDiskQuotaUsers):HRESULT;stdcall;

{    function CreateEnumUsers (
        rgpUserSids : Pointer;
        cpSids : DWORD;
        fNameResolution : DWORD;
        var ppEnum : PEnumDiskQuotaUsers):HRESULT;stdcall;
}
    function CreateUserBatch ({var} ppBatch : PPDiskQuotaUserBatch):HRESULT;stdcall;
    function InvalidateSidNameCache ():HRESULT;stdcall;
    function GiveUserNameResolutionPriority (pUser : PDiskQuotaUser):HRESULT;stdcall;
    function ShutdownNameResolution ():HRESULT;stdcall;

 end;
//typedef IDiskQuotaControl DISKQUOTA_CONTROL, *PDISKQUOTA_CONTROL;


type
 IDiskQuotaEvents = interface
  ['{7988B579-EC89-11cf-9C00-00AA00A14F56}']
   function OnUserNameChanged(pUser : PDiskQuotaUser):HRESULT;stdcall;
 end;


procedure DiskQuotaSetDisabled(var S:Cardinal);
procedure DiskQuotaSetTracked(var S:Cardinal);
procedure DiskQuotaSetEnforced(var S:Cardinal);

function DiskQuotaIsDisabled(S:Cardinal):Boolean;
function DiskQuotaIsTracked(S:Cardinal):Boolean;
function DiskQuotaIsEnforced(S:Cardinal):Boolean;

function DiskQuotaFileIncomplete(S:Cardinal):Boolean;
function DiskQuotaFileRebuilding(S:Cardinal):Boolean;

function DiskQuotaIsLoggedUserThreshold(f:Cardinal):Boolean;
function DiskQuotaIsLoggedUserLimit(f:Cardinal):Boolean;

procedure DiskQuotaSetLogUserTreshold(var f : Cardinal; yn:Boolean);
procedure DiskQuotaSetLogUserLimit(var f:Cardinal; yn:Boolean);

implementation

//
// Helper macros for setting and testing state value.
//

procedure DiskQuotaSetDisabled(var S:Cardinal);
begin
 S:=S and not DISKQUOTA_STATE_MASK;
end;

procedure DiskQuotaSetTracked(var S:Cardinal);
begin
 S := S or (DISKQUOTA_STATE_MASK and DISKQUOTA_STATE_TRACK);
end;

procedure DiskQuotaSetEnforced(var S:Cardinal);
begin
 S := S or (DISKQUOTA_STATE_ENFORCE and DISKQUOTA_STATE_ENFORCE );
end;

function DiskQuotaIsDisabled(S:Cardinal):Boolean;
begin
 Result:=(DISKQUOTA_STATE_DISABLED=(S and DISKQUOTA_STATE_MASK));
end;

function DiskQuotaIsTracked(S:Cardinal):Boolean;
begin
 Result:=(DISKQUOTA_STATE_TRACK = (S and DISKQUOTA_STATE_MASK));
end;

function DiskQuotaIsEnforced(S:Cardinal):Boolean;
begin
 Result:= (DISKQUOTA_STATE_ENFORCE = (S and DISKQUOTA_STATE_MASK));
end;

//
// These file state flags are read-only.
//
function DiskQuotaFileIncomplete(S:Cardinal):Boolean;
begin
 Result:= ((S and DISKQUOTA_FILESTATE_INCOMPLETE)<>0);
end;

function DiskQuotaFileRebuilding(S:Cardinal):Boolean;
begin
 Result:= ((S and DISKQUOTA_FILESTATE_REBUILDING)<>0);
end;


//
// Helper macros to interrogate a log flags DWORD.
//
function DiskQuotaIsLoggedUserThreshold(f:Cardinal):Boolean;
begin
 Result:=((F and DISKQUOTA_LOGFLAG_USER_THRESHOLD)<>0);
end;

function DiskQuotaIsLoggedUserLimit(f:Cardinal):Boolean;
begin
 Result:= (F and DISKQUOTA_LOGFLAG_USER_LIMIT)<>0;
end;

//
// Helper macros to set/clear bits in a log flags DWORD.
//
procedure DiskQuotaSetLogUserTreshold(var f : Cardinal; yn:Boolean);
begin
 F := F and not DISKQUOTA_LOGFLAG_USER_THRESHOLD;
 if yn then F := F or DISKQUOTA_LOGFLAG_USER_THRESHOLD;
end;

procedure DiskQuotaSetLogUserLimit(var f:Cardinal; yn:Boolean);
begin
 F := F and not DISKQUOTA_LOGFLAG_USER_LIMIT;
 if yn then F := F or DISKQUOTA_LOGFLAG_USER_LIMIT;
end;
end.

Вот и все на сегодня.
С уважением, ведущий рассылки FliNT




http://subscribe.ru/
E-mail: ask@subscribe.ru
Отписаться
Убрать рекламу

В избранное