Test whether requested resource is a file or directory

This commit is contained in:
Camden Dixie O'Brien 2022-10-14 10:20:38 +01:00
parent a3cdde2c34
commit 543858d01d

171
main.c
View File

@ -157,7 +157,6 @@ int main(int argc, char *argv[])
socklen_t paddr_size = sizeof(paddr); socklen_t paddr_size = sizeof(paddr);
int cfd; int cfd;
ssize_t n, slen; ssize_t n, slen;
DIR *rdir;
while (!exit_requested) { while (!exit_requested) {
/* /*
* Accept incoming connection. * Accept incoming connection.
@ -236,97 +235,127 @@ int main(int argc, char *argv[])
goto close_client_socket; goto close_client_socket;
ppos += n; 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.
*/ */
if (ppos + 1 > PBUF_SIZE) {
/*
* Open the resource.
*/
if (srvroot_len + 1 > PBUF_SIZE) {
fprintf(stderr, "Path buffer is too small\n"); fprintf(stderr, "Path buffer is too small\n");
goto close_client_socket; goto close_client_socket;
} }
pbuf[srvroot_len] = '\0'; pbuf[ppos] = '\0';
do { struct stat rstat;
errno = 0; if (stat(pbuf, &rstat) == -1) {
rdir = opendir(pbuf); fprintf(stderr, "Failed to stat() path \"%s\"\n", pbuf);
} while (errno == EINTR);
if (rdir == NULL) {
fprintf(stderr, "Failed to open %s\n", pbuf);
goto close_client_socket; goto close_client_socket;
} }
if (S_ISREG(rstat.st_mode)) {
/*
* 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;
/* /*
* Construct full path to entry in pbuf. * Send hard-coded response for files for now.
*
* This is needed in order to pass it to stat() later. The
* path must be null-terminated.
*/ */
namelen = strlen(ent->d_name); const char *resp = "Files don't work yet\r\n.\r\n";
if (srvroot_len + namelen + 2 > PBUF_SIZE) { if (retrying_write(cfd, resp, strlen(resp)) == -1) {
fprintf(stderr, "Path buffer is too small\n"); 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; 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) { struct dirent *ent;
fprintf(stderr, unsigned namelen;
"Failed to stat() path \"%s\", skipping entry\n", pbuf); while ((ent = readdir(rdir)) != NULL) {
continue; /*
* 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, closedir(rdir);
ent->d_name, &pbuf[srvroot_len], HOST, PORT); } else {
if (n >= RBUF_SIZE) { fprintf(stderr,
fprintf(stderr, "Requested resource \"%s\" was not a directory or a "
"Response buffer was too small, skipping entry\n"); "regular file\n",
continue; pbuf);
}
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; goto close_client_socket;
} }
/*
* Close the resource.
*/
closedir(rdir);
close_client_socket: close_client_socket:
close(cfd); close(cfd);
} }