Update TODO.
[libguestfs.git] / daemon / checksum.c
1 /* libguestfs - the guestfsd daemon
2  * Copyright (C) 2009-2011 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 "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     close (fd);
66     return NULL;
67   }
68
69   pulse_mode_start ();
70
71   flags = COMMAND_FLAG_CHROOT_COPY_FILE_TO_STDIN | fd;
72   r = commandf (&out, &err, flags, program, NULL);
73   if (r == -1) {
74     pulse_mode_cancel ();
75     reply_with_error ("%s: %s", program, err);
76     free (out);
77     free (err);
78     return NULL;
79   }
80
81   free (err);
82
83   /* Split it at the first whitespace. */
84   len = strcspn (out, " \t\n");
85   out[len] = '\0';
86
87   pulse_mode_end ();
88
89   return out;                   /* Caller frees. */
90 }
91
92 char *
93 do_checksum (const char *csumtype, const char *path)
94 {
95   int fd;
96
97   CHROOT_IN;
98   fd = open (path, O_RDONLY);
99   CHROOT_OUT;
100
101   if (fd == -1) {
102     reply_with_perror ("%s", path);
103     return NULL;
104   }
105
106   return checksum (csumtype, fd);
107 }
108
109 char *
110 do_checksum_device (const char *csumtype, const char *device)
111 {
112   int fd;
113
114   fd = open (device, O_RDONLY);
115   if (fd == -1) {
116     reply_with_perror ("%s", device);
117     return NULL;
118   }
119
120   return checksum (csumtype, fd);
121 }
122
123 /* Has one FileOut parameter. */
124 int
125 do_checksums_out (const char *csumtype, const char *dir)
126 {
127   struct stat statbuf;
128   int r;
129
130   const char *program = program_of_csum (csumtype);
131   if (program == NULL)
132     return -1;
133
134   char *sysrootdir = sysroot_path (dir);
135   if (!sysrootdir) {
136     reply_with_perror ("malloc");
137     return -1;
138   }
139
140   r = stat (sysrootdir, &statbuf);
141   if (r == -1) {
142     reply_with_perror ("%s", dir);
143     free (sysrootdir);
144     return -1;
145   }
146   if (!S_ISDIR (statbuf.st_mode)) {
147     reply_with_error ("%s: not a directory", dir);
148     free (sysrootdir);
149     return -1;
150   }
151
152   char *cmd;
153   if (asprintf_nowarn (&cmd, "cd %Q && find -type f -print0 | xargs -0 %s",
154                        sysrootdir, program) == -1) {
155     reply_with_perror ("asprintf");
156     free (sysrootdir);
157     return -1;
158   }
159   free (sysrootdir);
160
161   if (verbose)
162     fprintf (stderr, "%s\n", cmd);
163
164   FILE *fp = popen (cmd, "r");
165   if (fp == NULL) {
166     reply_with_perror ("%s", cmd);
167     free (cmd);
168     return -1;
169   }
170   free (cmd);
171
172   /* Now we must send the reply message, before the file contents.  After
173    * this there is no opportunity in the protocol to send any error
174    * message back.  Instead we can only cancel the transfer.
175    */
176   reply (NULL, NULL);
177
178   char str[GUESTFS_MAX_CHUNK_SIZE];
179
180   while ((r = fread (str, 1, GUESTFS_MAX_CHUNK_SIZE, fp)) > 0) {
181     if (send_file_write (str, r) < 0) {
182       pclose (fp);
183       return -1;
184     }
185   }
186
187   if (ferror (fp)) {
188     perror (dir);
189     send_file_end (1);                /* Cancel. */
190     pclose (fp);
191     return -1;
192   }
193
194   if (pclose (fp) != 0) {
195     perror (dir);
196     send_file_end (1);                /* Cancel. */
197     return -1;
198   }
199
200   if (send_file_end (0))        /* Normal end of file. */
201     return -1;
202
203   return 0;
204 }