[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH 2/4] added missing file commands (attributes, deleti
From: |
itamar . tal4 |
Subject: |
[Qemu-devel] [PATCH 2/4] added missing file commands (attributes, deletion) |
Date: |
Tue, 31 Mar 2015 11:34:35 +0300 |
From: Itamar Tal <address@hidden>
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 <address@hidden>
* Michal Privoznik <address@hidden>
*
+ * Changes (address@hidden):
+ * - 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 <address@hidden>
* Gal Hammer <address@hidden>
*
+ * Changes (address@hidden):
+ * - 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