SVERROR HideDrive()

in host/common/unix/portablehelpersmajor.cpp [1180:1431]


SVERROR HideDrive(const char * drive, char const* mountPoint, std::string& output, std::string& error, bool checkformultipaths )
{
    SVERROR sve = SVS_OK;
    if( NULL == drive )
    {
        sve = EINVAL;
        DebugPrintf( SV_LOG_ERROR, "@ LINE %d in FILE %s \n", __LINE__, __FILE__ );
        DebugPrintf( SV_LOG_ERROR, "FAILED HideDrive()... err = EINVAL\n");
        error = "HideDrive(): The drive name is empty";
        return sve;
    }


    /**************************************************
    * This function has been made common to linux and *
    * and solaris, by means of following:             *
    * 1. The command names have been defined as macros*
    *    in implementation specific header files of   *
    *    sun and linux.                               *
    * 2. Added a function "GetCommandPath" that       *
    *    actually returns the specific path where     *
    *    the commands like fuser, umount are there    *
    **************************************************/ 

    /** 
    *
    * TODO: For solaris, since lazy unmount of 
    * device is not supported, but since there 
    * is support for forcible umount with -f option,
    * use of fuser can be eliminated.
    *
    */

    std::vector<std::string> mountPoints;

    std::string devicename=drive;
    if(!GetDeviceNameFromSymLink(devicename))
    {
        DebugPrintf(SV_LOG_ERROR, 
            "Function %s @LINE %d in FILE %s :failed GetDeviceNameFromSymLink for %s \n", 
            FUNCTION_NAME, LINE_NO, FILE_NAME, drive);
        error = "HideDrive(): Unable to obtain DeviceName from SymLink ";
        error += devicename;
        return SVE_FAIL;
    }

    const char * dev = devicename.c_str();
    /* This handles the GetDeviceNameFromSymLink function call by itself
    * since it compares device with actual name as well as link name */
    if(!GetMountPoints(dev,mountPoints))
    {
        DebugPrintf(SV_LOG_ERROR, 
            "Function %s @LINE %d in FILE %s :failed GetMountPoints for %s \n", 
            FUNCTION_NAME, LINE_NO, FILE_NAME, drive);
        error = "HideDrive(): Unable to retrieve mountpoints associated with ";
        error += dev;
        return SVE_FAIL;
    }
    /*
    * In case of vsnaps,we should not call CanUnmountMountPointsIncludingMultipath'
    */
    if(checkformultipaths)
    {
        if(!CanUnmountMountPointsIncludingMultipath(drive,mountPoints,error))
        {
            DebugPrintf(SV_LOG_DEBUG,
                "Function %s @LINE %d in FILE %s :failed CanUnmountMountPointsIncludingMultipath for %s \n",
                FUNCTION_NAME, LINE_NO, FILE_NAME, drive);
            return SVE_FAIL;
        }
    }

    if(mountPoints.empty())
    {
        DebugPrintf(SV_LOG_INFO, "Mountpoints corresponding to %s are already removed.\n", drive);
        DeleteFSEntry(drive);
        return SVS_OK;
    }

	LocalConfigurator localConfigurator;
	SV_UINT max_unmount_reties = localConfigurator.getMaxUnmountRetries();
    std::vector<std::string>::iterator mnt_iter = mountPoints.begin();
    std::vector<std::string>::iterator mnt_end = mountPoints.end();
    for ( ; mnt_iter != mnt_end ; ++mnt_iter)
    {
		std::string errmsg;
		if(!IsValidMountPoint((*mnt_iter),errmsg))
		{
			sve = SVE_FAIL;
			std::ostringstream ostr;
			ostr << "Cannot Hide " << drive << ". " << errmsg <<std::endl;
			error = ostr.str().c_str();
			return sve;
		}
		//Bug #7348 - mountpoint with spaces
		std::string mountpoint = "\"";
		mountpoint += (*mnt_iter);
		mountpoint += "\"";
		const char * mntpt = mountpoint.c_str();
		//changes for Bug 4147
		//checking for PWD is same as hide drive mount point directory or not
		//CHK_DIR: is added in the error string to identify the below condition failed
		//this is added to identify not a failed case in the vsnap unmount
		char cwd_path[MAXPATHLEN];
		if (ACE_OS::getcwd (cwd_path, sizeof (cwd_path)) == 0)
		{
			if(ENOENT != ACE_OS::last_error())
			{
				std::stringstream errmsgcwd;
				errmsgcwd << "Failed to get the current directory.\n";
				errmsgcwd <<"errno = " << ACE_OS::last_error() << "\nerror = " << ACE_OS::strerror(ACE_OS::last_error());
				errmsgcwd <<" .\n";
				error = "CHK_DIR: " + errmsgcwd.str();
				DebugPrintf(SV_LOG_INFO, "%s\n",errmsgcwd.str().c_str());
				return SVE_FAIL;
			}
		}
		else
		{
			std::string curdir = cwd_path;
			std::string mountdir = (*mnt_iter);
			if(0 == curdir.find(mountdir, 0))
			{
				std::string errmsg;
				errmsg = "The mount directory is ";
				errmsg += mountdir;
				errmsg += ".\nThe present working directory is ";
				errmsg += curdir;
				errmsg += ".\nThe present working directory is inside the mount directory.\n";
				error = "CHK_DIR: " + errmsg;
				DebugPrintf(SV_LOG_INFO, "%s\n",errmsg.c_str());
				return SVE_FAIL;
			}
		}
		DebugPrintf(SV_LOG_INFO, "\n\nUnMounting %s ...\n", mntpt);

		SV_UINT retrycount = 0; 
		int retval = 0;
		do
		{
			retval = TerminateUsersAndUnmountMountPoint(mntpt,error);
			if(retval == -1 || (retval == 1 && retrycount == max_unmount_reties))
			{
				return SVE_FAIL;
			}
			else if(retval == 1)
			{
				DebugPrintf(SV_LOG_INFO, "Attempting to retry unmount operation on %s.\n", mntpt);
				retrycount++;
			}
		}while(retval);
        //        VsnapDeleteMntPoint(mntpt) ;
    } // finished removing the mount points

    if(mnt_iter != mnt_end)
    {
        DebugPrintf(SV_LOG_ERROR, "Removal of Mountpoints corresponding to %s failed.\n", drive);
        error = "HideDrive(): Removal of Mountpoints corresponding to ";
        error += drive;
        error += " failed";
        //if(fd != -1) close(fd);
        return SVE_FAIL;
    }

    // fuser -m returns 1 if nobody accessess the specified block device
    // It returns 0 if atleast one process accesses the block device

    DebugPrintf(SV_LOG_INFO, "\nTrying to find processes accessing %s ...\n", drive);

    std::string command = SV_FUSER_FORDEV;

    InmCommand fuserdevice(command + dev);

    std::string pathvar = "PATH=";
    pathvar += GetCommandPath(command);
    fuserdevice.PutEnv(pathvar);

    InmCommand::statusType status = fuserdevice.Run();

    if (status != InmCommand::completed) {
        DebugPrintf(SV_LOG_ERROR, 
            "Function %s @LINE %d in FILE %s :failed to find processes accessing %s\n",
            FUNCTION_NAME, LINE_NO, FILE_NAME,drive);
        error = "HideDrive(): Failed to find processes accessing ";
        error += drive;
        if(!fuserdevice.StdErr().empty())
        {
            error += fuserdevice.StdErr();
        }
        //if(fd != -1) close(fd);
        return SVE_FAIL;
    }

    if(!fuserdevice.ExitCode()) //Device is busy, exitcode returned is 0
    {
        DebugPrintf(SV_LOG_INFO, "\nTrying to shutdown processes accessing %s ...\n", drive);
        command = SV_FUSER_DEV_KILL;

        InmCommand fuserdevicekill(command + dev);
        pathvar = "PATH=";
        pathvar += GetCommandPath(command);
        fuserdevicekill.PutEnv(pathvar);

        status = fuserdevicekill.Run();

        if (status != InmCommand::completed) {
            DebugPrintf(SV_LOG_ERROR, 
                "Function %s @LINE %d in FILE %s :failed to shutdown processes accessing %s\n",
                FUNCTION_NAME, LINE_NO, FILE_NAME,drive);
            error = "HideDrive(): Failed to shutdown processes accessing ";
            error += drive;
            if(!fuserdevicekill.StdErr().empty())
            {
                error += fuserdevicekill.StdErr();
            }
            //if(fd != -1) close(fd);
            return SVE_FAIL;
        }

        if (fuserdevicekill.ExitCode()) {
            DebugPrintf(SV_LOG_ERROR, 
                "Function %s @LINE %d in FILE %s :failed to shutdown processes accessing %s\n",
                FUNCTION_NAME, LINE_NO, FILE_NAME,drive);
            error = "HideDrive(): Failed to shutdown processes accessing ";
            error += drive;
            std::ostringstream msg;
            msg << "Exit Code = " << fuserdevicekill.ExitCode() << std::endl;
            if(!fuserdevicekill.StdOut().empty())
            {
                msg << "Output = " << fuserdevicekill.StdOut() << std::endl;
            }
            if(!fuserdevicekill.StdErr().empty())
            {
                msg << "Error = " << fuserdevicekill.StdErr() << std::endl;
            }
            error += msg.str();
            //if(fd != -1) close(fd);
            return SVE_FAIL;
        }

    }

    DebugPrintf(SV_LOG_INFO, "Removing entries for the device %s from %s ...\n", drive, SV_FSTAB);
    if(!DeleteFSEntry(drive))
    {
        DebugPrintf(SV_LOG_INFO, "Note: %s was not updated. please update it manually.\n", SV_FSTAB);
    }

    DebugPrintf(SV_LOG_INFO, "Removal of Mountpoints corresponding to %s succeeded\n", drive);

    return sve;
}