do the output buffering ourselves; this improves performance a lot
[cwebfiles.git] / listdir.c
1 #include <stdlib.h>
2 #include <sys/types.h>
3 #include <unistd.h>
4 #include "cwebfiles.h"
5 #include <sys/types.h>
6 #include <dirent.h>
7 #include <unistd.h>
8 #include <sys/fsuid.h>
9 #include <string.h>
10 #include <stdio.h>
11 #include <errno.h>
12 #include <sys/stat.h>
13 #include <fmt.h>
14 #include <sys/prctl.h>
15
16 // 64k RAM aren't really that much, and it should speed things up significantly
17 #define OBULEN (64*1024)
18 static char outbuf[OBULEN] = "Status: 200 here's your listing"
19                             "\nX-Frame-Options: DENY"
20                             "\nContent-Type: text/plain;charset=utf8"
21                             "\n\n";
22
23 int main() {
24   //FASTOUTPUT
25   prctl(PR_SET_DUMPABLE, 1); // FIXME XXX potential security problem; disable later!
26   if (chdir("/")) senderr("chdir / failed", MYFAULT);
27   
28   char *encpath = getenv("QUERY_STRING");
29   login_and_setup(); // will clear the environment
30   if (encpath == NULL) senderr("no query string received", NOTMYFAULT);
31   
32   // OK, we're authenticated. Drop our fsuid to the user's.
33   // Yes, there's no error indication apart from this kludge. Sucks.
34   setfsuid(session.uid);
35   if (setfsuid(session.uid) != session.uid) senderr("setfsuid failed", MYFAULT);
36   
37   // Do what the user wants us to do.
38   char path[strlen(encpath)/2+1];
39   unhex((unsigned char *)path, encpath, strlen(encpath));
40   path[strlen(encpath)/2] = 0;
41   if (chdir(path)) {
42     char errstr[128];
43     snprintf(errstr, 128, "can't access that path: %s", strerror(errno));
44     senderr(errstr, NOTMYFAULT);
45   }
46   DIR *dir = opendir(".");
47   if (dir == NULL) {
48     char errstr[128];
49     snprintf(errstr, 128, "can't open that path for reading: %s", strerror(errno));
50     senderr(errstr, NOTMYFAULT);
51   }
52   
53   // ok, let's do fast output!
54   // The directory was opened successfully - send our success header.
55   char *p = outbuf+strlen(outbuf);
56   
57   // send one line per file
58   #define LINE_LENGTH_LIMIT (NAME_MAX*2+1+FMT_ULONG+1+FMT_ULONG+1)
59   struct dirent *dent;
60   while ((dent = readdir(dir)) != NULL) {
61     if (p+LINE_LENGTH_LIMIT >= outbuf+OBULEN) {
62       // Whoah, that's a big directory!
63       // Flush the pipe.
64       write(1, outbuf, p-outbuf);
65       // And reuse the buffer.
66       p = outbuf;
67     }
68     struct stat st;
69     // if we can't really see the file anyway, skip it
70     if (lstat(dent->d_name, &st)) continue;
71     //printf("%s %lu %u\n", encfilename, (unsigned long)st.st_size, (unsigned int)st.st_mode);
72     size_t name_len = strlen(dent->d_name);
73     hex(p, (unsigned char *)dent->d_name, name_len);
74     p += name_len*2;
75     *(p++) = ' ';
76     p += fmt_ulong(p, (unsigned long)st.st_size);
77     *(p++) = ' ';
78     p += fmt_ulong(p, (unsigned int)st.st_mode);
79     *(p++) = '\n';
80   }
81   
82   // final pipe flush
83   write(1, outbuf, p-outbuf);
84 }