[ Next ] [ Previous ] | Chapter 4 - part III | [ Contents ] [ Chapter 3: Multitasking ] [ Chapter 5: Interprocess Communication ]

### File I/O and Extended Attributes - part III

#### An Extended Attribute Example: CHKEA.C

The next example, CHKEA.C, shows a way to find out if the file object has Extended Attributes associated with it. If so, then the query is made as to the size of all of the Extended Attributes that are attached. Last, the names of the types of the Extended Attributes are displayed, and the extended attribute data is dumped.

> CHKEA.C
> CHKEA.MAK
> CHKEA.DEF

CHKEA.EXE expects a command-line input argument that is the name of the file of interest. Wildcard characters are accepted. First, a determination is made if the file object can be located on the hard disk; if successful, the full name of the object is constructed.

`DosQueryPathInfo (` `apchArgs[1],`
`FIL_QUERYFULLNAME,`
`achPath,`
`CCHMAXPATHCOMP );`
`pchPath = strrchr ( achPath, '\\') ;`
`if ( pchPath != NULL )`
`{ pchPath++;`
`*pchPath = 0;`
`} /* endif */`
`ulCount = 1;`
`hdFile = HDIR_SYSTEM`
`arReturn = DosFindFirst(apchArqs[1],`
`&hdFile,`
`FILE_DIRECTORY,`
`&ffbFile,`
`sizeof ( FILEFINDBUF4 ) ,`
`&ulCount,`
`FIL_QUERYEASIZE ) ;`

The DosFindFirst API is the most useful function call available to a programmer when attempting to locate the objects.

`/* Finds the first file object or group of file objects whose names match the specification. */`
`/* The specification can include extended attributes (EA) associated with a file or directory. */`

`APIRET APIENTRY DosFindFirst(`
`PSZ pszFileSpec,/* Address of the ASCIIZ path name of the file or subdirectory to be found. */`
`PHDIR phdir, /* Address of the handle associated with this DosFindFirst request. */`
`ULONG flAttribute,/* Attribute value that determines the file objects to be searched for. */`
`PVOID pfindbuf, /* Result buffer. */`
`ULONG cbBuf, /* The length, in bytes, of pfindbuf. */`
`PULONG pcFileNames,/* Pointer to the number of entries: */`
`ULONG ulInfoLevel /* The level of file information required. */`
`);`

The definition for this API can be found in the BSEDOS.H header file, which is part of the OS/2 Developers Toolkit. Table 4.7 presents the arguments of interest.

Table 4.7 Arguments of DosFindFirst
Arguments | Value(s) | Meaning
phdir | HDIR_SYSTEM | Use STDOUT for handle
phdir | HDIR_CREATE | Handle is created
flAttribute | bit encoded | Type of object to search for
pfindbuf | depends on ulInfoLevel | Result of the request
ulInfoLevel | FIL_STANDARD | Standard file information is returned
ulInfoLevel | FIL_QUERYEASIZE | File EA size is returned
ulInfoLevel | FIL_QUERYEASFROMLIST | Actual EA data is returned

Table 4.8 lists the acceptable values for the flAuribute argument.

Table 4.8 Acceptable Values for flAttribule
Value | Description
MUST_HAVE_ARCHIVED | Files returned must have the archive bit set
MUST_HAVE_DIRECTORY | Files returned must have the directory bit set
MUST_HAVE_SYSTEM | Files returned must have the system bit set
MUST_HAVE_HIDDEN | Files returned must have the hidden bit set
MUST_HAVE_READONLY | Files returned must have the read-only bit set
FILE_ARCHIVED | Files with archive bit set are not returned unless this value is specified
FILE_DIRECTORY | Files with directory bit set are not returned unless this value is specified
FILE_SYSTEM | Files with system bit set are not returned unless this value is specified
FILE_HIDDEN | Files with hidden bit set are not returned unless this value is specified
FILE_READONLY | Files with read-only bit set are not returned unless this value is specified

phdir is an input/output parameter. On input it specifies the type of file handle required by the application. HDIR_SYSTEM tells the operating system to assign a handle that will always be available to the process. This is a handle for standard output. HDIR_CREATE will cause the system to allocate a handle and return it to the application in phdir. Since pszFileSpec can accept wildcard characters, the handle returned can be used in conjunction with the DosFindNext to find the next file object that matches the pszFileSpec.

flAttribute is an input bit-encoded flag that tells DosFindFirst what types of file objects to look for. These bits represent conditions that may be true or must be true. For example, a programmer may want to locate a directory with a particular name that may be hidden; although there are files that can correspond to the pszFileSpec specified, only the directories are of interest. The following bit combination could be used.

`flAttribute = MUST_HAVE_DIRECTORY | FILE_HIDDEN;`

The pfindbuf is a pointer to the buffer that must be allocated prior to making the DosFindFirst call, and must be passed to the API as a pointer. On output the buffer will contain the information specified by the next parameter ulInfoLevel, which can have three valid values associated with it (FIL_STANDARD, FIL_QUERYEASIZIE, FILE_QUEARYEASFROMLIST).

The first value requests DosFindFirst to return FIL_STANDARD information about the file. FIL_STANDARD information contains the data associated with the FILEFINDBUF3 structure.

FIL_QUERYEASIZE information is requested by specifying FIL_QUERYEASIZE for ulInfoLcvcl, and it returns the data associated with the FILEFINDBUF4 structure. Finally, FIL_QUERYEASFROMLIST information is obtained by specifying the value FIL_QUERYEASFROMLIST for the ulInfoLevel. It returns an EAOP2 data structure.

The FIL_QUERYEASFROMLIST request is slightly different from the previous two levels. On input pfindbuf must contain the EAOP2 data structure with the correct names of the EAs to be queried. Since EA data structures are variable in length. the fpGEA2List must contain a pointer to the GEA2 list, which in turn must have the correct value specified for the oNextEntryOffset and szName. The szName specifies the EA to be returned, and the oNextEntryOffset contains the number of bytes from the beginning of the first entry to the end of the next entry. On output the EAOP2 contains a pointer to the fpFEA2List. The fpFEA2List points to the list of FEA2 structures that have the actual EA information. All of the input records must be aligned on a two-word boundary, and the last in the list of GEA2 structures oNextEntryOffset value must be set to zero. The following are the various data buffers that are returned depending on the level of information requested.

- FIL_STANDARD Output generally contains the basic file information without EAs, the FILEFINDBUF3 data structure without the last two fields: cchName and achName.

`/* Level 1 (32-bit) information (used without EAs). */`
`typedef struct _FILEFINDBUF3 {`
`ULONG oNextEntryOffset; /* Offset of next entry. */`
`FDATE fdateCreation; /* Date of file creation. */`
`FTIME ftimeCreation; /* Time of file creation. */`
`FDATE fdateLastAccess; /* Date of last access. */`
`FTIME ftimeLastAccess; /* Time of last access. */`
`FDATE fdateLastWrite; /* Date of last write. */`
`FTIME ftimeLastWrite; /* Time of last write. */`
`ULONG cbFile; /* Size of file. */`
`ULONG cbFileAlloc; /* Allocation size. */`
`ULONG attrFile; /* File attributes. */`
`UCHAR cchName;`
`CHAR achName[CCHMAXPATHCOMP];/* File name including null terminator. */`
`} FILEFINDBUF3;`

The oNextEntryOffset field indicates the number of bytes from the beginning of the current structure to the beginning of the next structure. When this field is 0, the last structure has been reached.

- FIL_QUERYEASIZE Output contains the same file information as FIL_STANDARD plus EA size.

`Level 2 (32-bit) information (used with EAs).`
`typedef struct _FILEFINDBUF4 {`
`ULONG oNextEntryOffset; /* Offset of next entry. */`
`FDATE fdateCreation; /* Date of file creation. */`
`FTIME ftimeCreation; /* Time of file creation. */`
`FDATE fdateLastAccess; /* Date of last access. */`
`FTIME ftimeLastAccess; /* Time of last access. */`
`FDATE fdateLastWrite; /* Date of last write. */`
`FTIME ftimeLastWrite; /* Time of last write. */`
`ULONG cbFile; /* Size of file. */`
`ULONG cbFileAlloc; /* Allocated size. */`
`ULONG attrFile; /* File attributes. */`
`ULONG cbList; /* Size of the file's extended attributes. */`
`UCHAR cchName; /* Length of file name. */`
`CHAR achName[CCHMAXPATHCOMP]; /* File name including null terminator. */`
`} FILEFINDBUF4;`

`typedef FILEFINDBUF4 *PFILEFINDBUF4;`

The cbList field contains the size of the entire EA set for this file object, in bytes.

- FIL_QUERYEASFROMLIST input contains the GEA2 information. Output contains the FEA2 information.

On input, pfindbuf contains an EAOP2 data structure. fpGEA2List contains a pointer to a GEA2 list, which defines the attribute names whose values are to be returned. Entries in the GEA2 list must be aligned on a doubleword boundary. Each oNextEntryOffset field must contain the number of bytes from the beginning of the current entry to the beginning of the next entry.

On output, pfindbuf contains a structure with a set of records, each aligned on a doubleword boundary. These records represent the directory entry and associated EAs for the matched file object. pfindbuf has the following format:

- The EAOP2 data structure, with the fpFEA2List pointer incorrect.
- The EAOP2 data structure occurs only once in the pfindbuf buffer. The rest of these records are repeated for the remainder of the file objects found.
- A FILEFINDBUF3 data structure without the last two fields: cchName and achName.
- A FEA2LIST data structure contained in and related to the FILEFINDBUF3 returned.
- Length of the name string of the file object (cbName)
- Name of the file object matched by the input pattern (achName)

| Gotcha! The result buffer from DosFindFirst should be less than 64KB.

`/* Level 3 (32-bit) (FIL_QUERYEASFROMLIST) file information - get extended attributes. */`

`typedef struct _GEA2 {`
`ULONG oNextEntryOffset; /* Offset to next entry. */`
`BYTE cbName; /* Name length not including NULL. */`
`CHAR szName[1]; /* Attribute name. */`
`} GEA2;`
`typedef GEA2 *PGEA2;`

`/* Get extended attributes list. */`

`typedef struct _GEA2LIST {`
`ULONG cbList; /* Total bytes of structure including full list. */`
`GEA2 list[1]; /* Variable-length GEA2 structures. */`
`} GEA2LIST;`
`typedef GEA2LIST *PGEA2LIST;`

`/* FEA2 defines the format for setting the full extended attributes in the file. */`

`typedef struct _FEA2 {`
`ULONG oNextEntryOffset; /* Offset to next entry. */`
`BYTE fEA; /* Extended attributes flag. */`
`BYTE cbName; /* Length of szName, not including NULL. */`
`USHORT cbValue; /* Value length. */`
`CHAR szName[1]; /* Extended attribute name. */`
`} FEA2;`
`typedef FEA2 *PFEA2;`

`/* FEA2 data structure. */`

`typedef struct _FEA2LIST {`
`ULONG cbList; /* Total bytes of structure including full list. */`
`FEA2 list[1]; /* Variable-length FEA2 structures. */`
`} FEA2LIST;`
`typedef FEA2LIST *PFEA2LIST;`

`/* EAOP2 data structure. */`

`typedef struct _EAOP2 {`
`PGEA2LIST fpGEA2List; /* GEA set. */`
`PFEA2LIST fpFEA2List; /* FEA set. */`
`ULONG oError; /* Offset of FEA error. */`
`} EAOP2;`

`typedef EAOP2 *PEAOP2;`

Figure 4.4 Illustrates the EAOP2 structure in memory.

Figure 4.4 Map of EAOP2 memory buffer
EAOP2 | fpGEA2List | cbList (4 bytes) |
list | cbNextEntryOffest (4 bytes) cbName (1byte) szName (cbName bytes)
fpFEA2List | cbList (4 bytes) |
list | cbNextEntryOffset ( 4 bytes) fEA (1 byte) cbName (1 byte) cbValue (2 bytes) szName (cbName bytes) EA Data (cbValue)

DosFindFirst also accomplishes one other thing. It provides us with the size of the EAs associated with the file. A buffer of this size, pbBuffer, is allocated.
DosEnumAttribute is used to identify the names of the EAs associated with a particular file object.

`/* Identifies names and lengths of extended attributes for a specific file or subdirectory. */`

`APIRET APIENTRY DosEnumAttribute(`
`ULONG ulRefType, /* A value that indicates whether pvFile points to a handle or to an ASCIIZ name. */`
`PVOID pvFile, /* Address of the handle of a file returned by DosOpen; or the ASCIIZ name of a file or subdirectory. */`
`ULONG ulEntry, /* Ordinal of an entry in the file object's EA list, which indicates where in the list to begin the return of EA information. */`
`PVOID pvBuf, /* Address of the buffer where EA information is returned. */`
`ULONG cbBuf, /* The length, in bytes, of the buffer pointed to by pvBuf. */`
`PULONG pulCount, /* Pointer to number of EAs. */`
`ULONG ulInfoLevel); /* Level of information required. */`

The ulRefType tells the DosEnumAttribute about the next input parameter. When the value is 0. the pvFile argument contains a file handle; when the value is 1, the pvFile argument contains a pointer to null-terminated string representing the name of the file object.

If the pvFile contains a handle, then this handle must be obtained by an earlier call to a DosOpen or similar API.
ulEntry describes the ordinal of the file object's EA entry. This value must be non-zero and positive. The value of 1 is indicative of the first EA entry in the list, 2 of the second one, and so on.
pvBuf is the pointer to the output buffer. Only FIL_STANDARD information can be returned; thus the ulInfoLevel is always 1 (ENUMEA_LEVEL_NO_VALUE).
cbBuf is the length of the buffer referenced by the pvBuf.
pulCount is an input/output type argument. On input, the value contains the number of EAs for which the information is requested. If the value of -1L is specified, all of the EAs are queried, and the information is returned in the pvBuf provided the buffer is of adequate size. On output this argument contains the actual number of EAs for which the information was returned. If the buffer is big enough, all of the requested EAs for the file will be returned. On output the buffer contains the list of those FEA2 structures that are aligned on a two-word boundary. The last structure in the list will have the oNextEntryOffset value of zero.
`arReturn = DosEnumAttribute (`
`ENUMEA_REFTYPE_PATH,`
`achFile,`
`1,`
`pbBuffer,`
`ffbFile.cbList,`
`ulCount,`
`ENUMEA_LEVEL_NO_VALUE);`
`printf("\nThis object contains %ld Eas.\n", ulCount);`

In this example, DosEnumAttribute uses a '1" as the EA ordinal, indicating the function is to start enumerating at the first EA. Since pbBuffer is big enough to hold all the EA, it should all be placed in the buffer after just one function call to DosEnumAttribute.
`pdAttribute = (PFEA2)pbBuffer;`
`while ( ulCount != 0 )`
`{ printf("\nFound EA with name (Name length=%i)\"%s\"",`
`(int)(pdAttribute->cbName),pdAttribute->szName);`
`DumpEA(achFile,`
`pdAttribute);`
`ulCount--;`
`pdAttribute = (PFEA2)(((PBYTE)pdAttribute)+`
`pdAttribute->oNextEntryOffset);`
`} /* endwhile */`
Once the EM are enumerated, a while loop is used to loop through and list each EA. The user function DumpEA is covered in more detail later. The next EA is found by adding the oNextEntryOffes to the pbBuffer pointer. Notice the casting involved here. Remember, additions should be made in PBYTE-increments, not in PFEA2-increments.
`arReturn = DosFindNext(hdFile,`
`&ffbFile,`
`sizeof(ffbFile),`
`&ulCount);`

Once all the EAs are listed for one file object, DosFindNext is used to find the next file object this matches the wildcard criteria.

In order so obtain the values of the EAs, Level FIL_QUERYEASFROMLIST information should be specified and DosQueryFileInfo or DosQueryPathInfo should be used. Also, it is important to remember that while one process is reading the EA information, another one can be changing it. To prevent this from becoming a problem. the application must open a file wish the sharing flag set to the deny-write state. This will prevent another user from changing the information in the EAs while in use. Note that the DosEnumAttribute may return a different EA for the same specified ordinal number, because ordinals are assigned only to the existing EAs. An application can delete an EA, then turn around and write another one in its place. The ordinal numbers are not preserved, and thus are not unique. The following formula (from the OS/2 2.1 Control Program Programming Reference manual) shows the information needed to calculate the required buffer size.

> The buffer size is calculated as follows:

4 bytes (for oNextEntryOffset) | +
1 byte (for fEA) | + wild card
1 byte (for cbName) | +
2 bytes (for cbVabse) | +
Value of cbName (for the name of EA) | +
1 byte (for NULL in cbName) | +
Value of cbValue (for the value of EA) |

| Gotcha! Each EA list entry must start on a double-word boundary. The DumpEA function checks the FEA2 structure to see if the EA matches the types, .LONGNAME, .ICONPOS, or .TYPE. These types were selected as examples, simply because each is an ASCII string.

`ulFBufLen = sizeof(FEA2LIST) + pdAttribute->cbName + 1 + /* actual name */`
`pdAttribute->cbValue; /* actual value */`
`pFEA2 = (PFEA2)calloc(1,`
`ulFBufLen);`
`if (!pFEA2)`
`return FALSE;`

`/***************************/`
`/* only one pFEA2 attribute in this list */`
`/***************************/`

`eaopGet.fpFEA2List = (FEA2LIST *)pFEA2;`
`eaopGet.fpFEA2List->cbList = ulFBufLen;`

The first step is building the fpFEA2List structure for input. The size of the buffer is calculated by adding the structure size, plus the size of the EA name, cbName, plus the sire of the EA data cbValue, plus one byte for a '\0' appended to the name. The fpFEA2List structure in the eaopGet structure is set equal to the memory that has been allocated. The only other initialization involved is setting cbList equal to the size of the output buffer.

`ulGBufLen = sizeof(GEA2LIST) + pdAttribute->cbName +1;`
`pGEA2List = (GEA2LIST *) calloc(1,`
`ulGBufLen);`
`if (!pGEA2List)`
`{ free(pFEA2);`
`return FALSE;`
`}`
`/**************************/`
`/* initialize fpGEA2List */`
`/**************************/`
`pGEA2List->cbList = ulGBufLen;`
`pGEA2List->list[0].oNextEntryOffset = 0;`
`pGEA2List->list[0].cbName = pdAttribute->cbName;`
`strcpy(pGEA2List->list[0].szName,`
`pdAttribute->szName);`
`eaopGet.fpGEA2List = (GEA2LIST *) pGEA2List;`

The fpGEA2List structure is used to tell the DosQuery functions which EAs the programmer is interested in. The buffer size is calculated like the fpFEA2List buffer. The offset to the next list entry is set to 0, because this example is looking for only one EA at a time. The cbList variable is the buffer size. The cbNanse variable is the EA name string size. The actual name is copied into the szName buffer. The last assignment is setting fpGEA2List in the eaopGet structure equal to the pGEA2List structure that has just been created.
DosQueryPathInfo is used to retrieve the actual EA data. The prototype for the function is:

`/* Gets file information for a file or subdirectory.*/`

`APIRET DosQueryPathInfo(`
`PSZ pszPathName, /* Address of the ASCIIZ file specification of the file or subdirectory. */`
`ULONG ulInfoLevel, /* The level of path information required. */`
`PVOID pInfoBuf, /* Address of the storage area containing the requested level of path information. */`
`ULONG cbInfoBuf); /* The length, in bytes, of pInfoBuf. */`

The first parameter is the filename to use to query the information. The second parameter is the level of information to retrieve. The value FIL_QUERYEASFROMLIST will retrieve the EA information. The third parameter is a pointer to the EAOP2 structure. The last parameter is the size of the EAOP2. This value is equal to the size of the fpFEA2List structure plus the size of the fpGEA2List structure.

`rc = DosQueryPathInfo(pszFile,`
`FIL_QUERYEASFROMLIST,`
`(PVOID)&eaopGet,`
`ulEBufLen);`
`ulSize = sizeof(FEA2LIST);`
`pFEA2 = (PFEA2)eaopGet.fpFEA2List->list;`
`ulDataStart = ulSize+pFEA2->cbName;`
`ptrEAData = (PEAINFO)((PBYTE)eaopGet.fpFEA2List + ulDataStart);`
`ptrEADataHolder = calloc(1,`
`sizeof(EAINFO) +`
`ptrEAData->usEALength+1);`
`printf("Type = 0x%x ",`
`ptrEAData->usEAType);`
`printf("Length = 0x%x",`
`ptrEAData->usEALength);`
`memcpy(ptrEADataHolder,`
`ptrEAData->bEAData,`
`ptrEAData->usEALength);`
`printf("\nData = %s", ptrEADataHolder);`

The last step in the DumpEA function is actually to print out the EA data. The data is returned in the fpFEA2List structure that was set up on input. First, the offset into the fpFEA2List where the EA data is located is found by adding the size of the FEA2 structure plus the size of the attribute name. If this sounds confusing, take a look at Figure 4.4 to help illustrate this. The EA data is formatted in the following manner. The first USHORT contains the type of EA data. The second USHORT contains size of the EAdata. All the bytes that follow contain the actual data located in that EA. This data is copied into a memory buffer that contains enough space for a '\0' character at the end. The EA data does not contain the '\0' character at the end of the data, because not all EA data is in the form of an ASCII null-terminated string.

[ Next ] [ Previous ] | Chapter 4 - part III | [ Contents ] [ Chapter 3: Multitasking ] [ Chapter 5: Interprocess Communication ]