6. БАЗА ДАННЫХ ОБЪЕКТОВ6.1. Объекты, коды объектов и права доступа Файл-сервер Novell NetWare содержит базу данных объектов, в которой есть сведения о ресурсах, доступных в сети (файл-серверы, серверы печати и т. п.), о пользователях и группах пользователей и т. д. Эта база данных называется Bindery. Физически она находится в двух скрытых файлах с именами net$bind.sys и net$bval.sys, расположенных в каталоге SYS:SYSTEM. Каждый объект, хранящийся в базе данных, имеет свое имя, уникальный для данного файл-сервера идентификатор, тип, байт доступа. Кроме того, объект может быть статическим или динамическим. Например, каждый пользователь, добавленный супервизором сети или администратором, становится объектом, имеющим имя и идентификатор. Тип такого объекта соответствует значению 1, и это статический объект. Статический объект хранится в базе до тех пор, пока он не будет удален явным образом. Если в сети имеются несколько файл-серверов, то для каждого сервера в базе Bindery имеются объекты, соответствующие всем серверам, активным в сети. Это временные объекты, которые удаляются автоматически, когда соответствующий файл-сервер прекращает свою работу. Вы можете воспользоваться этим обстоятельством для получения списка активных серверов, имеющихся в сети. Программы пользователя могут не только считывать содержимое базы объектов, но и добавлять свои собственные записи, а также редактировать имеющиеся. Для сокращения объема книги мы рассмотрим только способы извлечения информации из базы Bindery. Полностью работа с базой данных объектов описана в документации, поставляющейся вместе с библиотекой Novell NetWare C Interface. 6.1. Объекты, коды объектов и права доступаБаза данных Bindery хранит объекты, которые имеют такие атрибуты, как идентификатор, имя, тип, флаг (статический или динамический объект), байт доступа. Идентификатор объекта - число размером 4 байта, которое является уникальным для данного файл-сервера. Никакие два объекта, сведения о которых хранятся в одной базе данных на одном сервере, не могут иметь одинаковые идентификаторы. Имя объекта представляет собой текстовую строку размером не более 47 байт. В операциях поиска объектов в базе данных по имени в поле имени допускается указывать символы "*" и "?", которые интерпретируются обычным способом, как и в MS-DOS. Библиотека NetWare C Interface имеет функции, позволяющие просматривать список всех объектов базы данных, искать объекты определенного типа или по имени с использованием шаблона и символов "*" и "?". По идентификатору объекта вы можете легко получить его имя и наоборот, по имени можно узнать идентификатор объекта. Тип объекта определяет сетевой ресурс и может принимать следующие значения:
Флаг объекта характеризует время жизни объекта. Если этот флаг равен нулю, объект статический и для его уничтожения необходимо вызывать специальную функцию. Если флаг равен единице, то это динамический объект, который удаляется автоматически, как только исчезает соответствующий сетевой ресурс. Например, если в сети имеется несколько файл-серверов, в базе каждого сервера имеются объекты, описывающие все активные серверы сети. Как только один из серверов прекращает свою работу, из всех баз данных, расположенных на остальных файл-серверах, удаляются объекты, описывающие завершивший работу файл-сервер. Байт доступа используется для определения прав, необходимых для поиска, чтения, создания, редактирования или удаления объекта. Две тетрады байта отвечают за доступ на чтение и доступ на запись. Младшие четыре бита байта доступа отвечают за чтение, старшие - за запись. Для тетрад определены значения от 0 до 4 - уровни доступа. Для того чтобы пользователь или другой объект получили доступ, тетрады его собственного байта доступа должны иметь значения, равные или превышающие значения в байте доступа объекта, к которому запрашивается доступ. Приведем список возможных значений для уровней доступа:
Для определения собственного уровня доступа и идентификатора пользователя программа может воспользоваться функцией GetBinderyAccessLevel(): int GetBinderyAccessLevel(BYTE *SecurityAccessLevel, long *ObjectID); Первый параметр этой функции указывает на слово, в которое будет записан уровень доступа, второй - на двойное слово, в которое будет записан идентификатор пользователя. Вместо функции GetBinderyAccessLevel() можно использовать функцию E3h прерывания INT 21h:
Буфер запроса имеет следующий формат: struct REQUEST { WORD PacketLength; // размер пакета запроса BYTE Function; // должно быть равно 70 }; Буфер ответа имеет следующий формат: struct REPLAY { WORD PacketLength; // размер пакета BYTE SecurityAccessLevel; // уровень доступа long ObjectID; // идентификатор объекта }; По идентификатору объекта вы можете получить его имя и тип с помощью функции GetBinderyObjectName(): int GetBinderyObjectName(long ObjectID, char *ObjectName, WORD *ObjectType); Для объекта, идентификатор которого задан первым параметром, функция возвращает имя объекта и его тип, записывая их в области памяти, указанные при помощи второго и третьего параметров. Функция возвращает 0 при успешном завершении или код ошибки:
Вместо функции GetBinderyObjectName() вы также можете использовать функцию E3h прерывания INT 21h. При этом необходимо использовать форматы буфера запроса и буфера ответа, приведенные ниже. Буфер запроса: struct REQUEST { WORD PacketLength; // размер пакета запроса BYTE Function; // должно быть равно 54 long ObjectID; // идентификатор объекта }; Буфер ответа: struct REPLAY { WORD PacketLength; // размер пакета long ObjectID; // идентификатор объекта WORD ObjectType; // тип объекта BYTE ObjectName[48]; // имя объекта }; Для получения идентификатора объекта по его имени и типу вы можете воспользоваться функцией GetBinderyObjectID(): int GetBinderyObjectID(char *ObjectName,WORD ObjectType, long *ObjectID); Имя и тип объекта задаются первым и вторым параметрами, идентификатор записывается по адресу, заданному третьим параметром. Функция возвращает 0 при успешном завершении или код ошибки:
Вместо функции GetBinderyObjectID() можно использовать функцию E3h прерывания INT 21h. Приведем форматы буфера запроса и буфера ответа. Буфер запроса: struct REQUEST { WORD PacketLength; // размер пакета запроса BYTE Function; // должно быть равно 53 long ObjectType; // тип объекта BYTE ObjectNameLength; // длина имени объекта BYTE ObjectName[ObjectNameLength]; // имя объекта }; Буфер ответа: struct REPLAY { WORD PacketLength; // размер пакета long ObjectID; // идентификатор объекта WORD ObjectType; // тип объекта BYTE ObjectName[48]; // имя объекта }; 6.1.1. Программа BACCESSПриведем текст программы BACCESS (листинг 26), которая определяет и выводит на экран имя, идентификатор, тип объекта, а также его уровень доступа. В программе используются описанные выше функции. // =================================================== // Листинг 26. Программа для просмотра уровня // доступа рабочей станции // Файл baccess\baccess.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 GetBinderyAccessLevel(BYTE *, long *); extern "C" int GetBinderyObjectName(long, char*, WORD*); void main(void) { char MajorVersion=0; char MinorVersion=0; char Revision=0; int ccode; BYTE SecurityAccessLevel; long ObjectID; char ObjectName[48]; WORD ObjectType; printf("\n*BACCESS* (C) Frolov A., 1993\n"); // Проверяем присутствие сетевой оболочки asm push si GetNetWareShellVersion(&MajorVersion, &MinorVersion, &Revision); asm pop si if(MajorVersion == 0) { printf("\nОболочка NetWare не загружена\n"); return; } // Получаем свой идентификатор и уровень доступа GetBinderyAccessLevel(&SecurityAccessLevel, &ObjectID); // По идентификатору определяем свое имя ccode = GetBinderyObjectName(ObjectID, ObjectName, &ObjectType); // Если пользователь подключился к файл-серверу, // выводим его имя, идентификатор и тип if(!ccode) { printf("Пользователь %s, ID = %lX, Type = %d\n", ObjectName, ObjectID, ObjectType); } // Выводим права доступа на чтение printf("Права доступа на чтение:\t"); switch(SecurityAccessLevel & 0x0f) { case 0: printf("Anyone\t(не подключен к файл-серверу)\n"); break; case 1: printf("Logged\t(подключен к файл-серверу)\n"); break; case 2: printf("Object\t(подключен к файл-серверу " "с именем и паролем)\n"); break; case 3: printf("Supervisor\t(права супервизора)\n"); break; case 4: printf("NetWare\t(права Novell NetWare)\n"); break; } // Выводим права доступа на запись printf("Права доступа на запись:\t"); switch((SecurityAccessLevel >> 4) & 0x0f) { case 0: printf("Anyone\t(не подключен к файл-серверу)\n"); break; case 1: printf("Logged\t(подключен к файл-серверу)\n"); break; case 2: printf("Object\t(подключен к файл-серверу " "с именем и паролем)\n"); break; case 3: printf("Supervisor\t(права супервизора)\n"); break; case 4: printf("NetWare\t(права Novell NetWare)\n"); break; } } 6.2. Просмотр базы объектовВ этом разделе мы рассмотрим задачу сканирования базы данных Bindery с целью получения списка имеющихся в ней объектов. Например, вам может потребоваться список активных серверов в сети, список пользователей или список рабочих групп. Для получения списка объектов, сведения о которых хранятся в базе данных Bindery, предназначена функция ScanBinderyObject(): int ScanBinderyObject(char *SearchObjectName, WORD SearchObjectType, long *ObjectID, char *ObjectName, WORD *ObjectType, char *ObjectHasProperties, char *ObjectFlag, char *ObjectSecurity); Эта функция должна использоваться в цикле. При первом вызове в переменную, на которую указывает параметр ObjectID, необходимо записать значение -1. В дальнейшем в эту переменную будет записываться идентификатор найденного объекта. Для поиска следует указать имя объекта (параметр SearchObjectName) и тип объекта (параметр SearchObjectType). В качестве имени объекта можно использовать шаблон с символами "*" и "?". Тип объекта может быть задан конкретно, либо можно указать значение -1. В последнем случае функция будет искать объекты всех типов. Для того чтобы найти все объекты всех типов, в качестве имени надо указать строку "*", в качестве типа задать значение -1. Для найденных объектов в соответствующие переменные, указанные параметрами функции, будут записаны имя объекта (параметр ObjectName), тип объекта (параметр ObjectType), флаг (параметр ObjectFlag), байт доступа (параметр ObjectSecurity). Кроме того, в переменную, на которую указывает параметр ObjectHasProperties, записывается значение 0xFF, если объект имеет дополнительную связанную с ним информацию (Properties), которую можно извлечь специально предназначенными для этого функциями. Функция возвращает 0 при успешном завершении или код ошибки:
Ваша программа должна вызывать функцию ScanBinderyObject() в цикле до тех пор, пока она не возвратит код ошибки, отличный от нуля. Вместо функции ScanBinderyObject() можно использовать функцию E3h прерывания INT 21h:
Буфер запроса имеет следующий формат: struct REQUEST { WORD PacketLength; // размер пакета запроса BYTE Function; // должно быть равно 55 BYTE ObjectID; // идентификатор объекта WORD SearchObjectType; // тип объекта BYTE NameLength; // длина имени образца для // поиска объекта BYTE SearchObjectName[NameLength]; // имя образца для // поиска объекта }; Буфер ответа имеет следующий формат: struct REPLAY { WORD PacketLength; // размер пакета long ObjectID; // идентификатор объекта WORD ObjectType; // тип объекта BYTE ObjectName[48]; // имя объекта BYTE ObjectFlag; // флаг объекта BYTE SecurityAccessLevel; // уровень доступа BYTE ObjectHasProperties; // есть записи }; В базе данных объектов Bindery с каждым объектом может быть связано несколько дополнительных записей, содержащих данные (property). Каждая такая запись имеет свое имя, флаг и байт доступа. Если объект имеет записи, вы можете получить список их имен и других атрибутов при помощи функции ScanProperty(): int ScanProperty(char *ObjectName, WORD ObjectType, char *SearchPropertyName, long *SequenceNumber, char *PropertyName, char *PropertyFlag, char char *PropertySecurity, char *PropertyHasValue, char *MoreProperties); Функция должна вызываться в цикле. При первом вызове переменная, на которую указывает параметр SequenceNumber, должна содержать значение -1. При последующих вызовах содержимое этой переменной будет изменяться автоматически. Для считывания полей функции необходимо указать имя сканируемого объекта (параметр ObjectName), тип объекта (параметр ObjectType), а также имя записи или шаблон имени записи (параметр SearchPropertyName). В шаблоне можно использовать символы "*" и "?". Для найденных записей в соответствующие переменные, указанные параметрами функции, будут записаны имя записи (параметр PropertyName), флаг записи (параметр PropertyFlag), байт доступа (параметр PropertySecurity), признак того, что запись имеет значения (параметр PropertyHasValue), признак того, что в объекте есть еще и другие записи (MoreProperties). Функция возвращает 0 при успешном завершении или код ошибки:
В Novell NetWare для каждого типа объекта существует определенный набор записей, которые могут быть связаны с этим объектом. Например, с объектом типа 1 (обычный пользователь) связаны такие записи, как PASSWORD (пароль) и SECURITY_EQUALS (эквивалентность прав доступа). Содержимое записей можно считать при помощи функции ReadPropertyValue(), которая описана в документации по библиотеке NetWare C Interface. Для этого пользователь, запустивший программу, должен обладать достаточным уровнем доступа. Приведем некоторые имена полей, определенных в NetWare:
Полный список полей и подробное их описание вы найдете в документации по библиотеке NetWare C Interface. 6.2.1. Программа BSCANПриведем исходный текст программы BSCAN (листинг 27), которая просматривает базу данных объектов. Для каждого найденного объекта программа выводит имя и расшифрованный тип объекта, флаг и уровень доступа. Если объект имеет дополнительные записи (properties), вызывается функция, которая выводит имена найденных записей. // =================================================== // Листинг 27. Программа для просмотра содержимого // базы данных объектов // Файл bscan\bscan.cpp // // (C) A. Frolov, 1993 // =================================================== #include <stdlib.h> #include <stdio.h> #include <string.h> #define WORD unsigned int #define BYTE unsigned char extern "C" int GetNetWareShellVersion(char *,char *, char *); extern "C" int ScanBinderyObject(char *, WORD, long *, char *, WORD *, char *, char *, char *); extern "C" int ScanProperty(char *, WORD, char *, long *, char *, char *, char *, char *, char *); void Property(char *ObjectName, WORD ObjectType); void main(void) { char MajorVersion=0; char MinorVersion=0; char Revision=0; int ccode; BYTE ObjectSecurity; long ObjectID; char SearchObjectName[48]; char ObjectName[48]; WORD SearchObjectType; WORD ObjectType; char ObjectHasProperties; char ObjectFlag; printf("\n*BSCAN* (C) Frolov A., 1993\n"); // Проверяем присутствие сетевой оболочки asm push si GetNetWareShellVersion(&MajorVersion, &MinorVersion, &Revision); asm pop si if(MajorVersion == 0) { printf("\nОболочка NetWare не загружена\n"); return; } // Просматриваем в цикле содержимое базы объектов, // ищем объекты всех типов SearchObjectType = -1; // Маска для поиска всех объектов strcpy(SearchObjectName, "*"); for(ObjectID = -1;;) { // Получаем очередной объект ccode = ScanBinderyObject(SearchObjectName, SearchObjectType, &ObjectID, ObjectName, &ObjectType, &ObjectHasProperties, &ObjectFlag, &ObjectSecurity); // Если больше нет объектов или произошла ошибка, завершаем цикл if(ccode) break; // Выводим имя и тип объекта printf("\n%-18s\t", ObjectName); switch(ObjectType) { case 0: printf("??? "); break; case 1: printf("Пользователь "); break; case 2: printf("Группа "); break; case 3: printf("Очередь на печать "); break; case 4: printf("Файл-сервер "); break; case 5: printf("Сервер заданий "); break; case 6: printf("Шлюз "); break; case 7: printf("Сервер печати "); break; case 8: printf("Очередь архивирования "); break; case 9: printf("Сервер для архивирования"); break; case 0xA: printf("Очередь заданий "); break; case 0xb: printf("Администратор "); break; case 0x26: printf("Сервер удаленного моста "); break; default: printf("Объект 0x%04.4X ", ObjectType); break; } // Выводим флаг объекта, который может иметь два значения: // 0 для постоянных объектов и 1 для временных if(ObjectFlag) printf("Временный "); else printf("Постоянный"); // Выводим байт прав, необходимых для получения доступа к объекту printf(" Доступ %02.2X", ObjectSecurity); // Если для объекта имеются дополнительные записи, // выводим их названия if(ObjectHasProperties) Property(ObjectName, ObjectType); } } // ================================================================= // Функция Property выводит названия дополнительных записей объектов // ================================================================= void Property(char *ObjectName, WORD ObjectType) { int ccode; BYTE PropertySecurity; long ObjectID; char SearchPropertyName[16]; char PropertyName[16]; WORD SearchObjectType; char PropertyFlag; long SequenceNumber; char PropertyHasValue; char MoreProperties; // Маска для поиска всех записей strcpy(SearchPropertyName, "*"); for(SequenceNumber=-1;;) { // Получаем запись ccode = ScanProperty(ObjectName, ObjectType, SearchPropertyName, &SequenceNumber, PropertyName, &PropertyFlag, &PropertySecurity, &PropertyHasValue, &MoreProperties); // Если записей больше нет, завершаем цикл if(ccode) break; // Выводим название записи printf("\n\tProperty %s", PropertyName); } } |