void StaticPhysicalDiskInstance::Update()

in source/code/scxsystemlib/disk/staticphysicaldiskinstance.cpp [1564:2295]


    void StaticPhysicalDiskInstance::Update()
    {
        static SCXCoreLib::LogSuppressor suppressor(SCXCoreLib::eWarning, SCXCoreLib::eTrace);
        /*
         * In some cases, we must modify the device ID (m_device) that was
         * passed to us by provider (for example, the provider never passes
         * the raw device name, but we need that). Rather than changing the
         * real device, and then potentially running into problems if the
         * device ID doesn't match with the device ID from other sources,
         * we make a copy of the device in m_rawDevice.  Then we can change
         * this, as needed, for various platforms.
         *
         * The m_rawDevice is for internal use only, and never shared via a
         * Get() method.
         */

        if (m_rawDevice.length() == 0)
        {
            m_rawDevice = m_device;
        }

#if defined(linux)

        // Open the device (Note: We must have privileges for this to work).
        if (!m_deps->open(StrToUTF8(m_rawDevice).c_str(), O_RDONLY | O_NONBLOCK))
        {
            throw SCXErrnoOpenException(m_rawDevice, errno, SCXSRCLOCATION);
        }

        // Clear properties. It is important to clear all the properties here because when we collect data from the
        // hardware we may do so only if property is not set already. So if all of the properties are not cleared here
        // that may prevent the code from resetting the particular property with fresh data from the hardware.
        Clear();

        // For many properties we have two interfaces available: SCSI with SG_ ioctls and ATA with HDIO_ ioctls.
        // Often ATA drives are also available through SCSI interface by using SCSI translation. The approach we use
        // is to try any interface available for particular property and keep the best data we get. Determining the
        // actual device type is not 100% reliable. Often ATA drives are named sdX instead of hdX. Also ATA drives
        // may be exposed through SCSI interface and it seems that emulation flag may be inaccurate regardless of the
        // translation. Considering all that the approach we take is to first the SCSI ioctls and decide it is SCSI drive
        // unless ATA ioctls work as well. In that case we assume it is ATA drive and SCSI was just translation layer.
        // If neither works we check the drive name to determine if it is a virtual drive.

        // Be nice, see if we have SG_IO before using it. We can be nice because it appears that if SG_GET_VERSION_NUM
        // fails, then SG_IO also always fails. If that changes then this if should be removed and functions using
        // SG_IO should be called regardless of SG_GET_VERSION_NUM call.
        int ver = 0;
        if (m_deps->ioctl(SG_GET_VERSION_NUM, &ver) == 0 )
        {
            // We have SCSI ioctl working. However it might also be just ATA translation. We also have to check
            // HDIO_ ioctls. For now we will assume it is SCSI drive.
            m_intType = eDiskIfcSCSI;
            // SG_IO is available only on versions 3.0 and above.
            if(ver >= 30000)
            {
                // We have SG_IO.
                unsigned char rsp_buff[255];
                    
                // Refer to "SCSI Primary Commands - 4" (SPC-4), chapter 6.4.1, available at www.t10.org/members/w_spc4.htm.
                // Do standard INQUIRY, which is page code 0, EVPD 0. SqInq() function uses SG_IO.
                if (SqInq(0, 0, rsp_buff, sizeof (rsp_buff)) == true)
                {
                    // Byte 15:8 of Standard INQUIRY data is "T10 VENDOR IDENTIFICATION", we use it as manufacturer.
                    string manufacturer(reinterpret_cast<const char *>(rsp_buff) + 8, 8);
                    // Trim string to first 0.
                    manufacturer = manufacturer.c_str();
                    // Trim the whitespaces. It is necessary because if property is not available it may not be just.
                    // empty string but a string of 'space' characters.
                    m_manufacturer = StrTrim(StrFromUTF8(manufacturer));

                    // Treat PRODUCT REVISION LEVEL as firmware revision.
                    // PRODUCT REVISION LEVEL has 4 bytes, starting at byte 32.
                    string fwRev(reinterpret_cast<const char *>(rsp_buff) + 32, 4);
                    fwRev = fwRev.c_str();
                    m_Properties.firmwareRevision = StrTrim(StrFromUTF8(fwRev));

                    // Treat PRODUCT IDENTIFICATION as model.
                    // PRODUCT IDENTIFICATION has 16 bytes starting at byte 16 */
                    string model(reinterpret_cast<const char *>(rsp_buff) + 16, 16);
                    model = model.c_str();
                    m_model = StrTrim(StrFromUTF8(model));

                    // Find out if media is removable.
                    // RMB bit 7 byte 1 indicate removable or not.
                    if (filterbit<unsigned char>(rsp_buff[1], 7))
                    {
                        // Removable media other than floppy.
                        m_Properties.mediaType = mediaTypeNames[1];
                        m_Properties.capabilities[eDiskCapSupportsRemovableMedia] = eDiskCapSupportsRemovableMedia;
                    }
                    else
                    {
                        // Fixed hard disk media.
                        m_Properties.mediaType = mediaTypeNames[2];
                    }
                }
                // Refer to "SCSI Primary Commands - 4" (SPC-4), chapter 7.7.1, available at www.t10.org/members/w_spc4.htm.
                // Page code 80h is to get Unit Serial Number, optional. SqInq() function uses SG_IO.
                if (SqInq(0x80, 1, rsp_buff, sizeof(rsp_buff)))
                {
                    string serialNumber(reinterpret_cast<const char *>(rsp_buff) + 4, rsp_buff[3]);
                    // Trim string to first 0.
                    serialNumber = serialNumber.c_str();
                    // Trim the whitespaces. It is necessary because if property is not available it may not be just
                    // empty string but a string of 'space' characters.
                    m_Properties.serialNumber = StrTrim(StrFromUTF8(serialNumber));
                }
                // Set the m_Properties.availability. Since this is our first attempt we do not have to check if this
                // property is also set. Also, this is more advanced way of getting power state so we give it a priority.
                // CheckSCSIPowerMode() function also uses SG_IO.
            }
            CheckSCSIPowerMode();
        }
        // Rest of the SCSI. Also update disk type to SCSI unless it is already set to SCSI in the previous code.
        // We do not put this function inside SG_GET_VERSION_NUM block since SG_GET_VERSION_NUM is the upper SG layer
        // of the SCSI functionality while UpdateSCSIAttributes() uses SCSI_IOCTL_ that is middle layer SCSI. It is
        // unclear if SG_GET_VERSION_NUM can be used to check if SCSI_IOCTL_ is available. To be sure we decouple the two.
        UpdateSCSIAttributes();

        // Now be nice with HDIO_, first check if we have HDIO_ interface. We can be nice because it appears that if
        // HDIO_GET_32BIT fails then other HDIO_ ioctl-s also fail. However if that changes then other HDIO_
        // ioctl-s should be called regardless of HDIO_GET_32BIT. Note, even if HDIO_GET_32BIT succeeds other
        // HDIO_ ioctl-s like HDIO_GET_IDENTITY may still sometimes fail.
        int io32bit = 0;
        if (m_deps->ioctl(HDIO_GET_32BIT, &io32bit) == 0)
        {
            // We have HDIO_ ioctl. It is most likely ATA drive regardless of SCSI test that happened before since SCSI
            // could be only translation layer. It seems that SCSI is not available through HDIO_ ioctls (usually and
            // for now).
            m_intType = eDiskIfcIDE;

            struct hd_driveid hdid;
            memset(&hdid, 0, sizeof(hdid));
            if (m_deps->ioctl(HDIO_GET_IDENTITY, &hdid) == 0)
            {
                string serialNumberHDIO(reinterpret_cast<char*>(hdid.serial_no), sizeof(hdid.serial_no));
                // Trim string to first 0.
                serialNumberHDIO = serialNumberHDIO.c_str();
                // Trim the whitespaces. It is necessary because if property is not available it may not be just
                // empty string but a string of 'space' characters.
                wstring serialNumber = StrTrim(StrFromUTF8(serialNumberHDIO));
                // Check if we have a better serial number. Sometimes one set of ioctl-s gives less info than the other
                // so we will usually go by size when determining what data is better.
                if (serialNumber.size() > m_Properties.serialNumber.size())
                {
                    m_Properties.serialNumber = serialNumber;
                }

                // Try to get firmware revision.
                string fwRevHDIO(reinterpret_cast<char*>(hdid.fw_rev), sizeof(hdid.fw_rev));
                fwRevHDIO = fwRevHDIO.c_str();
                wstring fwRev = StrTrim(StrFromUTF8(fwRevHDIO));
                if (fwRev.size() > m_Properties.firmwareRevision.size())
                {
                    m_Properties.firmwareRevision = fwRev;
                }

                // Try to get model name.
                string modelHDIO(reinterpret_cast<char*>(hdid.model), sizeof(hdid.model));
                modelHDIO = modelHDIO.c_str();
                wstring model = StrTrim(StrFromUTF8(modelHDIO));
                if (model.size() > m_model.size())
                {
                    m_model = model;
                }
                
                // Get the power management capabilities. NOTE: right now we do not get power management caps by
                // using SCSI interface, however if at some point latter we do implement that we will have to
                // decide what data to use, SCSI or IDE. Right now we simply set power management using IDE if data is
                // available from HDIO_GET_IDENTITY.
                // First check if power management is supported.
                // If bit 3 of word 82 is set to one, the Power Management feature set is supported.
                m_Properties.powermanagementSupported = filterbit<unsigned short>(hdid.command_set_1, 3);
                // Collect check power management capabilities.
                if (!m_Properties.powermanagementSupported)
                {
                    m_Properties.powerManagementCapabilities.push_back(eNotSupported);
                }
                else
                {
                    // Word 85 bit 3, 1 = Power Management feature set enabled.
                    if (filterbit<unsigned short>(hdid.cfs_enable_1, 3))
                    {
                        m_Properties.powerManagementCapabilities.push_back(eEnabled);
                    }
                    else
                    {
                        m_Properties.powerManagementCapabilities.push_back(eDisabled);
                    }
                    // If bit 5 of word 86 is set to one, the Power-Up In Standby feature set has been enabled via the
                    // SET FEATURES command.
                    if(filterbit<unsigned short>(hdid.cfs_enable_2, 5))
                    {
                        m_Properties.powerManagementCapabilities.push_back(ePowerSavingModesEnteredAutomatically);
                    }
                }

                // Find out if media is removable. We give priority to SCSI INQUIRE command, so we will update this
                // property only if it was not done already.
                if(m_Properties.mediaType == mediaTypeNames[3])
                {
                    // Media type is "Format is unknown" which means it was not already set by SCSI.
                    if (filterbit<unsigned short>(hdid.config, 7))
                    {
                        m_Properties.mediaType = mediaTypeNames[1];
                        m_Properties.capabilities[eDiskCapSupportsRemovableMedia] = eDiskCapSupportsRemovableMedia;
                    }
                    else
                    {
                        m_Properties.mediaType = mediaTypeNames[2];
                    }
                }
            }
            else
            {
                std::wstringstream out;
                out << L"On device \"" << m_device << L"\" ioctl(HDIO_GET_32BIT) succeeded but "
                        L"ioctl(HDIO_GET_IDENTITY) failed with errno = " <<
                        StrFrom(errno) << L".";
                SCX_LOG(m_log, suppressor.GetSeverity(out.str()), out.str());
            }
            // See if availability property is already set. If it is then don't do anything because CheckSCSIPowerMode()
            // has priority so we want to keep it if it succeeded. CheckSCSIPowerMode() has priority because it is done 
            // by using SCSI INQUIRE command which seems to be more advanced. Also, if CheckATAPowerMode() fails it
            // resets the availability to eDiskAvaUnknown so we lose data if it is already set by CheckSCSIPowerMode().
            if (m_Properties.availability == eDiskAvaUnknown)
            {
                // OK, availability is not already set, set it now.
                CheckATAPowerMode();
            }
        }
        // We tried ioctl's. If we still don't have the disk type then try to determine from the disk name.
        // Determining ATA or SCSI from name is unreliable and there is a trend to have all disks named sdX.
        // We try to determine only if drive is virtual.
        if (m_intType == eDiskIfcUnknown)
        {
            if (GetId().substr(0, 3) == L"vxd")
            {
                m_intType = eDiskIfcVirtual;
            }
        }

        // Determine remaining properties.
        if (!m_cdDrive)
        {
            // Not CD or DVD, get geometry.
            DiskSizeAndGeometryFromKernel();
        }
        CheckSupportWriting();
        UpdateDiskSignature();
        ParsePartitions();

        // Not all the code paths detect power management capabilities. For example SCSI or virtual drives are not checked
        // for this property. In the case there are no power management capabilities, powermanagementSupported must contain
        // eNotSupported.
        if (m_Properties.powerManagementCapabilities.empty())
        {
            m_Properties.powerManagementCapabilities.push_back(eNotSupported);
        }

        // Close the handle to the drive.
        if (0 != m_deps->close())
        {
            // Can't imagine that we can't close the fd, but if so, we don't want to miss that.
            throw SCXErrnoException(L"close", errno, SCXSRCLOCATION);
        }

#elif defined(aix)

        int res;

        /*
         * On AIX, we use the ODM interface (Object Data Model).  We query the
         * ODM for various information that we need.  The ODM is populated at
         * boot time with the hardware that the system contains (plus a bunch
         * of other stuff).
         */

        std::wstring id = GetId();
        size_t slashpos;
        if ( (slashpos = id.rfind('/')) != std::wstring::npos )
        {
            id = id.substr(slashpos+1);
        }

        std::wstring criteria = L"name=" + id;
        bool fIsVirtualDisk = false;

        // Get the CuDv object to give us the interface type
        // This also tells us if this is a virtual disk ...
        //
        // If it's a virtual disk, minimal information is avalable, so we do
        // the best we can do and provide reasonable defaults for the rest.

        struct CuDv dvData;
        memset(&dvData, '\0', sizeof(dvData));
        switch (LookupODM(CuDv_CLASS, criteria, &dvData))
        {
        case 0: /* Huh?  Criteria failed???  We're broken ... */
            throw SCXInternalErrorException(StrAppend(L"Invalid ODM (CuDv) criteria: ", criteria), SCXSRCLOCATION);
            break;

        case 1: /* Success case */
        {
            // The interface, found in PdDvLn_Lvalue, is of the form:
            //    <class>/<subclass>/<type>
            // The <subclass> will be a string like "scsi", or "iscsi",
            // or "ide" (perhaps on AIX for x86).

            std::vector<wstring> parts;
            StrTokenize(StrFromUTF8(dvData.PdDvLn_Lvalue), parts, L"/");

            if (0 == StrCompare(parts[1], L"scsi", true)
                || 0 == StrCompare(parts[1], L"iscsi", true))
            {
                m_intType = eDiskIfcSCSI;
            }
            else if (0 == StrCompare(parts[1], L"ide", true))
            {
                m_intType = eDiskIfcIDE;
            }
            else if (0 == StrCompare(parts[1], L"vscsi", true))
            {
                m_intType = eDiskIfcSCSI;
                fIsVirtualDisk = true;
            }
            else if (0 == StrCompare(parts[1], L"vide", true))
            {
                m_intType = eDiskIfcIDE;
                fIsVirtualDisk = true;
            }
            else if (0 == StrCompare(parts[1], L"advm", true))
            {
                m_intType = eDiskIfcVirtual;
                fIsVirtualDisk = true;
            }

            break;
        }

        default:
            /* Ignore all other values (just case -1) */
            break;
        }

        if ( ! fIsVirtualDisk )
        {
            // Get the VPD data, which gives us manufacturer and model

            struct CuVPD vpdData;
            memset(&vpdData, '\0', sizeof(vpdData));
            switch (LookupODM(CuVPD_CLASS, criteria, &vpdData))
            {
            case 0: /* Huh?  Criteria failed???  We're broken ... */
                throw SCXInternalErrorException(L"Invalid ODM (CuVPD) criteria: " + criteria, SCXSRCLOCATION);
                break;

            case 1: /* Success case */
                DecodeVPD(&vpdData);
                break;

            default:
                /* Ignore all other values (just case -1) */
                break;
            }

            // Next get the CuAt object, which gives us the size
            // Note: On some devices, this isn't available ...
            wstring attrCriteria = criteria + L" and attribute=size_in_mb";
            struct CuAt atData;
            memset(&atData, '\0', sizeof(atData));
            if (1 == LookupODM(CuAt_CLASS, attrCriteria, &atData))
            {
                m_sizeInBytes = atol(atData.value);
                m_sizeInBytes *= 1024 * 1024; /* Get from MB to bytes */
            }
        }
        else
        {
            // Note: CuAt is present for virtual disks but doesn't have the size_in_mb attribute.
            //       CuVPD isn't available at all for firtual disks (thus our defaults here)

            m_manufacturer = L"IBM";        // Obviously
            m_model = L"Virtual";           // Reasonabe value (sort of)
            m_sizeInBytes = 0;              // Not able to determinate
        }

#elif defined(hpux)

        /*
         * Open the device (Note: we must have privileges for this to work)
         *
         * Note: We usually get a device like "/dev/disk/disk3".  While we can open
         * this device, the ioctl()'s we call won't work unless we have the raw device
         * open.
         *
         * As a result, we bag the device we received and construct our own given the
         * name of the device.
         */

        if (m_rawDevice == m_device)
        { // Happens first time called
            if (wstring::npos != m_rawDevice.find(L"/dsk/"))
            {
                m_rawDevice = SCXCoreLib::StrAppend(L"/dev/rdsk/", GetId());
            }
            else
            {
                m_rawDevice = SCXCoreLib::StrAppend(L"/dev/rdisk/", GetId());
            }
        }
        if (!m_deps->open(StrToUTF8(m_rawDevice).c_str(), O_RDONLY))
        {
            throw SCXErrnoOpenException(m_rawDevice, errno, SCXSRCLOCATION);
        }

        /* Look up the disk manufacturer and model */

#if defined(hpux) && (PF_MAJOR==11) && (PF_MINOR<31)
        //
        // Need to use old-style structures for HP-UX 11i v2
        //

        struct inquiry_2 scsiData;
        memset(&scsiData, 0, sizeof(scsiData));
        if (0 == m_deps->ioctl(int(SIOC_INQUIRY), &scsiData))
        {
            /* We got space-filled strings in our structure - null terminate 'em */

            char vendorID[sizeof(scsiData.vendor_id)+1], productID[sizeof(scsiData.product_id)+1];

            memset(vendorID, '\0', sizeof(vendorID));
            memset(productID, '\0', sizeof(productID));

            memcpy(vendorID, scsiData.vendor_id, sizeof(vendorID)-1);
            memcpy(productID, scsiData.product_id, sizeof(productID)-1);

            m_manufacturer = StrTrim(StrFromUTF8(vendorID));
            m_model = StrTrim(StrFromUTF8(productID));
        }
        else
        {
            SCX_LOGERROR(m_log, L"System error getting drive inquiry data, errno=" + StrFrom(errno));
        }

        /* Look up capacity, interface type, etc */

        disk_describe_type diskDescr;
        memset(&diskDescr, 0, sizeof(diskDescr));
        if (0 == m_deps->ioctl(int(DIOC_DESCRIBE), &diskDescr))
        {
            /* Set SCSI if we know that's what we've got (20=SCSI) */

            if (20 == diskDescr.intf_type)
            {
                m_intType = eDiskIfcSCSI;
            }

            /* Capacity and sector size */

            m_sizeInBytes = static_cast<scxulong> (diskDescr.maxsva);

            m_sectorSize = diskDescr.lgblksz;
            if (0 == m_sectorSize)
            {
                /* Hmm, didn't get a sector size - let's make a reasonable guess */
                m_sizeInBytes *= 512;
            }
            else
            {
                m_sizeInBytes *= m_sectorSize;
            }
        }
        else
        {
            SCX_LOGERROR(m_log, L"System error getting drive description, errno=" + StrFrom(errno));
        }
#else
        inquiry_3_t scsiData;
        memset(&scsiData, 0, sizeof(scsiData));
        if (0 == m_deps->ioctl(int(SIOC_INQUIRY), &scsiData))
        {
            /* We got space-filled strings in our structure - null terminate 'em */

            char vendorID[sizeof(scsiData.vendor_id)+1], productID[sizeof(scsiData.product_id)+1];

            memset(vendorID, '\0', sizeof(vendorID));
            memset(productID, '\0', sizeof(productID));

            memcpy(vendorID, scsiData.vendor_id, sizeof(vendorID)-1);
            memcpy(productID, scsiData.product_id, sizeof(productID)-1);

            m_manufacturer = StrTrim(StrFromUTF8(vendorID));
            m_model = StrTrim(StrFromUTF8(productID));
        }
        else
        {
            SCX_LOGERROR(m_log, L"System error getting drive inquiry data, errno=" + StrFrom(errno));
        }

        /* Look up capacity, interface type, etc */

        disk_describe_type_ext_t diskDescr;
        memset(&diskDescr, 0, sizeof(diskDescr));
        if (0 == m_deps->ioctl(int(DIOC_DESCRIBE_EXT), &diskDescr))
        {
            /* Set SCSI if we know that's what we've got (20=SCSI) */

            if (20 == diskDescr.intf_type)
            {
                m_intType = eDiskIfcSCSI;
            }

            /* Capacity and sector size */

            m_sizeInBytes = static_cast<scxulong> (diskDescr.maxsva_high);
            m_sizeInBytes = (m_sizeInBytes << 32) + diskDescr.maxsva_low;

            m_sectorSize = diskDescr.lgblksz;
            if (0 == m_sectorSize)
            {
                /* Hmm, didn't get a sector size - let's make a reasonable guess */
                m_sizeInBytes *= 512;
            }
            else
            {
                m_sizeInBytes *= m_sectorSize;
            }
        }
        else
        {
            SCX_LOGERROR(m_log, L"System error getting drive description, errno=" + StrFrom(errno));
        }
#endif

        /* Close the handle to the drive */

        if (0 != m_deps->close())
        {
            /* Can't imagine that we can't close the device, but if so, we don't want to miss that */

            throw SCXErrnoException(L"close", errno, SCXSRCLOCATION);
        }

#elif defined(sun)

        /*
         * Open the device (Note: We must have privileges for this to work)
         *
         * Note: We usually get a device like "/dev/dsk/c0d0", but this won't
         * work.  We try once (just in case), but if that fails, we build our
         * own path to look like: "/dev/rdsk/c0d0s0".  If that fails too, then
         * we just bag it.
         */

        SCX_LOGHYSTERICAL(m_log, L"Update(): trying disk device " + m_rawDevice );
        if (!m_deps->open((const char *)StrToUTF8(m_rawDevice).c_str(), (int)O_RDONLY))
        {
            /* Reconstruct the path from the name and try again */
            /* Note that we need to check several slices if the disk does not use all of them. */
            for (int i = 0; i<=15; ++i)
            {
                m_rawDevice = L"/dev/rdsk/" + GetId() + StrAppend(L"s", i);
                SCX_LOGHYSTERICAL(m_log, L"Update(): re-trying disk device " + m_rawDevice );
                if (!m_deps->open((const char *)StrToUTF8(m_rawDevice).c_str(), (int)O_RDONLY))
                {
                    if ((EIO != errno && ENXIO != errno) || 15 <= i) // EIO _or_ ENXIO is received if the slice is not used.
                    {
                        throw SCXErrnoOpenException(m_rawDevice, errno, SCXSRCLOCATION);
                    }
                }
                else //the raw file has been opened
                {
                    /*for raw device /dev/rdsk/cntndnsn
                    Where:
                    cn    controller n
                    tn    SCSI target id n (0-6)
                    dn    SCSI LUN n (0-7 normally; some HBAs support LUNs to 15
                               or 32. See the specific manpage for details)
                    referece: http://www.s-gms.ms.edus.si/cgi-bin/man-cgi?sd+7D
                    */
                    size_t pos = GetId().find_first_of('c');
                    if( pos != wstring::npos )
                    {
                        m_Properties.SCSIBus=StrToUInt(GetId().substr(pos+1,1));
                    }
                    pos = GetId().find_first_of('t');
                    if( pos != wstring::npos )
                    {
                        m_Properties.SCSITargetId=StrToUInt(GetId().substr(pos+1,1));
                    }
                    pos = GetId().find_first_of('d');
                    if( pos != wstring::npos )
                    {
                        m_Properties.SCSILogicalUnit=StrToUInt(GetId().substr(pos+1,1));
                    }

                    break;

                }
            }
        }

        SCX_LOGHYSTERICAL(m_log, L"Update(): opened disk device " + m_rawDevice );

        /*
          Look up the size of the disk.

          We also use this call to determine if this is a disk at all.  On Sun,
          we get called for all sorts of devices that may not be hard drives at
          all ...
        */

        struct dk_minfo dkmedia;
        memset(&dkmedia, '\0', sizeof(dkmedia));
        if (0 == m_deps->ioctl(int(DKIOCGMEDIAINFO), &dkmedia))
        {
            m_sizeInBytes = static_cast<scxulong> (dkmedia.dki_capacity) * dkmedia.dki_lbsize;

            // verify that media is supported
            if ( dkmedia.dki_media_type != DK_FIXED_DISK ){
                // close the descriptor - strange, but we don't use helper class
                // with 'close-in-dtor' here
                m_deps->close();
                // this exception means that disk is not of interest (CD, tape, etc)
                throw SCXNotSupportedException(m_rawDevice + L" has unsupported type " + StrFrom(dkmedia.dki_media_type)
                                               , SCXSRCLOCATION);
            }
            else //the device is fixed disk
            {
                //For fixed disk drives, MediaLoaded will always be TRUE
                m_Properties.mediaLoaded=true;

                //"Fixed hard disk media"
                m_Properties.mediaType=mediaTypeNames[2];

            }


        }
        else
        {
            std::wstringstream out;
            out << L"On device \"" << m_device << L"\" ioctl(DKIOCGMEDIAINFO) failed with errno = " <<
                    StrFrom(errno) << L".";
            SCX_LOG(m_log, suppressor.GetSeverity(out.str()), out.str());
        }

        /* Look up the drive interface type */

        struct dk_cinfo dkinfo;
        memset(&dkinfo, '\0', sizeof(dkinfo));
        if (0 == m_deps->ioctl(int(DKIOCINFO), &dkinfo))
        {
            switch (dkinfo.dki_ctype)
            {
            case DKC_DIRECT:
                m_intType = eDiskIfcIDE;
                break;

            case DKC_SCSI_CCS:
                m_intType = eDiskIfcSCSI;
                break;
            }
        }
        else
        {
            SCX_LOGTRACE(m_log, L"System error getting disk interface information, errno=" + StrFrom(errno));
        }

        /* Look up the drive's sector size */

        struct vtoc dkvtoc;
        memset(&dkvtoc, '\0', sizeof(dkvtoc));
        if (0 == m_deps->ioctl(int(DKIOCGVTOC), &dkvtoc))
        {
            m_sectorSize = dkvtoc.v_sectorsz;

            //m_Properties.partitions=dkvtoc.v_nparts;
            int validPartitionNumber=0;
            for(int i=0;i<dkvtoc.v_nparts;i++)
            {
                if(dkvtoc.v_part[i].p_tag>0)
                {
                    validPartitionNumber++;
                }
            }
            m_Properties.partitions = validPartitionNumber;
        }
        else
        {
            std::wstringstream out;
            out << L"On device \"" << m_device << L"\" ioctl(DKIOCGVTOC) failed with errno = " <<
                    StrFrom(errno) << L".";
            SCX_LOG(m_log, suppressor.GetSeverity(out.str()), out.str());
        }

        /* Look up drive geometry information */

        struct dk_geom dkgeom;
        memset(&dkgeom, '\0', sizeof(dkgeom));
        if (0 == m_deps->ioctl(int(DKIOCGGEOM), &dkgeom))
        {
            m_totalCylinders = dkgeom.dkg_pcyl;
            m_totalHeads = dkgeom.dkg_nhead;
            //on http://wwwcgi.rdg.ac.uk:8081/cgi-bin/cgiwrap/wsi14/poplog/man/7I/dkio,
            //dkg_nsect means /* # of sectors per track*/
            m_Properties.sectorsPerTrack = dkgeom.dkg_nsect;
        }
        else
        {
            std::wstringstream out;
            out << L"On device \"" << m_device << L"\" ioctl(DKIOCGGEOM) failed with errno = " <<
                    StrFrom(errno) << L".";
            SCX_LOG(m_log, suppressor.GetSeverity(out.str()), out.str());
        }

        GetDiskGeometry(m_sizeInBytes, m_sectorSize, m_totalCylinders, m_totalHeads, m_Properties.sectorsPerTrack);

        /* Close the handle to the drive */

        if (0 != m_deps->close())
        {
            /* Can't imagine that we can't close the device, but if so, we don't want to miss that */

            throw SCXErrnoException(L"close", errno, SCXSRCLOCATION);
        }

#else
#error Implementation for Update() method not provided for platform
#endif
    }