pwrite: Check offset is not negative.
[libguestfs.git] / daemon / checksum.c
1 /* libguestfs - the guestfsd daemon
2  * Copyright (C) 2009 Red Hat Inc.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 #include <config.h>
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <fcntl.h>
26 #include <sys/stat.h>
27
28 #include "../src/guestfs_protocol.h"
29 #include "daemon.h"
30 #include "actions.h"
31
32 static const char *
33 program_of_csum (const char *csumtype)
34 {
35   if (STRCASEEQ (csumtype, "crc"))
36     return "cksum";
37   else if (STRCASEEQ (csumtype, "md5"))
38     return "md5sum";
39   else if (STRCASEEQ (csumtype, "sha1"))
40     return "sha1sum";
41   else if (STRCASEEQ (csumtype, "sha224"))
42     return "sha224sum";
43   else if (STRCASEEQ (csumtype, "sha256"))
44     return "sha256sum";
45   else if (STRCASEEQ (csumtype, "sha384"))
46     return "sha384sum";
47   else if (STRCASEEQ (csumtype, "sha512"))
48     return "sha512sum";
49   else {
50     reply_with_error ("unknown checksum type, expecting crc|md5|sha1|sha224|sha256|sha384|sha512");
51     return NULL;
52   }
53 }
54
55 static char *
56 checksum (const char *csumtype, int fd)
57 {
58   const char *program;
59   char *out, *err;
60   int flags, r;
61   int len;
62
63   program = program_of_csum (csumtype);
64   if (program == NULL)
65     return NULL;
66
67   flags = COMMAND_FLAG_CHROOT_COPY_FILE_TO_STDIN | fd;
68   r = commandf (&out, &err, flags, program, NULL);
69   if (r == -1) {
70     reply_with_error ("%s: %s", program, err);
71     free (out);
72     free (err);
73     return NULL;
74   }
75
76   free (err);
77
78   /* Split it at the first whitespace. */
79   len = strcspn (out, " \t\n");
80   out[len] = '\0';
81
82   return out;                   /* Caller frees. */
83 }
84
85 char *
86 do_checksum (const char *csumtype, const char *path)
87 {
88   int fd;
89
90   CHROOT_IN;
91   fd = open (path, O_RDONLY);
92   CHROOT_OUT;
93
94   if (fd == -1) {
95     reply_with_perror ("%s", path);
96     return NULL;
97   }
98
99   return checksum (csumtype, fd);
100 }
101
102 char *
103 do_checksum_device (const char *csumtype, const char *device)
104 {
105   int fd;
106
107   fd = open (device, O_RDONLY);
108   if (fd == -1) {
109     reply_with_perror ("%s", device);
110     return NULL;
111   }
112
113   return checksum (csumtype, fd);
114 }
115
116 /* Has one FileOut parameter. */
117 int
118 do_checksums_out (const char *csumtype, const char *dir)
119 {
120   struct stat statbuf;
121   int r;
122
123   const char *program = program_of_csum (csumtype);
124   if (program == NULL)
125     return -1;
126
127   char *sysrootdir = sysroot_path (dir);
128   if (!sysrootdir) {
129     reply_with_perror ("malloc");
130     return -1;
131   }
132
133   r = stat (sysrootdir, &statbuf);
134   if (r == -1) {
135     reply_with_perror ("%s", dir);
136     free (sysrootdir);
137     return -1;
138   }
139   if (!S_ISDIR (statbuf.st_mode)) {
140     reply_with_error ("%s: not a directory", dir);
141     free (sysrootdir);
142     return -1;
143   }
144
145   char *cmd;
146   if (asprintf_nowarn (&cmd, "cd %Q && find -type f -print0 | xargs -0 %s",
147                        sysrootdir, program) == -1) {
148     reply_with_perror ("asprintf");
149     free (sysrootdir);
150     return -1;
151   }
152   free (sysrootdir);
153
154   if (verbose)
155     fprintf (stderr, "%s\n", cmd);
156
157   FILE *fp = popen (cmd, "r");
158   if (fp == NULL) {
159     reply_with_perror ("%s", cmd);
160     free (cmd);
161     return -1;
162   }
163   free (cmd);
164
165   /* Now we must send the reply message, before the file contents.  After
166    * this there is no opportunity in the protocol to send any error
167    * message back.  Instead we can only cancel the transfer.
168    */
169   reply (NULL, NULL);
170
171   char str[GUESTFS_MAX_CHUNK_SIZE];
172
173   while ((r = fread (str, 1, GUESTFS_MAX_CHUNK_SIZE, fp)) > 0) {
174     if (send_file_write (str, r) < 0) {
175       pclose (fp);
176       return -1;
177     }
178   }
179
180   if (ferror (fp)) {
181     perror (dir);
182     send_file_end (1);                /* Cancel. */
183     pclose (fp);
184     return -1;
185   }
186
187   if (pclose (fp) != 0) {
188     perror (dir);
189     send_file_end (1);                /* Cancel. */
190     return -1;
191   }
192
193   if (send_file_end (0))        /* Normal end of file. */
194     return -1;
195
196   return 0;
197 }