[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.

/* 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.

 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.

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:

 
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 ]