3. Работа с томами и каталогами

В этой главе мы рассмотрим вопросы, связанные с обращением к томам и каталогам, расположенным на файл-сервере Novell NetWare. Мы расскажем вам о механизме отображения дисков рабочей станции на сетевые каталоги и научим вас составлять программы, выполняющие такое отображение. Вы узнаете об атрибутах каталогов, сможете создавать, переименовывать и удалять каталоги, узнавать и изменять их атрибуты.

3.1. Таблица томов файл-сервера

Каждый файл-сервер хранит информацию о сетевых томах в таблице томов (Volume Table), состоящей из 256 элементов. Номера элементов используются для адресации томов и называются номерами томов (Volume Number).

Зная номер тома, программа может получить такие важные характеристики тома, как его объем, размер свободного пространства на томе, максимальное количество каталогов, которое можно создать на томе, количество уже созданных каталогов. Кроме того, программа может определить, является ли данный том файл-сервера съемным.

Одна из важных задач - определение имен и номеров томов, смонтированных на файл-сервере.

Для определения имен смонтированных томов лучше всего воспользоваться функцией GetVolumeName() из библиотеки NetWare C Interface:

int GetVolumeName(int VolumeNumber, char*VolumeName);


Первый параметр функции задает номер тома, для которого необходимо получить имя. На сервере Novell NetWare версии 2.2 можно создать 32 тома, версия 3.11 допускает существование 64 томов. Поэтому диапазон возможных значений для первого параметра в зависимости от версии NetWare может быть от 0 до 31 или от 0 до 63.

Второй параметр - указатель на буфер размером 16 байт, в который будет записано имя тома.

В случае ошибки функция возвращает ненулевое значение. Например, если вы укажете недопустимый номер тома, функция возвратит значение 0x98.

Для определения списка смонтированных томов вы можете вызывать эту функцию в цикле, задавая ей номера томов в диапазоне от 0 до 63. Если функция вернет ненулевое значение или если на месте первой буквы имени тома будет двоичное нулевое значение, то это означает, что на данном сервере больше нет смонтированных томов.

Можно решить и обратную задачу - по имени тома определить его номер. Для этого предназначена функция GetVolumeNumber():

int GetVolumeNamber(char*VolumeName, int *VolumeNumber);


Для тома, имя которого задается первым параметром, функция определяет номер тома и записывает его по адресу, заданному вторым параметром. Если функция вернет ненулевое значение, указанному номеру тома не соответствует никакой том.

Для получения справочной информации о томе удобно воспользоваться функцией GetVolumeInfoWithNumber():

int GetVolumeInfoWithNumber(BYTE VolumeNumber, char *VolumeName,
        WORD *TotalBlocks, WORD *SectorsPerBlock,
        WORD *AvailableBlocks, WORD *TotalDirectorySlots,
        WORD *AvailableDirectorySlots, WORD *Removable);


Для тома, номер которого задан параметром VolumeNumber, функция возвращает имя, записывая его по адресу, указанному параметром VolumeName, общее количество блоков (параметр TotalBlocks), количество секторов в одном блоке (параметр SectorsPerBlock), количество свободных блоков (параметр AvailableBlocks), количество каталогов, имеющихся на томе (параметр TotalDirectorySlots), количество каталогов, которые можно дополнительно создать на томе (параметр AvailableDirectorySlots), признак того, что том является съемным (параметр Removable).

При использовании этой функции следует учесть, что она предоставляет информацию только о тех серверах, к которым пользователь подключен. Если рабочая станция создала канал с сервером, но не подключилась к нему, функция вернет код ошибки 252.

Размер сектора составляет 512 байт, что вы можете использовать для подсчета объема тома в килобайтах.

Если переменная, адрес которой указан параметром Removable, получила значение 0, это означает, что соответствующий том несъемный.

В следующем разделе мы приведем исходный текст программы, которая для текущего сервера (или первичного сервера, если текущий сервер не определен) выводит список смонтированных томов. Для каждого тома программа выводит его объем в килобайтах и размер имеющегося на томе свободного пространства.

Информация о томах может быть получена и без использования описанных выше функций библиотеки NetWare C Interface.

Для определения соответствия между номером тома и именем тома можно воспользоваться функцией E2h прерывания INT 21h:

На входе: AH = E2h;
DS:SI = Адрес буфера запроса;
ES:DI = Адрес буфера ответа;
На выходе: AL = Код ошибки или 0, если операция завершилась без ошибок.

Буфер запроса имеет следующий формат:

struct REQUEST {
        WORD         PacketLength; // размер пакета запроса
        BYTE         Function;     // должно быть равно 6
        BYTE         VolumeNumber; // номер тома
};


В этом буфере вам надо заполнить все поля, указав размер буфера и номер тома, для которого необходимо получить имя. Код функции в поле Function должен иметь значение 6.

Приведем формат буфера ответа:

struct REPLAY {
        WORD         PacketLength;     // размер пакета
        BYTE         VolumeNameLength; // длина имени тома
        BYTE         VolumeName[16];   // имя тома
};


Если указанному номеру тома не соответствует ни один том, поле VolumeNameLength будет содержать нулевое значение.

Для выполнения обратной операции - получения номера тома по его имени - можно воспользоваться той же функцией E2h прерывания INT 21h. Но формат буферов запроса и ответа будет другой.

Формат буфера запроса:

struct REQUEST {
        WORD         PacketLength;   // размер пакета запроса
        BYTE         Function;       // должно быть равно 5
        BYTE         NameLength;     // длина имени тома
        BYTE         VolumeName[16]; // имя тома
};


В этом буфере вам надо указать размер буфера, длину имени тома и имя тома, для которого необходимо получить номер имени. Код функции в поле Function должен иметь значение 5.

Приведем формат буфера ответа:

struct REPLAY {
        WORD         PacketLength;    // размер пакета
        BYTE         VolumeNumber;    // номер имени тома
};


Если том, имя которого указано в буфере запроса, смонтирован, регистр AL после возврата из функции будет равен нулю.

Для получения информации о смонтированном томе по номеру тома можно воспользоваться функцией DAh прерывания INT 21h:

На входе: AH = DAh;
DL = Номер тома;
ES:DI = Адрес буфера ответа.
На выходе: AL = Код ошибки или 0, если операция завершилась без ошибок.

Буфер ответа имеет следующий формат:

struct REPLAY {
        WORD SectorsPerBlock;
        WORD TotalBlocks;
        WORD AvailableBlocks;
        WORD TotalDirectorySlots;
        WORD AvailableDirectorySlots;
        BYTE VolumeName[16];
        WORD Removable;
};


Назначение полей этой структуры аналогично назначению параметров функции GetVolumeInfoWithNumber().

3.1.1. Программа VOLINFO

Приведем исходный текст программы, которая выводит список смонтированных томов для текущего сервера. Если текущий диск локальный, программа выходит информацию о первичном сервере.

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

Для каждого тома программа получает информацию о томе, вызывая функцию GetVolumeInfoWithNumber().

// ===================================================
// Листинг 7. Программа для просмотра имен
// томов текущего или первичного файл-сервера
// Файл volinfo\volinfo.cpp
//
// (C) A. Frolov, 1993
// ===================================================

#include <stdlib.h>
#include <stdio.h>

#define WORD unsigned int
#define BYTE unsigned char

extern "C" int GetNetWareShellVersion(char *,char *, char *);
extern "C" int GetVolumeName(int, char*);
extern "C" int GetVolumeInfoWithNumber(BYTE, char*,
                                WORD*, WORD*, WORD*, WORD*, WORD*, WORD*);

void main(void) {

        char MajorVersion=0;
        char MinorVersion=0;
        char Revision=0;
        char VolumeName[64][16];
        int ccode, i;

        WORD TotalBlocks, SectorsPerBlock, AvailableBlocks;
        WORD TotalDirectorySlots, AvailableDirectorySlots, Removable;
        long TotalSectors, AvailableSectors;

        printf("\n*VOLINFO* (C) Frolov A., 1993\n");

        asm push si
        GetNetWareShellVersion(&MajorVersion,
                        &MinorVersion, &Revision);
        asm pop si

        if(MajorVersion == 0) {
                printf("\nОболочка NetWare не загружена\n");
                return;
        }

        printf("\nСмонтированные тома:\n"
                "----------------------------------------------\n");
        printf("Имя тома\tВсего Кбайт\tСвободно Кбайт\n");
        printf("----------------------------------------------");

// Цикл по томам файл-сервера.

        for(i=0; i<64; i++) {

// Получаем и выводим имя тома

                ccode = GetVolumeName(i, VolumeName[i]);
                printf("\n%s\t", VolumeName[i]);

// Если ошибка или тома нет, выходим из цикла

                if(ccode) break;
                if(!*(VolumeName[i])) break;

// Получаем информацию о томе

                ccode = GetVolumeInfoWithNumber(i, VolumeName[i],
                        &TotalBlocks, &SectorsPerBlock, &AvailableBlocks,
                        &TotalDirectorySlots,&AvailableDirectorySlots,
                        &Removable);

                if(!ccode) {

// Подсчитываем общее количество секторов на томе
// и количество свободных секторов

                        TotalSectors = (long)TotalBlocks * SectorsPerBlock;
                        AvailableSectors = 
                                (long)AvailableBlocks * SectorsPerBlock;

// Выводим размер томов и размер свободного пространства
// в килобайтах. Учитываем, что размер сектора
// составляет 512 байт.

                        printf("\t%ld\t\t%ld",
                                ((long)TotalSectors * 512L) / 1024L,
                                ((long)AvailableSectors * 512L) / 1024L);
                }
        }
}


Вот что программа VOLINFO вывела на экран, когда мы запустили ее на нашем сервере SYSPRG:

*VOLINFO* (C) Frolov A., 1993

Смонтированные тома:
----------------------------------------------
Имя тома                Всего Кбайт     Свободно Кбайт
----------------------------------------------
SYS                     140000          8084
VOL1                    178864          13768
VOL2                    160000          13372
VOL3                    169024          924


3.2. Отображение дисков рабочей станции

Для обеспечения возможности работы с файлами, расположенными на дисках файл-сервера, сетевая оболочка выполняет отображение локальных дисков рабочей станции на сетевые каталоги. При этом прикладная программа, запущенная на рабочей станции, может и не заметить, что она работает не с локальным диском, а с удаленным. Разумеется, для обеспечения возможности отображения пользователь должен подключиться к файл-серверу, сообщив ему свое имя и пароль. В предыдущей главе вы научились составлять программы, подключающие пользователя к файл-серверу.

Дополнительно к таблице номеров каналов серверов сетевая оболочка работает еще с тремя таблицами, необходимыми для отображения дисков. Это таблица флагов дисковых устройств (Drive Flag Table), таблица номеров каналов дисковых устройств (Drive Connection ID Table) и таблица индексов дисковых устройств (Drive Handle Table).

Эти таблицы могут отображать 32 дисковых устройства, все они имеют размер 32 байта, по одному байту на одно устройство.

У вас может возникнуть вопрос: почему 32 дисковых устройства, а не 26? Действительно, MS-DOS позволяет вам использовать только 26 дисковых устройств, обозначая их буквами в диапазоне от A до Z. Сетевая оболочка добавляет еще шесть устройств, которые обычно используются в качестве временных логических дисков, отображаемых на сетевые каталоги только на время работы программы. Для обозначения этих дополнительных дисков сетевая оболочка использует следующие символы:

[ левая квадратная скобка;
\ обратный слеш;
] правая квадратная скобка;
^ символ caret (знак для вставки);
_ подчеркивание;
' апостроф.

Таблица флагов дисковых устройств (Drive Flag Table) содержит байты состояния для каждого дискового устройства рабочей станции. Пользуясь этой таблицей, программа может определить, какие диски рабочей станции локальные, а какие отображены на сетевые каталоги. Приведем список возможных значений элементов таблицы флагов:

0 Диска нет, т. е. этот диск не отображен ни на локальный диск, ни на удаленный сетевой каталог
01h Диск постоянно отображен на сетевой каталог
02h Диск временно отображен на сетевой каталог (временное отображение действует только во время работы программы; когда программа завершается, отображение автоматически отменяется)
80h Локальный диск рабочей станции
81h Локальный диск рабочей станции, постоянно отображенный
на сетевой каталог
82h Локальный диск рабочей станции, временно отображенный
на сетевой каталог

Когда программа обращается к диску, сетевая оболочка просматривает таблицу флагов и определяет, с каким диском надо работать - с отображенным или локальным.

Для того чтобы определить, на каталог какого сервера отображен тот или иной диск, программа может использовать таблицу номеров каналов дисковых устройств (Drive Connection ID Table). В этой таблице для каждого диска указан номер канала, который сетевая оболочка рабочей станции использует для работы с сервером, на чей каталог отображен данный диск. Если диск локальный или его нет совсем, для такого диска в таблице находится нулевое значение. Так как рабочая станция может образовать до 8 каналов, таблица номеров каналов дисковых устройств может содержать значения от 1 до 8 (или 0 для неиспользуемых дисков).

Таблица индексов дисковых устройств (Drive Handle Table) имеет отношение к таблице каталогов, расположенной в файл-сервере. Для каждого канала файл-сервер заводит отдельную таблицу индексов дисковых устройств, в которой содержится информация о томе и каталоге, на который отображается соответствующий диск рабочей станции. Подробнее об этом мы расскажем ниже в разделе "Таблица каталогов файл-сервера".

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

Приведем прототип функции AllocPermanentDirectoryHandle():

int AllocPermanentDirectoryHandle(BYTE DirectoryHandle,
    char *DirectoryPath, char DriveLetter,
    BYTE *NewDirectoryHandle, BYTE *EffectiveRightsMask);


Вы можете указать путь к отображаемому сетевому каталогу либо с помощью индекса каталога через параметр DirectoryHandle, либо через полный путь к каталогу (параметр DirectoryPath), либо используя комбинированный способ. В программе DIRMAP, демонстрирующей применение этой функции (см. ниже), мы указали полный путь к отображаемому каталогу при помощи параметра DirectoryPath, а для параметра DirectoryHandle мы задали нулевое значение.

Параметр DriveLetter задает отображаемый диск. Если этот диск до вызова функции AllocPermanentDirectoryHandle() отображался на другой сетевой каталог или был локальным диском рабочей станции, это никак не скажется на успехе или результате отображения.

Параметр NewDirectoryHandle - указатель на переменную, в которую будет записан индекс, связанный с отображаемым каталогом. Вы можете использовать этот индекс для ссылки на каталог вместо полного имени каталога. Он будет нужен также для отмены отображения диска.

Параметр EffectiveRightsMask - указатель на байт памяти, в который будет записана маска прав доступа пользователя в данном каталоге. Назначение отдельных битов этой маски мы рассмотрим ниже в разделе "Таблица каталогов файл-сервера".

Отображение диска сохраняется до тех пор, пока оно не будет отменено функцией DeallocateDirectoryHandle():

int DeallocateDirectoryHandle(BYTE DirectoryHandle);


Эта функция отменяет отображение диска на каталог, имеющий индекс DirectoryHandle.

Для того чтобы отменить отображение диска, пользуясь его буквенным обозначением, можно воспользоваться функцией GetDirectoryHandle(), возвращающей индекс каталога для диска, заданного своим номером (0 - A, 1 - B и т. д.):

DirectoryHandle = GetDirectoryHandle(argv[1][0] - 'A');


Для отображения локального диска на сетевой каталог вместо функции AllocPermanentDirectoryHandle() можно воспользоваться функцией E2h прерывания INT 21h:

На входе: AH = E2h;
DS:SI = Адрес буфера запроса;
ES:DI = Адрес буфера ответа.
На выходе: AL = Код ошибки или 0, если операция завершилась без ошибок.

Буфер запроса имеет следующий формат:

struct REQUEST {
        WORD  PacketLength;    // размер пакета запроса
        BYTE  Function;        // должно быть равно 18
        BYTE  DirectoryHandle; // индекс каталога
        BYTE  DriveLetter;     // отображаемый диск
        BYTE  PathLength;      // длина пути к каталогу
        BYTE  DirectoryPath[PathLength]; // путь к каталогу
};


Приведем формат буфера ответа:

struct REPLAY {
        WORD  PacketLength;         // размер пакета 
        BYTE  NewDirectoryHandle;   // новый индекс каталога
        BYTE  EffectiveRightsMask ; // маска прав доступа
};


Для отмены отображения диска вместо функции DeallocateDirectoryHandle можно использовать функцию E2h прерывания INT 21h, заполнив буфер запроса следующим образом:

struct REQUEST {
        WORD  PacketLength;    // размер пакета запроса
        BYTE  Function;        // должно быть равно 20
        BYTE  DirectoryHandle; // индекс каталога
};


До сих пор мы рассматривали так называемое постоянное отображение дисков на сетевые каталоги. Это отображение сохраняется до тех пор, пока оно не будет отменено вызовом соответствующей функции. Однако вы можете заказать временное отображение, которое будет отменено автоматически после завершения работы программы, создавшей временное отображение.

Временное отображение выполняется функцией AllocTemporaryDirectoryHandle():

int AllocTemporaryDirectoryHandle(BYTE DirectoryHandle,
    char *DirectoryPath, char DriveLetter,
    BYTE *NewDirectoryHandle, BYTE *EffectiveRightsMask);


Параметры этой функции полностью аналогичны параметрам функции AllocPermanentDirectoryHandle(). Дополнительно к дискам с именами A - Z вы можете использовать диски, обозначаемые следующими символами: [, \, ], ^, _, '.

Для временного отображения диска на сетевой каталог вы можете воспользоваться функцией E2h прерывания INT 21h. Вам надо заполнить буфер запроса аналогично тому, как это нужно для создания постоянного отображения, но в поле Function необходимо указать значение 19. Формат буфера ответа полностью аналогичен формату, используемому при постоянном отображении.

Для получения адресов таблиц можно воспользоваться функцией EFh прерывания INT 21h:

На входе: AH = EFh;
AL = 0 - получить адрес таблицы индексов дисковых устройств (Drive Handle Table);
1 - получить адрес таблицы флагов дисковых устройств (Drive Flag Table);
2 - получить адрес таблицы номеров каналов дисковых устройств (Drive Connection ID Table);
3 - получить адрес таблицы номеров каналов
(Connection ID Table);
4 - получить адрес таблицы имен серверов (Server Name Table), состоящей из восьми элементов размером 48 байт. В каждом элементе записано имя сервера в формате текстовой строки, закрытой двоичным нулем.
На выходе: ES:SI = Адрес запрашиваемой таблицы.

3.2.1. Программа WKSTABLE

Приведем программу, отображающую состояние внутренних таблиц сетевой оболочки. Кроме таблиц отображения дисковых устройств программа показывает содержимое таблицы номеров каналов и таблицы имен файл-серверов, с которыми рабочая станция создала каналы.

Программа WKSTABLE (листинг 8) получает адреса всех таблиц и выводит их в стандартный поток вывода в соответствующем формате. Вы можете подключиться к нескольким серверам и выполнить отображение дисков утилитами attach.exe и map.exe из каталога SYS:PUBLIC, а затем запустить программу и посмотреть содержимое таблиц.

// ============================================================
// Листинг 8. Отображение содержимого таблиц сетевой оболочки.
// Файл wkstable\wkstable.cpp
//
// (C) A. Frolov, 1993
// ============================================================

#include <stdlib.h>
#include <stdio.h>
#include <dos.h>
#include <conio.h>

#define BYTE  unsigned char

// Формат таблицы номеров каналов для сетевой оболочки

struct ConnectionIDTable {
        BYTE InUseFlag;
        BYTE OrderNumber;
        BYTE NetworkNumber[4];
        BYTE NodeAddress[6];
        BYTE SocketNumber[2];
        BYTE ReceiveTimeout[2];
        BYTE RoutingNode[6];
        BYTE PacketSequenceNumber;
        BYTE ConnectionNumber;
        BYTE ConnectionStatus;
        BYTE MaximumTimeout[2];
        BYTE Reserved[5];
};
void GetTableAddress(int Table, char far * *TableAddress);
void ShowTable(char far *DriveHandleTable);
void ShowConnIDTable(struct ConnectionIDTable far *ConnIDTable);
void ShowServerNameTable(char far *ServerNameTable);

void main(void) {

// Указатели на таблицы сетевой оболочки

        char far *DriveHandleTable;
        char far *DriveFlagTable;
        char far *DriveServerTable;
        char far *ServerMappingTable;
        char far *ServerNameTable;

        struct ConnectionIDTable far *ConnIDTable;

// Получаем указатели на таблицы

        GetTableAddress(0, &DriveHandleTable);
        GetTableAddress(1, &DriveFlagTable);
        GetTableAddress(2, &DriveServerTable);
        GetTableAddress(3, &ServerMappingTable);
        GetTableAddress(4, &ServerNameTable);

        printf("\nТаблицы сетевой оболочки, (C) Frolov A., 1993\n"
                        "---------------------------------------------\n");

// Отображаем содержимое таблицы индексов дисковых устройств

        printf("\nDrive Handle Table  (%Fp)\n", DriveHandleTable);
        printf(  "------------------\n");
        ShowTable(DriveHandleTable);

// Отображаем содержимое таблицы флагов дисковых устройств

        printf("\nDrive Flag Table    (%Fp)\n", DriveFlagTable);
        printf(  "----------------\n");
        ShowTable(DriveFlagTable);

// Отображаем содержимое таблицы отображения дисков на серверы

        printf("\nDrive Server Table  (%Fp)\n", DriveServerTable);
        printf(  "------------------\n");
        ShowTable(DriveServerTable);

        printf("Нажмите любую клавишу для продолжения...\n");
        getch();

// Отображаем содержимое таблицы каналов с серверами

        ConnIDTable = (struct ConnectionIDTable far *)ServerMappingTable;

        printf("\nConnection ID Table (%Fp)\n", ConnIDTable);
        printf(  "-------------------\n");
        ShowConnIDTable(ConnIDTable);

// Отображаем содержимое таблицы имен серверов

        printf("\nServer Name Table   (%Fp)\n", ServerNameTable);
        printf(  "-----------------\n");
        ShowServerNameTable(ServerNameTable);
}

// ===========================================================
// Функция для отображения таблицы имен серверов
// ===========================================================

void ShowServerNameTable(char far *ServerNameTable) {
        for(int i=0; i<8; i++) {
                if(*(ServerNameTable + 48*i) != '\0')
                        printf("%d: %Fs\n", i+1, ServerNameTable + 48*i);
                else
                        printf("%d: Не используется\n", i+1);
        }
}

// ===========================================================
// Функция для отображения таблицы каналов рабочей станции
// ===========================================================

void ShowConnIDTable(struct ConnectionIDTable
                                                                far *ConnIDTable) {

        printf("Порядковый номер:\t");
        for(int i=0; i<8; i++) {
                printf("%d   ", i+1);
        }
        printf("\nНомер канала:\t\t");
        for(i=0; i<8; i++) {
                printf("%02.2X  ", (ConnIDTable + i)->ConnectionNumber);
        }

        printf("\nСостояние канала:\t");
        for(i=0; i<8; i++) {
                printf("%02.2X  ", (ConnIDTable + i)->ConnectionStatus);
        }
        printf("\n");
}

// ===========================================================
// Функция для вывода содержимого таблиц отображения
// дисковых устройств
// ===========================================================

void ShowTable(char far *Table) {

        printf("A  B  C  D  E  F  G  H  I  J  K  L  M  N  "
                         "O  P  Q  R  S  T  U  V  W  X  Y  Z\n");
        for(int i=0; i<26; i++) {
                printf("%02.2X ",(unsigned char)*(Table +i));
        }
        printf("\n[  \\  ]  ^  _  '\n");
        for(i=26; i<32; i++) {
                printf("%02.2X ",(unsigned char)*(Table +i));
        }
        printf("\n");
}
// ===========================================================
// Функция для получения указателей на таблицы
// оболочки рабочей станции
// ===========================================================

void GetTableAddress(int Table, char far* *TableAddress) {

        union REGS regs; struct SREGS sregs;

        regs.h.ah = 0xef;
        regs.h.al = Table;
        intdosx(&regs, &regs, &sregs);
        FP_OFF(*TableAddress) = regs.x.si;
        FP_SEG(*TableAddress) = sregs.es;
}


Мы запустили программу в сети, содержащей четыре файл-сервера, и вот что увидели на экране:

Таблицы сетевой оболочки, (C) Frolov A., 1993
---------------------------------------------

Drive Handle Table  (C143:01A0)
------------------
A  B  C  D  E  F  G  H  I  J  K  L  M  N  O  P  Q  R  S  T  U  V  W  X  Y  Z
00 00 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00 00 02 02 02 00 06 03 04 05 
[  \  ]  ^  _  '
00 00 00 00 00 00 

Drive Flag Table    (C143:01C0)
----------------
A  B  C  D  E  F  G  H  I  J  K  L  M  N  O  P  Q  R  S  T  U  V  W  X  Y  Z
80 80 80 80 80 80 80 80 80 01 00 00 00 00 00 00 00 00 01 01 01 00 01 01 01 01 
[  \  ]  ^  _  '
00 00 00 00 00 00 

Drive Server Table  (C143:01E0)
------------------
A  B  C  D  E  F  G  H  I  J  K  L  M  N  O  P  Q  R  S  T  U  V  W  X  Y  Z
00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 04 03 02 00 01 01 01 01 
[  \  ]  ^  _  '
00 00 00 00 00 00 
Нажмите любую клавишу для продолжения...

Connection ID Table (C143:0254)
-------------------
Порядковый номер:       1   2   3   4   5   6   7   8   
Номер канала:           04  0B  01  01  FF  FF  FF  FF  
Состояние канала:       FF  FF  FF  FF  00  00  00  00  

Server Name Table   (C143:0354)
-----------------
1: SYSPRG
2: SMARTNET
3: NETLAB
4: WINLAB
5: Не используется
6: Не используется
7: Не используется
8: Не используется


Из таблицы Server Name Table видно, что рабочая станция создала каналы с серверами SYSPRG, SMARTNET, NETLAB и WINLAB.

Анализируя содержимое таблицы Drive Flag Table, можно сделать вывод, что рабочая станция имеет локальные диски от A: до I:, диски J:, S:, T:, U:, W:, X:, Y:, Z: постоянно отображены на сетевые каталоги, т. е. это сетевые диски.

Из таблицы Drive Server Table видно, что диски J:, W:, X:, Y:, Z: отображены на каталоги сервера SYSPRG (номер канала соответствует позиции имени файл-сервера в таблице имен). Диск U: отображен на сервер SMARTNET (канал 2), диск T: - на сервер NETLAB (канал 3), диск S: - на сервер WINLAB (канал 4).

Таблица Drive Handle Table позволяет сетевой оболочке определить, на какие конкретно каталоги отображены соответствующие диски, так как она содержит индекс таблицы отображения соответствующего файл-сервера.

Из таблицы Connection ID Table можно получить информацию о том, какие из 8 имеющихся каналов задействованы и какие номера каналов используют серверы для работы с нашей станцией. Например, сервер SYSPRG использует канал с номером 04h, сервер SMARTNET - канал с номером 0Bh, а серверы NETLAB и WINLAB - каналы с номером 01h. Пусть вас не смущает, что последние два сервера используют один и тот же номер канала: эти номера соответствуют таблице, расположенной в сервере, а не в рабочей станции.

3.2.2. Программа DIRMAP

При помощи программы DIRMAP (листинг 9) вы сможете отображать локальные диски рабочей станции на сетевые каталоги. В качестве первого параметра при запуске программы необходимо задать букву отображаемого диска, в качестве второго - полный путь к сетевому каталогу (без имени сервера).

// ===================================================
// Листинг 9. Отображение локальных дисков на
// сетевые каталоги
// Файл dirmap\dirmap.cpp
//
// (C) A. Frolov, 1993
// ===================================================

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>

#define BYTE unsigned char
#define WORD unsigned int

extern "C" int GetNetWareShellVersion(char *,char *, char *);
extern "C" int AllocPermanentDirectoryHandle(BYTE, char *,
                                                 char, BYTE*, BYTE*);

void main(int argc, char *argv[]) {

        char MajorVersion=0;
        char MinorVersion=0;
        char Revision=0;

        BYTE NewDirectoryHandle;
        BYTE RightsMask;

        int ccode;

        printf("\n*DIRMAP* (C) Frolov A., 1993\n");

// Проверяем наличие сетевой оболочки

        asm push si
        GetNetWareShellVersion(&MajorVersion,
                        &MinorVersion, &Revision);
        asm pop si

        if(MajorVersion == 0) {
                printf("\nОболочка NetWare не загружена\n");
                return;
        }

// Необходимо задать букву, обозначающую имя
// локального диска и путь к сетевому каталогу

        if(argc < 3) {
                printf("Укажите локальный диск и путь "
                        "к каталогу, \nнапример: dirmap f sys:users\n");
                return;
        }

// Параметры должны быть заданы заглавными буквами

        strupr(argv[1]);
        strupr(argv[2]);

// Создаем новый индекс каталога и отображаем диск

        ccode = AllocPermanentDirectoryHandle(0,
                argv[2], argv[1][0],
                &NewDirectoryHandle, &RightsMask);

// Если ошибок не было, выводим индекс каталога
// и маску прав для каталога

        if(!ccode)
                printf("Индекс каталога: %d, маска прав: %02.2X\n",
                        NewDirectoryHandle, RightsMask);
        else
                printf("Ошибка %02.2X\n", ccode);
}


3.2.3. Программа DIRUNMAP

Программа DIRUNMAP (листинг 10) выполняет отмену отображения локального диска на сетевой каталог. Ей необходимо указать один параметр - букву, обозначающую диск, для которого необходимо отменить отображение.

// ===================================================
// Листинг 10. Отмена отображения локального диска на
// сетевой каталог
// Файл dirunmap\dirunmap.cpp
//
// (C) A. Frolov, 1993
// ===================================================

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>

#define BYTE unsigned char
#define WORD unsigned int

extern "C" int GetNetWareShellVersion(char *,char *, char *);
extern "C" BYTE GetDirectoryHandle(BYTE);
extern "C" int DeallocateDirectoryHandle(BYTE);

void main(int argc, char *argv[]) {

        char MajorVersion=0;
        char MinorVersion=0;
        char Revision=0;

        BYTE DirectoryHandle;

        int ccode;

        printf("\n*DIRUNMAP* (C) Frolov A., 1993\n");

// Проверяем наличие сетевой оболочки

        asm push si
        GetNetWareShellVersion(&MajorVersion,
                        &MinorVersion, &Revision);
        asm pop si

        if(MajorVersion == 0) {
                printf("\nОболочка NetWare не загружена\n");
                return;
        }

// Необходимо задать букву, обозначающую имя
// локального диска и путь к сетевому каталогу

        if(argc < 2) {
                printf(
                        "Укажите локальный диск, например: dirunmap f\n");
                return;
        }
// Параметр должен быть задан заглавной буквой

        strupr(argv[1]);

// Получаем индекс каталога, на который отображен указанный диск

        DirectoryHandle = GetDirectoryHandle(argv[1][0] - 'A');

// Если диск не отображен на сетевой каталог,
// выводим сообщение об ошибке

        if(DirectoryHandle)
                printf("Индекс каталога: %d\n", DirectoryHandle);
        else {
                printf("Диск не отображен на сетевой каталог\n");
                return;
        }

// Отменяем отображение диска

        ccode = DeallocateDirectoryHandle(DirectoryHandle);

// Если ошибок не было, выводим индекс каталога
// и маску прав для каталога

        if(ccode)
                printf("Ошибка %02.2X\n", ccode);
        else
                printf("Диск %c удален\n", argv[1][0]);
}


3.3. Таблица каталогов файл-сервера

Вся информация о содержимом каталогов файл-сервера находится в таблице каталогов. Таблица каталогов по своему назначению напоминает каталоги MS-DOS: в ней находится информация об именах каталогов и файлов, байты атрибутов, информация о дате создания, идентификатор пользователя, создавшего файл или каталог, информация о правах доступа и т. д.

Для просмотра каталогов в операционной системе MS-DOS имеются соответствующие функции, о чем мы рассказывали в первом томе "Библиотеки системного программиста". Разумеется, программа может просматривать сетевые каталоги с помощью этих функций. Однако если вы воспользуетесь функциями, специально предназначенными для просмотра содержимого каталогов, вы сможете узнать имя пользователя, создавшего каталог или файл, а также получить информацию о правах доступа к каталогу или файлу.

Сетевая оболочка Novell NetWare имеет две разные функции для поиска подкаталогов в каталоге и для поиска файлов в каталоге (MS-DOS получает информацию сразу и о файлах, и о подкаталогах). В этом разделе мы расскажем о поиске каталогов, а файлами займемся позже, в главе "Работа с файлами".

Библиотека NetWare C Interface содержит функцию ScanDirectoryInformation(), предназначенную для просмотра содержимого каталогов. С ее помощью вы можете получить информацию о подкаталогах любого сетевого каталога.

Приведем прототип функции:

int ScanDirectoryInformation(BYTE DirectoryHandle,
        char *SearchDirectoryPath, int *SequenceNumber,
        char *DirectoryName, BYTE *CreationDateAndTime,
        long *OwnerObjectID, BYTE *MaximumRightsMask);


С помощью параметров DirectoryHandle и SearchDirectoryPath задается каталог, содержимое которого надо просмотреть. Параметр DirectoryHandle - это индекс в таблице дисков, которую файл-сервер поддерживает для каждой рабочей станции. Когда диск рабочей станции отображается на каталог файл-сервера, создается новый элемент в таблице дисков файл-сервера. Индекс DirectoryHandle однозначно соответствует сетевому каталогу, на который отображается локальный диск рабочей станции.

Вы можете задать в качестве параметра DirectoryHandle нулевое значение, при этом параметр SearchDirectoryPath должен указывать на текстовую строку, содержащую полный путь к исследуемому каталогу. В строке могут использоваться символы "*" и "?", которые трактуются так же, как и в MS-DOS. Например, для поиска всех подкаталогов каталога SYS:USERS вы можете задать 0 в поле DirectoryHandle и строку "SYS:USERS\*" в поле SearchDirectoryPath.

Имя файл-сервера не указывается, так как запрос направляется к предпочтительному, текущему или первичному серверу, в зависимости от того, существует ли предпочтительный сервер или текущий сервер. Если был задан предпочтительный сервер, функция ScanDirectoryInformation() обращается к предпочтительному серверу. Если предпочтительный сервер не был задан и на рабочей станции текущим является диск, отображенный на сетевой каталог, функция ScanDirectoryInformation() обращается к файл-серверу, содержащему этот каталог. Если текущий диск рабочей станции является локальным, функция ScanDirectoryInformation() обращается к первичному серверу.

Параметр SequenceNumber - указатель на слово, которое должно содержать нулевое значение при первом вызове функции ScanDirectoryInformation(). Для получения информации о всех подкаталогах вам придется вызывать эту функцию в цикле, при этом слово, на которое указывает параметр SequenceNumber, будет автоматически увеличиваться самой функцией.

Остальные четыре параметра - указатели на переменные, в которые будут записаны возвращаемые значения.

В область памяти, на которую указывает параметр DirectoryName, после успешного вызова функции будет записано имя обнаруженного подкаталога. Размер этой области памяти должен составлять 16 байт. Вызывая в цикле функцию ScanDirectoryInformation(), программа будет получать в поле, адресуемом параметром DirectoryName, имена найденных подкаталогов.

Параметр CreationDateAndTime указывает на область памяти, размером 4 байта, в которую будет записана информация о дате и времени создания найденного подкаталога. На рис. 1 приведен формат этой информации. К номеру года, возвращенному в первом байте, необходимо добавить число 80.

Рис.1. Формат даты и времени

Параметр OwnerObjectID - указатель на слово, в котором будет записан идентификатор пользователя, создавшего каталог. По этому идентификатору с помощью функции GetBinderyObjectName() вы легко сможете получить имя пользователя.

Параметр MaximumRightsMask - указатель на байт, в который будет записано значение маски прав доступа, связанное с данным каталогом. Маска используется для определения возможности доступа к каталогу и определяется при создании каталога. Каждый бит маски, установленный в 1, разрешает соответствующий вид доступа:

Номер бита Доступ
0 Чтение файлов
1 Запись в файлы
2 Открытие файлов
3 Создание файлов
4 Удаление файлов
5 Можно создавать подкаталоги и задавать для создаваемых подкаталогов права доступа
6 Поиск файлов в каталоге
7 Изменение атрибутов файлов

Функция ScanDirectoryInformation() при успешном завершении возвращает нулевое значение, в противном случае - код ошибки:

Код ошибки Значение
0x98 Заданный сетевой том не существует
0x9B Неправильное значение параметра индекса каталога
0x9C Неправильный путь к каталогу

Для просмотра подкаталогов в заданном каталоге вместо функции ScanDirectoryInformation() можно использовать функцию E2h прерывания INT 21h:

На входе: AH = E2h;
DS:SI = Адрес буфера запроса;
ES:DI Адрес буфера ответа.
На выходе: AL Код ошибки или 0, если операция завершилась без ошибок.

Буфер запроса имеет следующий формат:

struct REQUEST {
        WORD    PacketLength;        // размер пакета запроса
        BYTE    Function;            // должно быть равно 2
        BYTE    DirectoryHandle;     // индекс каталога
        WORD    SequenceNumber;      // порядковый номер
        BYTE    PathLength;          // длина поля пути
        BYTE    SearchDirectoryPath[PathLength]; // путь поиска
};


Приведем формат буфера ответа:

struct REPLAY {
        WORD    PacketLength;      // размер пакета
        BYTE    DirectoryName[16]; // имя найденного каталога
        BYTE    CreationDate[2];   // дата создания каталога
        BYTE    CreationTime[2];   // время создания каталога
        long    OwnerObjectID;     // идентификатор пользователя,
                                   // создавшего каталог
        BYTE            MaximumRightsMask; // маска прав доступа
        BYTE            Reserved;          // зарезервировано
        WORD    SubDirNumber;      // номер подкаталога в
                                   // каталоге
};


В процессе просмотра содержимого каталога программа должна вызывать эту функцию в цикле, задавая каждый раз (кроме первого) значение поля SequenceNumber в буфере запроса равным значению SubDirNumber, полученному в буфере ответа после предыдущего вызова функции. При первом вызове функции значение поля SequenceNumber должно быть равно нулю. Учтите, что поля SubDirNumber и SequenceNumber имеют "перевернутый" формат, т. е. младший байт поля записан по старшему адресу.

Если при вызове функции был найден подкаталог, соответствующий образцу, заданному в поле SearchDirectoryPath, регистр AL будет содержать нулевое значение.

3.3.1. Программа DIRSCAN

С помощью программы DIRSCAN вы сможете получить список подкаталогов для каталога, путь к которому задан в качестве параметра при запуске программы.

Для преобразования идентификатора пользователя, создавшего каталог,
в имя мы использовали функцию GetBinderyObjectName() из библиотеки NetWare C Interface:

int GetBinderyObjectName(long ObjectID,
         char *ObjectName, WORD *ObjectType);


Функция ищет в базе объектов запись с идентификатором ObjectID и в случае успеха записывает в переменные, адресуемые параметрами ObjectName и ObjectType, имя и тип объекта. В случае успешного поиска функция возвращает нулевое значение.

Итак, исходный текст программы DIRSCAN:

// ===================================================
// Листинг 11. Просмотр списка подкаталогов
// сетевого каталога
// Файл dirscan\dirscan.cpp
//
// (C) A. Frolov, 1993
// ===================================================

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>

#define BYTE unsigned char
#define WORD unsigned int

extern "C" int GetNetWareShellVersion(char *,char *, char *);
extern "C" int ScanDirectoryInformation(BYTE, char *, int *,
                                          char *, BYTE *, long *, BYTE *);
extern "C" int GetBinderyObjectName(long, char *, WORD *);

void main(int argc, char *argv[]) {

        char MajorVersion=0;
        char MinorVersion=0;
        char Revision=0;

        int SequenceNumber;
        char DirectoryName[16];
        BYTE CreationDataAndTime[4];
        long OwnerObjectID;
        BYTE RightsMask;
        int ccode;

        char ObjectName[48];
        WORD ObjectType;

        printf("\n*DIRSCAN* (C) Frolov A., 1993\n");

// Проверяем наличие сетевой оболочки

        asm push si
        GetNetWareShellVersion(&MajorVersion,
                        &MinorVersion, &Revision);
        asm pop si

        if(MajorVersion == 0) {
                printf("\nОболочка NetWare не загружена\n");
                return;
        }
// В качестве аргумента необходимо задать
// путь к просматриваемому каталогу в виде   SYS:USERS\*

        if(argc < 2) {
                printf("Укажите путь к каталогу, "
                        "например: dirscan sys:users\\*\n");
                return;
        }
        printf("Содержимое каталога %s\n", argv[1]);
        printf("--------------------------------------------\n");
        printf("Имя     \tКто владелец каталога\n");
        printf("--------------------------------------------\n");

// Путь должен быть задан заглавными буквами

        strupr(argv[1]);

// Цикл просмотра каталога

        for(SequenceNumber = 0;;) {

// Получаем информацию о содержимом каталога

                ccode = ScanDirectoryInformation(0, argv[1],                                    &SequenceNumber, DirectoryName,                                                         CreationDataAndTime, &OwnerObjectID, &RightsMask);

// Если были ошибки или каталог пуст, завершаем цикл

                if(ccode) break;
                if(DirectoryName[0] == '\0') break;

// Выводим имя каталога

                printf("%-12s", DirectoryName);

// Если для каталога определен владелец,
// получаем и выводим имя владельца

                if(OwnerObjectID) {
                        GetBinderyObjectName(OwnerObjectID, ObjectName,                                                 &ObjectType);
                        printf("\t%-12s \n", ObjectName);
                }
                else
                        printf("\t <Нет сведений о владельце> \n");
        }
}


3.4. Создание, переименование и удаление каталогов

Для работы с сетевыми каталогами вы можете использовать обычные функции MS-DOS, описанные в первом томе "Библиотеки системного программиста". Это возможно благодаря тому, что сетевая оболочка переназначает эти функции, посылая соответствующие запросы файл-серверу.

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

Для создания каталога вы можете воспользоваться функцией CreateDirectory():

int CreateDirectory(BYTE DirectoryHandle,
     char* DirectoryPath, BYTE MaximumRightsMask);


Параметры DirectoryHandle и DirectoryPath задают создаваемый каталог. Вы можете использовать оба параметра сразу, указывая индекс каталога, в котором вы будете создавать подкаталог (параметр DirectoryHandle ), и имя создаваемого подкаталога (параметр DirectoryPath). Можно также указывать только полный путь к создаваемому каталогу, а в качестве параметра DirectoryHandle задать нулевое значение.

Параметр MaximumRightsMask задает вид доступа, разрешенный пользователям для данного каталога:

Номер бита Доступ
0 Чтение файлов
1 Запись в файлы
2 Открытие файлов
3 Создание файлов
4 Удаление файлов
5 Можно создавать подкаталоги и задавать для создаваемых подкаталогов права доступа
6 Поиск файлов в каталоге
7 Изменение атрибутов файлов

Например, если в маске бит 3 установлен в 1, пользователи могут создавать в каталоге файлы и подкаталоги.

Приведенная ниже программа MAKEDIR позволит вам создавать каталоги и указывать при этом маску прав доступа.

В случае успешного завершения функция CreateDirectory() возвращает нулевое значение, в противном случае - код ошибки:

Код ошибки Значение
0x84 У пользователя недостаточно прав для создания подкаталога в данном каталоге
0x98 Указанный при создании каталога том не существует
0xFF Неправильно указан путь или имя каталога (например, в указанном каталоге уже существует подкаталог с таким же именем)

Для переименования уже существующего каталога следует воспользоваться функцией RenameDirectory():

int RenameDirectory(BYTE DirectoryHandle,
     char* DirectoryPath, char *NewDirectoryName);


Путь к каталогу, имя которого надо изменить, задается параметрами DirectoryHandle и DirectoryPath таким же образом, как и в предыдущей функции. Параметр NewDirectoryName задает новое имя, которое должно иметь размер не более 15 байт.

Функция возвращает ноль в случае успешного завершения или код ошибки:

Код ошибки Значение
0x8B У пользователя недостаточно прав для переименования подкаталога
0x9B Неправильно задан индекс каталога в параметре DirectoryHandle
0x9С Неправильно указан путь к каталогу
0x9E Неправильно задано имя каталога

Для удаления каталога предназначена функция DeleteDirectory():

int DeleteDirectory(BYTE DirectoryHandle,
     char* DirectoryPath);


Параметры этой функции задают путь к удаляемому каталогу.

Функция возвращает нулевое значение или код ошибки:

Код ошибки Значение
0x8A У пользователя недостаточно прав для удаления каталога
0x98 Указанный при удалении каталога том не существует
0x9B Неправильно задан индекс каталога в параметре DirectoryHandle
0x9С Неправильно указан путь к каталогу
0x9F Каталог используется в настоящее время кем-то из пользователей и не может быть удален
0xA0 Удаляемый каталог содержит файлы или подкаталоги, в то время как удалять можно только пустые каталоги

Для создания каталога без помощи функций библиотеки NetWare C Interface вам следует воспользоваться функцией E2h прерывания INT 21h:

На входе: AH = E2h;
DS:SI = Адрес буфера запроса;
ES:DI = Адрес буфера ответа.
На выходе: AL = Код ошибки или 0, если операция завершилась без ошибок.

Буфер запроса имеет следующий формат:

struct REQUEST {
        WORD  PacketLength;      // размер пакета запроса
        BYTE  Function;          // должно быть равно 10
        BYTE  DirectoryHandle;   // индекс каталога
        BYTE  MaximumGightsMask; // маска прав каталога
        BYTE  PathLength;        // длина пути к каталогу
        BYTE  DirectoryPath[PathLength]; // путь к каталогу
};


В случае успешного завершения функции регистр AL содержит нулевое значение.

Для удаления каталога также можно воспользоваться функцией E2h прерывания INT 21h. Формат буфера запроса в этом случае должен быть таким:

struct REQUEST {
        WORD  PacketLength;      // размер пакета запроса
        BYTE  Function;          // должно быть равно 11
        BYTE  DirectoryHandle;   // индекс каталога
        BYTE  Reserved;          // не используется
        BYTE  PathLength;        // длина пути к каталогу
        BYTE  DirectoryPath[PathLength]; // путь к каталогу
};


Код ошибки возвращается в регистре AL.

Для изменения имени существующего каталога с помощью функции E2h прерывания INT 21h вы должны задать буфер запроса в следующем формате:

struct REQUEST {
        WORD  PacketLength;              // размер пакета запроса
        BYTE  Function;                  // должно быть равно 15
        BYTE  DirectoryHandle;           // индекс каталога
        BYTE  PathLength;                // длина пути к каталогу
        BYTE  DirectoryPath[PathLength]; // путь к каталогу
        BYTE  NameLength;           // длина нового имени каталога
        BYTE  NewDirectoryName[NameLength]; // новое имя каталога
};


3.4.1. Программы MAKEDIR, RENMDIR, DELDIR

В этом разделе мы приведем исходные тексты программ, выполняющих основные действия над сетевыми каталогами - создание, переименование и удаление.

Программа MAKEDIR (листинг 12) создает каталог и задает для него маску прав доступа. Имя создаваемого каталога и маска передаются программе при запуске в качестве параметров:

// ===================================================
// Листинг 12. Создание каталога
// Файл makedir\makedir.cpp
//
// (C) A. Frolov, 1993
// ===================================================

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>

#define BYTE unsigned char
#define WORD unsigned int

extern "C" int GetNetWareShellVersion(char *,char *, char *);
extern "C" int CreateDirectory(BYTE, char*, BYTE);

void main(int argc, char *argv[]) {

        char MajorVersion=0;
        char MinorVersion=0;
        char Revision=0;

        BYTE RightsMask;

        int ccode;

        printf("\n*MAKEDIR* (C) Frolov A., 1993\n");

// Проверяем наличие сетевой оболочки

        asm push si
        GetNetWareShellVersion(&MajorVersion,
                        &MinorVersion, &Revision);
        asm pop si

        if(MajorVersion == 0) {
                printf("\nОболочка NetWare не загружена\n");
                return;
        }

// Необходимо задать букву, обозначающую имя
// локального диска и путь к сетевому каталогу

        if(argc < 3) {
                printf("Укажите имя создаваемого каталога и "
                        "права доступа, \nнапример: makedir sys:users RW");
                return;
        }

// Параметры должны быть заданы заглавными буквами

        strupr(argv[1]);
        strupr(argv[2]);

// Определяем маску прав, разбирая второй
// параметр, заданный при запуске программы

        RightsMask = 0x00;

        for(int i = 0; argv[2][i] != '\0' ; i++) {
                switch(argv[2][i]) {
                case 'R':
                        RightsMask |= 1; break;
                case 'W':
                        RightsMask |= 2; break;
                case 'O':
                        RightsMask |= 4; break;
                case 'C':
                        RightsMask |= 8; break;
                case 'D':
                        RightsMask |= 0x10; break;
                case 'P':
                        RightsMask |= 0x20; break;
                case 'S':
                        RightsMask |= 0x40; break;
                case 'M':
                        RightsMask |= 0x80; break;
                case '-':
                        break;
                default:
                        printf("Ошибка в параметрах\n");
                        return;
                }
        }

// Создаем каталог

        ccode = CreateDirectory(0, argv[1], RightsMask);

        if(!ccode)
                printf("Каталог создан\n");
        else
                printf("Ошибка %02.2X\n", ccode);
}


Программа RENMDIR (листинг 13) позволяет переименовать существующий сетевой каталог. Путь к каталогу и его новое имя следует задать в качестве параметров при запуске программы.

// ===================================================
// Листинг 13. Переименование каталога
// Файл renmdir\renmdir.cpp
//
// (C) A. Frolov, 1993
// ===================================================

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>

#define BYTE unsigned char
#define WORD unsigned int

extern "C" int GetNetWareShellVersion(char *,char *, char *);
extern "C" int RenameDirectory(BYTE, char *, char *);

void main(int argc, char *argv[]) {

        char MajorVersion=0;
        char MinorVersion=0;
        char Revision=0;

        int ccode;

        printf("\n*RENMDIR* (C) Frolov A., 1993\n");

// Проверяем наличие сетевой оболочки

        asm push si
        GetNetWareShellVersion(&MajorVersion,
                        &MinorVersion, &Revision);
        asm pop si

        if(MajorVersion == 0) {
                printf("\nОболочка NetWare не загружена\n");
                return;
        }

// Необходимо задать букву, обозначающую имя
// локального диска и путь к сетевому каталогу

        if(argc < 3) {
                printf("Укажите путь к каталогу и "
                        "новое имя, \nнапример: renmdir sys:users usr");
                return;
        }

// Параметры должны быть заданы заглавными буквами

        strupr(argv[1]);
        strupr(argv[2]);

// Переименовываем каталог

        ccode = RenameDirectory(0, argv[1], argv[2]);

        if(!ccode)
                printf("Каталог переименован\n");
        else
                printf("Ошибка %02.2X\n", ccode);
}


Программа DELDIR (листинг 14) удаляет каталог, путь к которому задан в качестве параметра при запуске программы.

// ===================================================
// Листинг 14. Удаление каталога
// Файл deldir\deldir.cpp
//
// (C) A. Frolov, 1993
// ===================================================

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>

#define BYTE unsigned char
#define WORD unsigned int

extern "C" int GetNetWareShellVersion(char *,char *, char *);
extern "C" int DeleteDirectory(BYTE, char *);

void main(int argc, char *argv[]) {

        char MajorVersion=0;
        char MinorVersion=0;
        char Revision=0;

        int ccode;

        printf("\n*DELDIR* (C) Frolov A., 1993\n");

// Проверяем наличие сетевой оболочки

        asm push si
        GetNetWareShellVersion(&MajorVersion,
                        &MinorVersion, &Revision);
        asm pop si

        if(MajorVersion == 0) {
                printf("\nОболочка NetWare не загружена\n");
                return;
        }

// Необходимо задать букву, обозначающую имя
// локального диска и путь к сетевому каталогу

        if(argc < 2) {
                printf("Укажите путь к каталогу, "
                        "\nнапример: deldir sys:users");
                return;
        }

// Параметр должен быть задан заглавными буквами

        strupr(argv[1]);

// Удаляем каталог

        ccode = DeleteDirectory(0, argv[1]);

        if(!ccode)
                printf("Каталог удален\n");
        else
                printf("Ошибка %02.2X\n", ccode);
}


3.5. Просмотр и изменение атрибутов

Для просмотра маски прав доступа каталога можно использовать функцию GetEffectiveDirectoryRights():

int GetEffectiveDirectoryRights(BYTE DirectoryHandle,
    char *DirectoryPath, BYTE *EffectiveRightsMask);


Параметры DirectoryHandle и DirectoryPath задают путь к каталогу, маску прав которого вам нужно получить. Параметр EffectiveRightsMask - указатель на байт памяти, в который будет записан байт маски прав доступа.

Функция возвращает нулевое значение или код ошибки:

Код ошибки Значение
0x98 Указанный при создании каталога том не существует
0x9B Неправильно задан индекс каталога в параметре DirectoryHandle

Функция SetDirectoryInformation() позволяет задать новые значения для времени и даты создания каталога, идентификатора пользователя, создавшего каталог и изменить маску прав доступа каталога:

int SetDirectoryInformation(BYTE DirectoryHandle,
     char *DirectoryPath, BYTE *NewCreationDateAndTime,
     long NewOwnerObjectID, BYTE MaximumRightsMask);


Параметры DirectoryHandle и DirectoryPath задают путь к нужному нам каталогу.

Параметр NewCreationDateAndTime указывает на массив из четырех байт с новыми значениями даты и времени. Формат этого массива мы рассматривали ранее в разделе, посвященном определению содержимого сетевых каталогов (см. рис. 1).

Параметр NewOwnerObjectID задает идентификатор нового владельца каталога. Этот идентификатор должен быть определен в базе объектов операционной системы Novell NetWare.

Параметр MaximumRightsMask задает новое значение для маски прав доступа каталога.

Функция возвращает нулевое значение или код ошибки:

Код ошибки Значение
0x9B Неправильно задан индекс каталога в параметре DirectoryHandle
0x9C Неправильно задан путь к каталогу

Учтите, что для успешного выполнения функции SetDirectoryInformation() пользователь должен иметь права на изменение атрибутов каталога. Сменить же владельца каталога может только пользователь с правами супервизора.

Если вам нужно изменить только маску прав доступа каталога, удобно воспользоваться функцией ModifyMaximumRightsMask():

int ModifyMaximumRightsMask(BYTE DirectoryHandle,
       char *DirectoryPath, BYTE RevokeRightsMask,
       BYTE GrantRightsMask);


Параметры DirectoryHandle и DirectoryPath задают путь к каталогу, маску которого необходимо изменить.

Параметр RevokeRightsMask задает удаляемые права доступа, а параметр GrantRightsMask - добавляемые.

Программа, использующая эту функцию, должна иметь права на изменение атрибутов каталога, в противном случае маска прав доступа каталога изменена не будет.

Функция возвращает нулевое значение или код ошибки:

Код ошибки Значение
0x8С У программы нет прав для изменения атрибутов
0x98 Указанный при создании каталога том не существует
0x9C Неправильно задан путь к каталогу

Для того чтобы получить права доступа к каталогу вместо функции GetEffectiveDirectoryRights() можно использовать функцию E2h прерывания INT 21h:

На входе: AH = E2h;
DS:SI = Адрес буфера запроса;
ES:DI = Адрес буфера ответа.
На выходе: AL = Код ошибки или 0, если операция завершилась без ошибок.

Буфер запроса имеет следующий формат:

struct REQUEST {
        WORD    PacketLength;        // размер пакета запроса
        BYTE    Function;            // должно быть равно 3
        BYTE    DirectoryHandle;     // индекс каталога
        BYTE    PathLength;          // длина поля пути
        BYTE    DirectoryPath[PathLength]; // путь к каталогу
};


Приведем формат буфера ответа:

struct REPLAY {
        WORD    PacketLength;        // размер пакета
        BYTE            EffectiveRightsMask; // права доступа
};


Для изменения атрибутов каталога вместо функции SetDirectoryInformation() вы также можете использовать функцию E2h прерывания INT 21h, заполнив буфер запроса следующим образом:

struct REQUEST {
        WORD    PacketLength;        // размер пакета запроса
        BYTE    Function;            // должно быть равно 25
        BYTE    DirectoryHandle;     // индекс каталога
        BYTE    NewCreationDateAndTime[4]; // новые дата и время
        long    NewOwnerObjectID;    // идентификатор владельца
        BYTE    MaximumRightsMask;   // маска прав доступа
        BYTE    PathLength;          // длина поля пути
        BYTE    DirectoryPath[PathLength]; // путь к каталогу
};


Если вам надо изменить маску прав доступа для существующего каталога, вы можете воспользоваться той же самой функцией прерывания INT 21h. Приведем формат соответствующего буфера запроса:

struct REQUEST {
        WORD    PacketLength;        // размер пакета запроса
        BYTE    Function;            // должно быть равно 4
        BYTE    DirectoryHandle;     // индекс каталога
        BYTE    RevokeRightsMask;    // удаляемые права доступа
        BYTE    GrantRightsMask;     // добавляемые права доступа
        BYTE    PathLength;          // длина поля пути
        BYTE    DirectoryPath[PathLength]; // путь к каталогу
};


Во всех описанных случаях после вызова прерывания INT 21h регистр AL содержит 0 или код ошибки.

3.5.1. Программа GETMASK

Программа GETMASK (листинг 15) показывает байт маски прав доступа для каталога, путь к которому задан в качестве параметра при запуске программы.

// ===================================================
// Листинг 15. Просмотр маски прав доступа к каталогу
// Файл getmask\getmask.cpp
//
// (C) A. Frolov, 1993
// ===================================================

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>

#define BYTE unsigned char
#define WORD unsigned int

extern "C" int GetNetWareShellVersion(char *,char *, char *);
extern "C" int GetEffectiveDirectoryRights(BYTE, char*, BYTE*);

void main(int argc, char *argv[]) {

        char MajorVersion=0;
        char MinorVersion=0;
        char Revision=0;

        BYTE RightsMask;

        int ccode;

        printf("\n*GETMASK* (C) Frolov A., 1993\n");

// Проверяем наличие сетевой оболочки

        asm push si
        GetNetWareShellVersion(&MajorVersion,
                        &MinorVersion, &Revision);
        asm pop si

        if(MajorVersion == 0) {
                printf("\nОболочка NetWare не загружена\n");
                return;
        }
// Необходимо задать букву, обозначающую имя
// локального диска и путь к сетевому каталогу

        if(argc < 2) {
                printf("Укажите путь к каталогу, "
                        "\nнапример: getmask sys:users");
                return;
        }

// Параметр должен быть задан заглавными буквами

        strupr(argv[1]);

// Удаляем каталог

        ccode = GetEffectiveDirectoryRights(0, argv[1], &RightsMask);

        if(!ccode) {
                printf("Права доступа: %02.2X\n", RightsMask);

                if(RightsMask & 0x01) printf("Read\t(Чтение)\n");
                if(RightsMask & 0x02) printf("Write\t(Запись)\n");
                if(RightsMask & 0x04) printf(
             "Open\t(Открытие файлов)\n");
                if(RightsMask & 0x08) printf("Create\t(Создание)\n");
                if(RightsMask & 0x10) printf("Delete\t(Уничтожение)\n");
                if(RightsMask & 0x20) printf("Parential\t"
             "(Определение прав)\n");
                if(RightsMask & 0x40) printf("Search\t(Поиск)\n");
                if(RightsMask & 0x80) printf("Modify\t"
             "(Изменение атрибутов)\n");
        }
        else
                printf("Ошибка %02.2X\n", ccode);
}


3.5.2. Программа SETMASK

Программа SETMASK (листинг 16) демонстрирует использование функции ModifyMaximumRightsMask() для изменения маски прав доступа существую-щего каталога.

В качестве первого параметра программе необходимо указать путь к каталогу. Второй и третий параметр задают соответственно удаляемые или добавляемые права доступа.

// ===================================================
// Листинг 16. Изменение маски прав доступа
// Файл setmask\setmask.cpp
//
// (C) A. Frolov, 1993
// ===================================================

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>

#define BYTE unsigned char
#define WORD unsigned int

extern "C" int GetNetWareShellVersion(char *,char *, char *);
extern "C" int ModifyMaximumRightsMask(BYTE, char*, BYTE,BYTE);

void main(int argc, char *argv[]) {

        char MajorVersion=0;
        char MinorVersion=0;
        char Revision=0;

        BYTE RevokeRightsMask;
        BYTE GrantRightsMask;

        int ccode;

        printf("\n*SETMASK* (C) Frolov A., 1993\n");

// Проверяем наличие сетевой оболочки

        asm push si
        GetNetWareShellVersion(&MajorVersion,
                        &MinorVersion, &Revision);
        asm pop si

        if(MajorVersion == 0) {
                printf("\nОболочка NetWare не загружена\n");
                return;
        }

// Необходимо задать путь к каталогу, удаляемые
// и добавляемые права доступа

        if(argc < 4) {
                printf("Укажите путь к каталогу, "
                        "удаляемые и добавляемые права доступа, "
                        "\nнапример: setmask sys:users W RO");
                return;
        }

// Параметры должны быть заданы заглавными буквами

        strupr(argv[1]);
        strupr(argv[2]);
        strupr(argv[3]);

// Определяем маску удаляемых прав, разбирая второй
// параметр, заданный при запуске программы

        RevokeRightsMask = 0x00;

        for(int i = 0; argv[2][i] != '\0' ; i++) {
                switch(argv[2][i]) {
                case 'R':
                        RevokeRightsMask |= 1; break;
                case 'W':
                        RevokeRightsMask |= 2; break;
                case 'O':
                        RevokeRightsMask |= 4; break;
                case 'C':
                        RevokeRightsMask |= 8; break;
                case 'D':
                        RevokeRightsMask |= 0x10; break;
                case 'P':
                        RevokeRightsMask |= 0x20; break;
                case 'S':
                        RevokeRightsMask |= 0x40; break;
                case 'M':
                        RevokeRightsMask |= 0x80; break;
                case '-':
                        break;
                default:
                        printf("Ошибка в параметрах\n");
                        return;
                }
        }

// Определяем маску добавляемых прав, разбирая
// третий параметр, заданный при запуске программы

        GrantRightsMask = 0x00;

        for(i = 0; argv[3][i] != '\0' ; i++) {
                switch(argv[3][i]) {
                case 'R':
                        GrantRightsMask |= 1; break;
                case 'W':
                        GrantRightsMask |= 2; break;
                case 'O':
                        GrantRightsMask |= 4; break;
                case 'C':
                        GrantRightsMask |= 8; break;
                case 'D':
                        GrantRightsMask |= 0x10; break;
                case 'P':
                        GrantRightsMask |= 0x20; break;
                case 'S':
                        GrantRightsMask |= 0x40; break;
                case 'M':
                        GrantRightsMask |= 0x80; break;
                case '-':
                        break;
                default:
                        printf("Ошибка в параметрах\n");
                        return;
                }
        }
// Изменяем маску доступа каталога

        ccode = ModifyMaximumRightsMask(0, argv[1],
                RevokeRightsMask, GrantRightsMask);

        if(!ccode)
                printf("Маска прав изменена\n");
        else
                printf("Ошибка %02.2X\n", ccode);
}