util: Improve virStrncpy() implementation

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>
This commit is contained in:
Andrea Bolognani 2018-07-20 13:00:44 +02:00
parent 19136bbf10
commit 7d70a63b94
2 changed files with 55 additions and 36 deletions

View File

@ -1121,22 +1121,22 @@
<p> <p>
Do not use the strncpy function. According to the man page, it Do not use the strncpy function. According to the man page, it
does <b>not</b> guarantee a NULL-terminated buffer, which makes does <b>not</b> guarantee a NULL-terminated buffer, which makes
it extremely dangerous to use. Instead, use one of the it extremely dangerous to use. Instead, use one of the replacement
functionally equivalent functions: functions provided by libvirt:
</p> </p>
<pre> <pre>
virStrncpy(char *dest, const char *src, size_t n, size_t destbytes) virStrncpy(char *dest, const char *src, size_t n, size_t destbytes)
</pre> </pre>
<p> <p>
The first three arguments have the same meaning as for strncpy; The first two arguments have the same meaning as for strncpy,
namely the destination, source, and number of bytes to copy, namely the destination and source of the copy operation. Unlike
respectively. The last argument is the number of bytes strncpy, the function will always copy exactly the number of bytes
available in the destination string; if a copy of the source requested and make sure the destination is NULL-terminated, as the
string (including a \0) will not fit into the destination, no source is required to be; sanity checks are performed to ensure the
bytes are copied and the routine returns &lt;0. Otherwise, n size of the destination, as specified by the last argument, is
bytes from the source are copied into the destination and a sufficient for the operation to succeed. On success, 0 is returned;
trailing \0 is appended. on failure, a value &lt;0 is returned instead.
</p> </p>
<pre> <pre>
@ -1144,9 +1144,7 @@
</pre> </pre>
<p> <p>
Use this variant if you know you want to copy the entire src 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 string into dest.
be evaluated more than once. This is equivalent to
virStrncpy(dest, src, strlen(src), destbytes)
</p> </p>
<pre> <pre>
@ -1157,8 +1155,7 @@
string into dest <b>and</b> you know that your destination string is string into dest <b>and</b> you know that your destination string is
a static string (i.e. that sizeof(dest) returns something a static string (i.e. that sizeof(dest) returns something
meaningful). Note that this is a macro, so arguments could be meaningful). Note that this is a macro, so arguments could be
evaluated more than once. This is equivalent to evaluated more than once.
virStrncpy(dest, src, strlen(src), sizeof(dest)).
</p> </p>
<pre> <pre>

View File

@ -769,44 +769,66 @@ virAsprintfInternal(bool report,
} }
/** /**
* virStrncpy * virStrncpy:
* *
* A safe version of strncpy. The last parameter is the number of bytes * @dest: destination buffer
* available in the destination string, *not* the number of bytes you want * @src: source buffer
* to copy. If the destination is not large enough to hold all n of the * @n: number of bytes to copy
* src string bytes plus a \0, <0 is returned and no data is copied. * @destbytes: number of bytes the destination can accomodate
* If the destination is large enough to hold the n bytes plus \0, then the *
* string is copied and 0 is returned. * 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 int
virStrncpy(char *dest, const char *src, size_t n, size_t destbytes) 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; return -1;
strncpy(dest, src, n); memcpy(dest, src, n);
/* strncpy NULL terminates iff the last character is \0. Therefore
* force the last byte to be \0
*/
dest[n] = '\0'; dest[n] = '\0';
return 0; return 0;
} }
/** /**
* virStrcpy * virStrcpy:
* *
* A safe version of strcpy. The last parameter is the number of bytes * @dest: destination buffer
* available in the destination string, *not* the number of bytes you want * @src: source buffer
* to copy. If the destination is not large enough to hold all n of the * @destbytes: number of bytes the destination can accomodate
* 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 * Copies @src to @dest.
* string is copied and 0 is returned. *
* See virStrncpy() for more information.
*
* Returns: 0 on success, <0 on failure.
*/ */
int int
virStrcpy(char *dest, const char *src, size_t destbytes) virStrcpy(char *dest, const char *src, size_t destbytes)
{ {
return virStrncpy(dest, src, strlen(src), destbytes); return virStrncpy(dest, src, -1, destbytes);
} }
/** /**