From 543858d01d8683d0072107bafd8f0b2d61ca28f5 Mon Sep 17 00:00:00 2001 From: Camden Dixie O'Brien Date: Fri, 14 Oct 2022 10:20:38 +0100 Subject: [PATCH] Test whether requested resource is a file or directory --- main.c | 171 +++++++++++++++++++++++++++++++++------------------------ 1 file changed, 100 insertions(+), 71 deletions(-) diff --git a/main.c b/main.c index 269af0d..b0a8eb1 100644 --- a/main.c +++ b/main.c @@ -157,7 +157,6 @@ int main(int argc, char *argv[]) socklen_t paddr_size = sizeof(paddr); int cfd; ssize_t n, slen; - DIR *rdir; while (!exit_requested) { /* * Accept incoming connection. @@ -236,97 +235,127 @@ int main(int argc, char *argv[]) goto close_client_socket; ppos += n; - // UNSAFE! - pbuf[ppos] = '\0'; - printf("Requested path: %s\n", pbuf); - /* - * Determine the resource's type. + * Determine whether the resource is a file or a directory. + * + * The path passed to stat() has to be null-terminated, which + * pbuf is not, so a trailing null byte must first be added + * there. */ - - /* - * Open the resource. - */ - if (srvroot_len + 1 > PBUF_SIZE) { + if (ppos + 1 > PBUF_SIZE) { fprintf(stderr, "Path buffer is too small\n"); goto close_client_socket; } - pbuf[srvroot_len] = '\0'; - do { - errno = 0; - rdir = opendir(pbuf); - } while (errno == EINTR); - if (rdir == NULL) { - fprintf(stderr, "Failed to open %s\n", pbuf); + pbuf[ppos] = '\0'; + struct stat rstat; + if (stat(pbuf, &rstat) == -1) { + fprintf(stderr, "Failed to stat() path \"%s\"\n", pbuf); goto close_client_socket; } - - /* - * Write a line for each entry in the directory to the client. - */ - struct dirent *ent; - struct stat rstat; - unsigned namelen; - while ((ent = readdir(rdir)) != NULL) { - if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) - continue; - + if (S_ISREG(rstat.st_mode)) { /* - * Construct full path to entry in pbuf. - * - * This is needed in order to pass it to stat() later. The - * path must be null-terminated. + * Send hard-coded response for files for now. */ - namelen = strlen(ent->d_name); - if (srvroot_len + namelen + 2 > PBUF_SIZE) { - fprintf(stderr, "Path buffer is too small\n"); + const char *resp = "Files don't work yet\r\n.\r\n"; + if (retrying_write(cfd, resp, strlen(resp)) == -1) { + fprintf(stderr, "Couldn't write to client socket\n"); + goto close_client_socket; + } + } else if (S_ISDIR(rstat.st_mode)) { + /* + * Open the directory. + * + * The path is already null-terminated from the earlier call + * to stat(). + */ + DIR *rdir; + do { + errno = 0; + rdir = opendir(pbuf); + } while (errno == EINTR); + if (rdir == NULL) { + fprintf(stderr, "Failed to open %s\n", pbuf); goto close_client_socket; } - pbuf[srvroot_len] = '/'; - memcpy(&pbuf[srvroot_len + 1], ent->d_name, namelen); - pbuf[srvroot_len + 1 + namelen] = '\0'; /* - * Identify entry type from file inode information. + * Write a line for each entry in the directory to the client. */ - if (stat(pbuf, &rstat) == -1) { - fprintf(stderr, - "Failed to stat() path \"%s\", skipping entry\n", pbuf); - continue; + struct dirent *ent; + unsigned namelen; + while ((ent = readdir(rdir)) != NULL) { + /* + * Skip . and .. entries. + */ + if (strcmp(ent->d_name, ".") == 0 + || strcmp(ent->d_name, "..") == 0) + continue; + + /* + * Construct full path to entry in pbuf. + * + * This is needed in order to pass it to stat() later. The + * path must be null-terminated. + */ + namelen = strlen(ent->d_name); + if (srvroot_len + namelen + 2 > PBUF_SIZE) { + fprintf(stderr, "Path buffer is too small\n"); + goto close_client_socket; + } + pbuf[srvroot_len] = '/'; + memcpy(&pbuf[srvroot_len + 1], ent->d_name, namelen); + pbuf[srvroot_len + 1 + namelen] = '\0'; + + /* + * Identify entry type from file inode information. + */ + if (stat(pbuf, &rstat) == -1) { + fprintf(stderr, + "Failed to stat() path \"%s\", skipping entry\n", + pbuf); + continue; + } + enum etype type; + if (S_ISREG(rstat.st_mode)) + type = ETYPE_FILE; + else if (S_ISDIR(rstat.st_mode)) + type = ETYPE_DIR; + else + continue; + + /* + * Format and send response line for current entry. + */ + n = snprintf(rbuf, RBUF_SIZE, "%1u%s\t%s\t%s\t%u\r\n", type, + ent->d_name, &pbuf[srvroot_len], HOST, PORT); + if (n >= RBUF_SIZE) { + fprintf(stderr, + "Response buffer was too small, skipping entry\n"); + continue; + } + if (retrying_write(cfd, rbuf, n) != n) { + fprintf(stderr, "Error sending respose line to client\n"); + continue; + } + } + if (retrying_write(cfd, ".\r\n", 3) != 3) { + fprintf(stderr, + "Error sending response terminator to client\n"); + goto close_client_socket; } - enum etype type; - if (S_ISREG(rstat.st_mode)) - type = ETYPE_FILE; - else if (S_ISDIR(rstat.st_mode)) - type = ETYPE_DIR; - else - continue; /* - * Format and send response line for current entry. + * Close the resource. */ - n = snprintf(rbuf, RBUF_SIZE, "%1u%s\t%s\t%s\t%u\r\n", type, - ent->d_name, &pbuf[srvroot_len], HOST, PORT); - if (n >= RBUF_SIZE) { - fprintf(stderr, - "Response buffer was too small, skipping entry\n"); - continue; - } - if (retrying_write(cfd, rbuf, n) != n) { - fprintf(stderr, "Error sending respose line to client\n"); - continue; - } - } - if (retrying_write(cfd, ".\r\n", 3) != 3) { - fprintf(stderr, "Error sending response terminator to client\n"); + closedir(rdir); + } else { + fprintf(stderr, + "Requested resource \"%s\" was not a directory or a " + "regular file\n", + pbuf); goto close_client_socket; } - /* - * Close the resource. - */ - closedir(rdir); - close_client_socket: close(cfd); }