1 /*-*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 
3 /*  Monkey HTTP Server
4  *  ==================
5  *  Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
6  *
7  *  Licensed under the Apache License, Version 2.0 (the "License");
8  *  you may not use this file except in compliance with the License.
9  *  You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  *  Unless required by applicable law or agreed to in writing, software
14  *  distributed under the License is distributed on an "AS IS" BASIS,
15  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  *  See the License for the specific language governing permissions and
17  *  limitations under the License.
18  */
19 
20 #define _GNU_SOURCE
21 
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <fcntl.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <errno.h>
28 
29 #include "mk_core.h"
30 
31 #ifdef _WIN32
32 #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
33 #define S_ISLNK(m) (0)
34 #define O_NONBLOCK (0)
35 #define lstat stat
36 #endif
37 
mk_file_get_info(const char * path,struct file_info * f_info,int mode)38 int mk_file_get_info(const char *path, struct file_info *f_info, int mode)
39 {
40     struct stat f, target;
41 
42     f_info->exists = MK_FALSE;
43 
44     /* Stat right resource */
45     if (lstat(path, &f) == -1) {
46         if (errno == EACCES) {
47             f_info->exists = MK_TRUE;
48         }
49         return -1;
50     }
51 
52     f_info->exists = MK_TRUE;
53     f_info->is_file = MK_TRUE;
54     f_info->is_link = MK_FALSE;
55     f_info->is_directory = MK_FALSE;
56     f_info->exec_access = MK_FALSE;
57     f_info->read_access = MK_FALSE;
58 
59     if (S_ISLNK(f.st_mode)) {
60         f_info->is_link = MK_TRUE;
61         f_info->is_file = MK_FALSE;
62         if (stat(path, &target) == -1) {
63             return -1;
64         }
65     }
66     else {
67         target = f;
68     }
69 
70     f_info->size = target.st_size;
71     f_info->last_modification = target.st_mtime;
72 
73     if (S_ISDIR(target.st_mode)) {
74         f_info->is_directory = MK_TRUE;
75         f_info->is_file = MK_FALSE;
76     }
77 
78 #ifndef _WIN32
79     gid_t EGID = getegid();
80     gid_t EUID = geteuid();
81 
82     /* Check read access */
83     if (mode & MK_FILE_READ) {
84         if (((target.st_mode & S_IRUSR) && target.st_uid == EUID) ||
85             ((target.st_mode & S_IRGRP) && target.st_gid == EGID) ||
86             (target.st_mode & S_IROTH)) {
87             f_info->read_access = MK_TRUE;
88         }
89     }
90 
91     /* Checking execution */
92     if (mode & MK_FILE_EXEC) {
93         if ((target.st_mode & S_IXUSR && target.st_uid == EUID) ||
94             (target.st_mode & S_IXGRP && target.st_gid == EGID) ||
95             (target.st_mode & S_IXOTH)) {
96             f_info->exec_access = MK_TRUE;
97         }
98     }
99 #endif
100 
101     /* Suggest open(2) flags */
102     f_info->flags_read_only = O_RDONLY | O_NONBLOCK;
103 
104 #if defined(__linux__)
105     /*
106      * If the user is the owner of the file or the user is root, it
107      * can set the O_NOATIME flag for open(2) operations to avoid
108      * inode updates about last accessed time
109      */
110     if (target.st_uid == EUID || EUID == 0) {
111         f_info->flags_read_only |=  O_NOATIME;
112     }
113 #endif
114 
115     return 0;
116 }
117 
118 /* Read file content to a memory buffer,
119  * Use this function just for really SMALL files
120  */
mk_file_to_buffer(const char * path)121 char *mk_file_to_buffer(const char *path)
122 {
123     FILE *fp;
124     char *buffer;
125     long bytes;
126     struct file_info finfo;
127 
128     if (mk_file_get_info(path, &finfo, MK_FILE_READ) != 0) {
129         return NULL;
130     }
131 
132     if (!(fp = fopen(path, "rb"))) {
133         return NULL;
134     }
135 
136     buffer = mk_mem_alloc_z(finfo.size + 1);
137     if (!buffer) {
138         fclose(fp);
139         return NULL;
140     }
141 
142     bytes = fread(buffer, finfo.size, 1, fp);
143 
144     if (bytes < 1) {
145         mk_mem_free(buffer);
146         fclose(fp);
147         return NULL;
148     }
149 
150     fclose(fp);
151     return (char *) buffer;
152 
153 }
154