From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([2001:4830:134:3::10]:54295) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1YcrdQ-0000aY-Gs for qemu-devel@nongnu.org; Tue, 31 Mar 2015 04:34:58 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1YcrdO-0004FG-MZ for qemu-devel@nongnu.org; Tue, 31 Mar 2015 04:34:56 -0400 Received: from mail-wi0-x230.google.com ([2a00:1450:400c:c05::230]:33732) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1YcrdO-0004F9-Bu for qemu-devel@nongnu.org; Tue, 31 Mar 2015 04:34:54 -0400 Received: by wixm2 with SMTP id m2so9018796wix.0 for ; Tue, 31 Mar 2015 01:34:53 -0700 (PDT) From: itamar.tal4@gmail.com Date: Tue, 31 Mar 2015 11:34:35 +0300 Message-Id: <1427790877-14544-3-git-send-email-itamar@guardicore.com> In-Reply-To: <1427790877-14544-1-git-send-email-itamar@guardicore.com> References: <1427790877-14544-1-git-send-email-itamar@guardicore.com> Subject: [Qemu-devel] [PATCH 2/4] added missing file commands (attributes, deletion) List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org Cc: ori@guardicore.com, ariel@guardicore.com, mdroth@linux.vnet.ibm.com, pavel@guardicore.com, Itamar Tal From: Itamar Tal This patch add support for retrieving file attributes and file deletion by file name, to get a more complete file functionality over the guest agent. --- qga/commands-posix.c | 23 +++++++++ qga/commands-win32.c | 139 +++++++++++++++++++++++++++++++++++++++++++++++++++ qga/commands.c | 8 +++ qga/qapi-schema.json | 42 ++++++++++++++++ 4 files changed, 212 insertions(+) diff --git a/qga/commands-posix.c b/qga/commands-posix.c index ba8de62..028d533 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -7,6 +7,9 @@ * Michael Roth * Michal Privoznik * + * Changes (itamar@guardicore.com): + * - file attributes, removal, hashes + * * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. */ @@ -2456,3 +2459,23 @@ void ga_command_state_init(GAState *s, GACommandState *cs) #endif ga_command_state_add(cs, guest_file_init, NULL); } + +GuestFileAttributes *qmp_guest_file_attributes(const char *path, Error **errp) +{ + GuestFileAttributes *file_stat = g_malloc0(sizeof(GuestFileAttributes)); + struct stat file_os_stat; + + if (stat(path, &file_os_stat)) { + error_setg(errp, "Failed to get file attributes for '%s' (error %d)", + path, errno); + return NULL; + } + + file_stat->mode = file_os_stat.st_mode; + file_stat->size = file_os_stat.st_size; + file_stat->access_time = file_os_stat.st_atime; + file_stat->modification_time = file_os_stat.st_mtime; + file_stat->creation_time = 0; /* not all Linux FS store file BoD */ + + return file_stat; +} diff --git a/qga/commands-win32.c b/qga/commands-win32.c index 3ef0549..aeb49c5 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -7,6 +7,9 @@ * Michael Roth * Gal Hammer * + * Changes (itamar@guardicore.com): + * - file attributes, removal, hashes + * * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. */ @@ -742,3 +745,139 @@ void ga_command_state_init(GAState *s, GACommandState *cs) } ga_command_state_add(cs, guest_file_init, NULL); } + +/* The CRT of Windows has a number of flaws wrt. its stat() implementation: + - time stamps are restricted to second resolution + - file modification times suffer from forth-and-back conversions between + UTC and local time + Therefore, we implement our own stat, based on the Win32 API directly. +*/ + +/* Seconds between 1.1.1601 and 1.1.1970 */ +static __int64 secs_between_epochs = 11644473600; + +static void +FILE_TIME_to_time_t_nsec(FILETIME *in_ptr, time_t *time_out, int* nsec_out) +{ + /* XXX endianness. Shouldn't matter, as all Windows implementations are + little-endian */ + /* Cannot simply cast and dereference in_ptr, + since it might not be aligned properly */ + __int64 in; + memcpy(&in, in_ptr, sizeof(in)); + *nsec_out = (int)(in % 10000000) * 100; /* FILETIME is in units of 100 + nsec. */ + *time_out = (time_t)((in / 10000000) - secs_between_epochs); +} + +static int +attributes_to_mode(DWORD attr) +{ + int m = 0; + if (0 != (attr & FILE_ATTRIBUTE_DIRECTORY)) { + m |= _S_IFDIR | 0111; /* IFEXEC for user,group,other */ + } else { + m |= _S_IFREG; + } + if (0 != (attr & FILE_ATTRIBUTE_READONLY)) { + m |= 0444; + } else { + m |= 0666; + } + return m; +} + +static int +attribute_data_to_stat(WIN32_FILE_ATTRIBUTE_DATA *info, + GuestFileAttributes *result) +{ + int nsec = 0; + + memset(result, 0, sizeof(*result)); + result->mode = attributes_to_mode(info->dwFileAttributes); + result->size = (((__int64)info->nFileSizeHigh)<<32) + info->nFileSizeLow; + FILE_TIME_to_time_t_nsec(&info->ftCreationTime, + (time_t *)&result->creation_time, &nsec); + FILE_TIME_to_time_t_nsec(&info->ftLastWriteTime, + (time_t *)&result->modification_time, &nsec); + FILE_TIME_to_time_t_nsec(&info->ftLastAccessTime, + (time_t *)&result->access_time, &nsec); + + return 0; +} + +static BOOL +attributes_from_dir(LPCSTR pszFile, LPWIN32_FILE_ATTRIBUTE_DATA pfad) +{ + HANDLE hFindFile; + WIN32_FIND_DATAA FileData; + hFindFile = FindFirstFileA(pszFile, &FileData); + if (hFindFile == INVALID_HANDLE_VALUE) { + return FALSE; + } + (void)FindClose(hFindFile); + pfad->dwFileAttributes = FileData.dwFileAttributes; + pfad->ftCreationTime = FileData.ftCreationTime; + pfad->ftLastAccessTime = FileData.ftLastAccessTime; + pfad->ftLastWriteTime = FileData.ftLastWriteTime; + pfad->nFileSizeHigh = FileData.nFileSizeHigh; + pfad->nFileSizeLow = FileData.nFileSizeLow; + return TRUE; +} + +/* based upon Python implementation of windows file stat() */ +GuestFileAttributes *qmp_guest_file_attributes(const char *path, Error **errp) +{ + WIN32_FILE_ATTRIBUTE_DATA info; + int code; + char *dot = NULL; + GuestFileAttributes *result = NULL; + + if (!GetFileAttributesExA(path, GetFileExInfoStandard, &info)) { + if (GetLastError() != ERROR_SHARING_VIOLATION) { + error_setg(errp, "Failed querying '%s' for attributes (error %d)", + path, (int)GetLastError()); + /* Protocol violation: we explicitly clear errno, instead of + setting it to a POSIX error. Callers should use GetLastError. */ + errno = 0; + return NULL; + } else { + /* Could not get attributes on open file. Fall back to + reading the directory. */ + if (!attributes_from_dir(path, &info)) { + error_setg(errp, "Error querying '%s' attributes (error %d)", + path, (int)GetLastError()); + /* Very strange. This should not fail now */ + errno = 0; + return NULL; + } + } + } + + result = g_malloc(sizeof(GuestFileAttributes)); + if (NULL == result) { + error_setg(errp, "No memory for attributes result for '%s' (error %d)", + path, errno); + return NULL; + } + + code = attribute_data_to_stat(&info, result); + if (code != 0) { + g_free(result); + error_setg(errp, "Error converting attributes result '%s' (code %d)", + path, code); + return NULL; + } + /* Set S_IFEXEC if it is an .exe, .bat, ... */ + dot = strrchr(path, '.'); + if (dot) { + if (stricmp(dot, ".bat") == 0 || + stricmp(dot, ".cmd") == 0 || + stricmp(dot, ".exe") == 0 || + stricmp(dot, ".com") == 0) { + result->mode |= 0111; + } + } + return result; +} + diff --git a/qga/commands.c b/qga/commands.c index f9378f4..64404d6 100644 --- a/qga/commands.c +++ b/qga/commands.c @@ -113,3 +113,11 @@ char *qmp_guest_file_hash(const char *path, Error **errp) return g_strdup((char *)sha256_hexdigest); } + +void qmp_guest_file_remove(const char *path, Error **errp) +{ + if (unlink(path)) { + error_setg(errp, "Error deleting file '%s' (error %d)", path, errno); + errno = 0; + } +} diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index 68b420d..cc670b2 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -904,3 +904,45 @@ { 'command': 'guest-file-hash', 'data': { 'path': 'str' }, 'returns': 'str' } + +## +# @GuestFileAttributes +# +# @mode: file access permissions mode +# @size: file size in bytes +# @access-time: file last access time +# @modification-time: file last modification time +# @creation-time: file creation time (when possible) +# +# Since: 2.4 +## +{ 'type': 'GuestFileAttributes', + 'data': {'mode': 'int', 'size': 'uint64', 'access-time': 'int', + 'modification-time': 'int', 'creation-time': 'int' + }} + +## +# @guest-file-attributes: +# +# Get file attributes for a file in the guest's operating system +# for a given file path +# +# Returns: file attributes structure (GuestFileAttributes type) +# +# Since 2.4 +## +{ 'command': 'guest-file-attributes', + 'data': { 'path': 'str' }, + 'returns': 'GuestFileAttributes'} + +## +# @guest-file-remove: +# +# Remove a file in the guest's operating system for a given file path +# +# Returns: +# +# Since 2.4 +## +{ 'command': 'guest-file-remove', + 'data': { 'path': 'str' }} -- 2.3.4