]> xenbits.xensource.com Git - libvirt.git/commitdiff
util: Improve virStrncpy() implementation
authorAndrea Bolognani <abologna@redhat.com>
Fri, 20 Jul 2018 11:00:44 +0000 (13:00 +0200)
committerAndrea Bolognani <abologna@redhat.com>
Mon, 23 Jul 2018 12:27:37 +0000 (14:27 +0200)
We finally get rid of the strncpy()-like semantics
and implement our own, more sensible ones instead.

As a bonus, this also fixes compilation on MinGW.

Signed-off-by: Andrea Bolognani <abologna@redhat.com>
docs/hacking.html.in
src/util/virstring.c

index 6c1a5121a4e119f0711119277b0dd69c6e15b61a..f99d143b7b828da55408005ddcc6fd9d96b1934b 100644 (file)
     <p>
       Do not use the strncpy function.  According to the man page, it
       does <b>not</b> guarantee a NULL-terminated buffer, which makes
-      it extremely dangerous to use.  Instead, use one of the
-      functionally equivalent functions:
+      it extremely dangerous to use.  Instead, use one of the replacement
+      functions provided by libvirt:
     </p>
 
 <pre>
   virStrncpy(char *dest, const char *src, size_t n, size_t destbytes)
 </pre>
     <p>
-      The first three arguments have the same meaning as for strncpy;
-      namely the destination, source, and number of bytes to copy,
-      respectively.  The last argument is the number of bytes
-      available in the destination string; if a copy of the source
-      string (including a \0) will not fit into the destination, no
-      bytes are copied and the routine returns &lt;0.  Otherwise, n
-      bytes from the source are copied into the destination and a
-      trailing \0 is appended.
+      The first two arguments have the same meaning as for strncpy,
+      namely the destination and source of the copy operation.  Unlike
+      strncpy, the function will always copy exactly the number of bytes
+      requested and make sure the destination is NULL-terminated, as the
+      source is required to be; sanity checks are performed to ensure the
+      size of the destination, as specified by the last argument, is
+      sufficient for the operation to succeed.  On success, 0 is returned;
+      on failure, a value &lt;0 is returned instead.
     </p>
 
 <pre>
 </pre>
     <p>
       Use this variant if you know you want to copy the entire src
-      string into dest.  Note that this is a macro, so arguments could
-      be evaluated more than once.  This is equivalent to
-      virStrncpy(dest, src, strlen(src), destbytes)
-      </p>
+      string into dest.
+    </p>
 
 <pre>
   virStrcpyStatic(char *dest, const char *src)
       string into dest <b>and</b> you know that your destination string is
       a static string (i.e. that sizeof(dest) returns something
       meaningful).  Note that this is a macro, so arguments could be
-      evaluated more than once.  This is equivalent to
-      virStrncpy(dest, src, strlen(src), sizeof(dest)).
+      evaluated more than once.
     </p>
 
 <pre>
index 3e2f85465f7dffc89ef10f749cf863d28777600d..93fda69d7f286133d08bac2e5862e23b70ee5166 100644 (file)
@@ -769,44 +769,66 @@ virAsprintfInternal(bool report,
 }
 
 /**
- * virStrncpy
+ * virStrncpy:
  *
- * A safe version of strncpy.  The last parameter is the number of bytes
- * available in the destination string, *not* the number of bytes you want
- * to copy.  If the destination is not large enough to hold all n of the
- * src string bytes plus a \0, <0 is returned and no data is copied.
- * If the destination is large enough to hold the n bytes plus \0, then the
- * string is copied and 0 is returned.
+ * @dest: destination buffer
+ * @src: source buffer
+ * @n: number of bytes to copy
+ * @destbytes: number of bytes the destination can accomodate
+ *
+ * Copies the first @n bytes of @src to @dest.
+ *
+ * @src must be NULL-terminated; if successful, @dest is guaranteed to
+ * be NULL-terminated as well.
+ *
+ * @n must be a reasonable value, that is, it must not exceed either
+ * the length of @src or the size of @dest. For the latter constraint,
+ * the fact that @dest needs to accomodate a NULL byte in addition to
+ * the bytes copied from @src must be taken into account.
+ *
+ * If you want to copy *all* of @src to @dest, use virStrcpy() or
+ * virStrcpyStatic() instead.
+ *
+ * Returns: 0 on success, <0 on failure.
  */
 int
 virStrncpy(char *dest, const char *src, size_t n, size_t destbytes)
 {
-    if (n > (destbytes - 1))
+    size_t src_len = strlen(src);
+
+    /* As a special case, -1 means "copy the entire string".
+     *
+     * This is to avoid calling strlen() twice, once in the virStrcpy()
+     * wrapper and once here for bound checking purposes. */
+    if (n == -1)
+        n = src_len;
+
+    if (n <= 0 || n > src_len || n > (destbytes - 1))
         return -1;
 
-    strncpy(dest, src, n);
-    /* strncpy NULL terminates iff the last character is \0.  Therefore
-     * force the last byte to be \0
-     */
+    memcpy(dest, src, n);
     dest[n] = '\0';
 
     return 0;
 }
 
 /**
- * virStrcpy
+ * virStrcpy:
+ *
+ * @dest: destination buffer
+ * @src: source buffer
+ * @destbytes: number of bytes the destination can accomodate
+ *
+ * Copies @src to @dest.
+ *
+ * See virStrncpy() for more information.
  *
- * A safe version of strcpy.  The last parameter is the number of bytes
- * available in the destination string, *not* the number of bytes you want
- * to copy.  If the destination is not large enough to hold all n of the
- * src string bytes plus a \0, <0 is returned and no data is copied.
- * If the destination is large enough to hold the source plus \0, then the
- * string is copied and 0 is returned.
+ * Returns: 0 on success, <0 on failure.
  */
 int
 virStrcpy(char *dest, const char *src, size_t destbytes)
 {
-    return virStrncpy(dest, src, strlen(src), destbytes);
+    return virStrncpy(dest, src, -1, destbytes);
 }
 
 /**