lib: Make fd_load work for non-regular files
authorVolker Lendecke <vl@samba.org>
Thu, 21 Feb 2019 17:37:08 +0000 (18:37 +0100)
committerAndrew Bartlett <abartlet@samba.org>
Tue, 26 Mar 2019 04:43:40 +0000 (04:43 +0000)
Follow-up to

https://lists.samba.org/archive/samba/2018-September/217992.html

and following. This also fixes a small and very theoretical race: Between the
fstat and the read call the file size might change. This would make us fail on
potentially legitimate files.

This is more complex and probably slower, but looking at the use cases I don't
think the speed matters.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=13859

Signed-off-by: Volker Lendecke <vl@samba.org>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
Autobuild-User(master): Andrew Bartlett <abartlet@samba.org>
Autobuild-Date(master): Tue Mar 26 04:43:40 UTC 2019 on sn-devel-144

lib/util/util_file.c

index 90d39f7cdd356087d76382b98bf3b92917060714..7a8644e3f5d8ad8ab1683e72a3561bbebb1f253b 100644 (file)
@@ -170,30 +170,63 @@ load a file into memory from a fd.
 **/
 _PUBLIC_ char *fd_load(int fd, size_t *psize, size_t maxsize, TALLOC_CTX *mem_ctx)
 {
-       struct stat sbuf;
-       char *p;
-       size_t size;
+       FILE *file;
+       char *p = NULL;
+       size_t size = 0;
+       size_t chunk = 1024;
+       int err;
+
+       if (maxsize == 0) {
+               maxsize = SIZE_MAX;
+       }
 
-       if (fstat(fd, &sbuf) != 0) return NULL;
+       file = fdopen(fd, "r");
+       if (file == NULL) {
+               return NULL;
+       }
 
-       size = sbuf.st_size;
+       while (size < maxsize) {
+               size_t newbufsize;
+               size_t nread;
 
-       if (maxsize) {
-               size = MIN(size, maxsize);
-       }
+               chunk = MIN(chunk, (maxsize - size));
 
-       p = (char *)talloc_size(mem_ctx, size+1);
-       if (!p) return NULL;
+               newbufsize = size + (chunk+1); /* chunk+1 can't overflow */
+               if (newbufsize < size) {
+                       goto fail; /* overflow */
+               }
 
-       if (read(fd, p, size) != size) {
-               talloc_free(p);
-               return NULL;
+               p = talloc_realloc(mem_ctx, p, char, newbufsize);
+               if (p == NULL) {
+                       goto fail;
+               }
+
+               nread = fread(p+size, 1, chunk, file);
+               size += nread;
+
+               if (nread != chunk) {
+                       break;
+               }
        }
-       p[size] = 0;
 
-       if (psize) *psize = size;
+       err = ferror(file);
+       if (err != 0) {
+               goto fail;
+       }
 
+       p[size] = '\0';
+
+       if (psize != NULL) {
+               *psize = size;
+       }
+
+       fclose(file);
        return p;
+
+fail:
+       TALLOC_FREE(p);
+       fclose(file);
+       return NULL;
 }
 
 /**