New API: checksums-out for checksumming many files.
[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 <sys/stat.h>
26
27 #include "../src/guestfs_protocol.h"
28 #include "daemon.h"
29 #include "actions.h"
30
31 static const char *
32 program_of_csum (const char *csumtype)
33 {
34   if (STRCASEEQ (csumtype, "crc"))
35     return "cksum";
36   else if (STRCASEEQ (csumtype, "md5"))
37     return "md5sum";
38   else if (STRCASEEQ (csumtype, "sha1"))
39     return "sha1sum";
40   else if (STRCASEEQ (csumtype, "sha224"))
41     return "sha224sum";
42   else if (STRCASEEQ (csumtype, "sha256"))
43     return "sha256sum";
44   else if (STRCASEEQ (csumtype, "sha384"))
45     return "sha384sum";
46   else if (STRCASEEQ (csumtype, "sha512"))
47     return "sha512sum";
48   else {
49     reply_with_error ("unknown checksum type, expecting crc|md5|sha1|sha224|sha256|sha384|sha512");
50     return NULL;
51   }
52 }
53
54 static char *
55 checksum (const char *csumtype, const char *path)
56 {
57   const char *program;
58   char *out, *err;
59   int r;
60   int len;
61
62   program = program_of_csum (csumtype);
63   if (program == NULL)
64     return NULL;
65
66   r = command (&out, &err, program, path, NULL);
67   if (r == -1) {
68     reply_with_error ("%s: %s", program, err);
69     free (out);
70     free (err);
71     return NULL;
72   }
73
74   free (err);
75
76   /* Split it at the first whitespace. */
77   len = strcspn (out, " \t\n");
78   out[len] = '\0';
79
80   return out;                   /* Caller frees. */
81 }
82
83 char *
84 do_checksum (const char *csumtype, const char *path)
85 {
86   /* Make the path relative to /sysroot. */
87   char *buf = sysroot_path (path);
88   if (!buf) {
89     reply_with_perror ("malloc");
90     return NULL;
91   }
92
93   char *r = checksum (csumtype, buf);
94   free (buf);
95   return r;
96 }
97
98 char *
99 do_checksum_device (const char *csumtype, const char *device)
100 {
101   return checksum (csumtype, device);
102 }
103
104 /* Has one FileOut parameter. */
105 int
106 do_checksums_out (const char *csumtype, const char *dir)
107 {
108   struct stat statbuf;
109   int r;
110
111   const char *program = program_of_csum (csumtype);
112   if (program == NULL)
113     return -1;
114
115   char *sysrootdir = sysroot_path (dir);
116   if (!sysrootdir) {
117     reply_with_perror ("malloc");
118     return -1;
119   }
120
121   r = stat (sysrootdir, &statbuf);
122   if (r == -1) {
123     reply_with_perror ("%s", dir);
124     free (sysrootdir);
125     return -1;
126   }
127   if (!S_ISDIR (statbuf.st_mode)) {
128     reply_with_error ("%s: not a directory", dir);
129     free (sysrootdir);
130     return -1;
131   }
132
133   char *cmd;
134   if (asprintf_nowarn (&cmd, "cd %Q && find -type f -print0 | xargs -0 %s",
135                        sysrootdir, program) == -1) {
136     reply_with_perror ("asprintf");
137     free (sysrootdir);
138     return -1;
139   }
140   free (sysrootdir);
141
142   if (verbose)
143     fprintf (stderr, "%s\n", cmd);
144
145   FILE *fp = popen (cmd, "r");
146   if (fp == NULL) {
147     reply_with_perror ("%s", cmd);
148     free (cmd);
149     return -1;
150   }
151   free (cmd);
152
153   /* Now we must send the reply message, before the file contents.  After
154    * this there is no opportunity in the protocol to send any error
155    * message back.  Instead we can only cancel the transfer.
156    */
157   reply (NULL, NULL);
158
159   char str[GUESTFS_MAX_CHUNK_SIZE];
160
161   while ((r = fread (str, 1, GUESTFS_MAX_CHUNK_SIZE, fp)) > 0) {
162     if (send_file_write (str, r) < 0) {
163       pclose (fp);
164       return -1;
165     }
166   }
167
168   if (ferror (fp)) {
169     perror (dir);
170     send_file_end (1);                /* Cancel. */
171     pclose (fp);
172     return -1;
173   }
174
175   if (pclose (fp) != 0) {
176     perror (dir);
177     send_file_end (1);                /* Cancel. */
178     return -1;
179   }
180
181   if (send_file_end (0))        /* Normal end of file. */
182     return -1;
183
184   return 0;
185 }