46
46
extern bool Debug ;
47
47
48
48
/* Constants */
49
- #define LOG_BUFFER_SIZE (100 * 1024) /* 100KB buffer for reading log file */
50
- #define MAX_LOG_LINES_DISPLAY 10 /* Number of log lines to show when no stats found */
49
+ /* 100KB buffer for reading log file */
50
+ #define LOG_BUFFER_SIZE (100 * 1024)
51
+ /* Number of log lines to show when no stats found */
52
+ #define MAX_LOG_LINES_DISPLAY 10
53
+
54
+ /* Static buffer for reading log file - shared across functions */
55
+ static char log_buffer [LOG_BUFFER_SIZE + 1 ];
56
+ /* Actual number of bytes read into log_buffer */
57
+ static size_t log_buffer_bytes_read = 0 ;
58
+
59
+ /* Compile-time assertion to ensure buffer size is reasonable */
60
+ _Static_assert (LOG_BUFFER_SIZE > 0 && LOG_BUFFER_SIZE <= (1024 * 1024 ),
61
+ "LOG_BUFFER_SIZE must be between 1 byte and 1MB" );
51
62
52
63
/* Debug logging macro */
53
64
#define DEBUG_LOG (fmt , ...) \
@@ -62,7 +73,6 @@ static char *parse_config_for_log_path(void)
62
73
dictionary * iniconfig = NULL ;
63
74
char * log_file_path = NULL ;
64
75
DEBUG_LOG ("Loading config file: %safp.conf" , _PATH_CONFDIR );
65
- /* Load the config file using iniparser */
66
76
iniconfig = iniparser_load (_PATH_CONFDIR "afp.conf" );
67
77
68
78
if (!iniconfig ) {
@@ -90,25 +100,20 @@ static char *parse_config_for_log_path(void)
90
100
return log_file_path ;
91
101
}
92
102
93
- /* Read the last portion of log file into buffer
94
- * Buffer must be at least LOG_BUFFER_SIZE + 1 bytes */
95
- static ssize_t read_log_tail (const char * log_file_path , char * buffer )
103
+ /* Read the last portion of log file into the static log_buffer
104
+ * Returns: true on success, false on error */
105
+ static bool read_log_tail (const char * log_file_path )
96
106
{
97
107
FILE * log_fp = NULL ;
98
-
99
- /* Validate input parameters */
100
- if (!buffer ) {
101
- fprintf (stderr , "ERROR: Buffer is NULL\n" );
102
- return -1 ;
103
- }
104
-
108
+ /* Reset global state */
109
+ log_buffer_bytes_read = 0 ;
105
110
DEBUG_LOG ("Opening log file: %s" , log_file_path );
106
111
log_fp = fopen (log_file_path , "rb" );
107
112
108
113
if (!log_fp ) {
109
114
DEBUG_LOG ("Failed to open log file: %s (errno=%d: %s)" ,
110
115
log_file_path , errno , strerror (errno ));
111
- return -1 ;
116
+ return false ;
112
117
}
113
118
114
119
DEBUG_LOG ("Successfully opened log file" );
@@ -117,7 +122,7 @@ static ssize_t read_log_tail(const char *log_file_path, char *buffer)
117
122
fprintf (stderr , "ERROR: Failed to seek to end of log file: %s\n" ,
118
123
strerror (errno ));
119
124
fclose (log_fp );
120
- return -1 ;
125
+ return false ;
121
126
}
122
127
123
128
long file_size = ftell (log_fp );
@@ -126,76 +131,82 @@ static ssize_t read_log_tail(const char *log_file_path, char *buffer)
126
131
fprintf (stderr , "ERROR: Failed to determine log file size: %s\n" ,
127
132
strerror (errno ));
128
133
fclose (log_fp );
129
- return -1 ;
134
+ return false ;
130
135
}
131
136
132
137
DEBUG_LOG ("Log file size: %ld bytes" , file_size );
133
138
134
139
if (file_size <= 0 ) {
135
140
fprintf (stderr , "WARNING: Log file size is 0, no content to read\n" );
136
141
fclose (log_fp );
137
- return 0 ;
142
+ /* Not an error, just no content */
143
+ return true;
138
144
}
139
145
140
146
/* Read from the end, up to LOG_BUFFER_SIZE to reserve space for null terminator */
141
- size_t max_read = LOG_BUFFER_SIZE ;
142
- long read_start = (file_size > (long )max_read ) ? (file_size -
143
- (long )max_read ) : 0 ;
144
- size_t read_size = (file_size > (long )max_read ) ? max_read :
147
+ long read_start = (file_size > LOG_BUFFER_SIZE ) ? (file_size - LOG_BUFFER_SIZE )
148
+ : 0 ;
149
+ size_t read_size = (file_size > LOG_BUFFER_SIZE ) ? LOG_BUFFER_SIZE :
145
150
(size_t )file_size ;
146
151
DEBUG_LOG ("Reading %zu bytes from position: %ld" , read_size , read_start );
147
152
148
153
if (fseek (log_fp , read_start , SEEK_SET ) != 0 ) {
149
154
fprintf (stderr , "ERROR: Failed to seek to read position: %s\n" ,
150
155
strerror (errno ));
151
156
fclose (log_fp );
152
- return -1 ;
157
+ return false ;
153
158
}
154
159
155
- size_t bytes_read = fread (buffer , 1 , read_size , log_fp );
160
+ size_t bytes_read = fread (log_buffer , 1 , read_size , log_fp );
156
161
157
162
if (ferror (log_fp )) {
158
163
fprintf (stderr , "ERROR: Failed to read log file: %s\n" , strerror (errno ));
159
164
fclose (log_fp );
160
- return -1 ;
165
+ return false ;
161
166
}
162
167
163
- /* Simple validation - fread cannot return more than requested */
164
- if (bytes_read > read_size ) {
165
- fprintf (stderr , "ERROR: fread returned more bytes than requested \n" );
168
+ /* Explicit bounds check and enforcement for static analyzer */
169
+ if (bytes_read > LOG_BUFFER_SIZE ) {
170
+ fprintf (stderr , "ERROR: Read more bytes than buffer size (corruption?) \n" );
166
171
fclose (log_fp );
167
- return -1 ;
172
+ return false;
173
+ }
174
+
175
+ /* Explicitly cap the value to help static analyzer understand the bound */
176
+ if (bytes_read > LOG_BUFFER_SIZE ) {
177
+ bytes_read = LOG_BUFFER_SIZE ;
168
178
}
169
179
170
- /* Null-terminate the buffer
171
- * Since read_size <= LOG_BUFFER_SIZE and bytes_read <= read_size,
172
- * we know bytes_read <= LOG_BUFFER_SIZE, so this is always safe */
173
- buffer [bytes_read ] = '\0' ;
180
+ /* Null-terminate the buffer - always safe with static buffer */
181
+ log_buffer [bytes_read ] = '\0' ;
182
+ log_buffer_bytes_read = bytes_read ;
174
183
fclose (log_fp );
175
- return ( ssize_t ) bytes_read ;
184
+ return true ;
176
185
}
177
186
178
- /* Search buffer backwards for dircache statistics line */
179
- static char * find_dircache_stats_line (const char * buffer , size_t bytes_read )
187
+ /* Search buffer backwards for dircache statistics line
188
+ * Uses the global log_buffer and log_buffer_bytes_read */
189
+ static char * find_dircache_stats_line (void )
180
190
{
181
191
char * stats_line = NULL ;
182
192
183
- /* Validate bytes_read before using for allocation */
184
- if (bytes_read == 0 || bytes_read > LOG_BUFFER_SIZE ) {
193
+ if (log_buffer_bytes_read == 0 ) {
185
194
return NULL ;
186
195
}
187
196
188
- /* Make a working copy of the buffer to avoid const issues */
189
- char * work_buffer = malloc (bytes_read + 1 );
197
+ /* Make a working copy of the static buffer to avoid modifying it
198
+ * log_buffer_bytes_read is guaranteed <= LOG_BUFFER_SIZE by read_log_tail() */
199
+ char * work_buffer = malloc (log_buffer_bytes_read + 1 );
190
200
191
201
if (!work_buffer ) {
192
202
return NULL ;
193
203
}
194
204
195
- memcpy (work_buffer , buffer , bytes_read );
196
- work_buffer [bytes_read ] = '\0' ;
205
+ /* Safe copy - log_buffer_bytes_read is bounded by LOG_BUFFER_SIZE */
206
+ memcpy (work_buffer , log_buffer , log_buffer_bytes_read );
207
+ work_buffer [log_buffer_bytes_read ] = '\0' ;
197
208
/* Search backwards through buffer for the LAST dircache statistics line */
198
- char * current = work_buffer + bytes_read ;
209
+ char * current = work_buffer + log_buffer_bytes_read ;
199
210
200
211
while (current > work_buffer ) {
201
212
/* Find end of previous line */
@@ -238,34 +249,34 @@ static char *find_dircache_stats_line(const char *buffer, size_t bytes_read)
238
249
}
239
250
240
251
/* Display the last N lines from buffer when no stats found */
241
- static void display_last_log_lines (const char * buffer , size_t bytes_read )
252
+ static void display_last_log_lines (void )
242
253
{
243
- /* No dircache statistics found - print message and last 10 lines of log */
244
254
printf ("No 'dircache statistics:' logs found.\n\n" );
245
255
printf ("(At least 'log level = default:info' is required)\n" );
246
256
printf ("Last 10 lines of log file:\n" );
247
257
printf ("---------------------------\n" );
248
258
249
- /* Validate bytes_read before using for allocation */
250
- if (bytes_read == 0 || bytes_read > LOG_BUFFER_SIZE ) {
251
- printf ("Invalid buffer size\n" );
259
+ if (log_buffer_bytes_read == 0 ) {
260
+ printf ("No log content available\n" );
252
261
return ;
253
262
}
254
263
255
- /* Make a working copy of the buffer to avoid const issues */
256
- char * work_buffer = malloc (bytes_read + 1 );
264
+ /* Make a working copy of the static buffer
265
+ * log_buffer_bytes_read is guaranteed <= LOG_BUFFER_SIZE by read_log_tail() */
266
+ char * work_buffer = malloc (log_buffer_bytes_read + 1 );
257
267
258
268
if (!work_buffer ) {
259
269
printf ("Memory allocation failed\n" );
260
270
return ;
261
271
}
262
272
263
- memcpy (work_buffer , buffer , bytes_read );
264
- work_buffer [bytes_read ] = '\0' ;
273
+ /* Safe copy - log_buffer_bytes_read is bounded by LOG_BUFFER_SIZE */
274
+ memcpy (work_buffer , log_buffer , log_buffer_bytes_read );
275
+ work_buffer [log_buffer_bytes_read ] = '\0' ;
265
276
/* Search backwards through buffer for last 10 lines */
266
277
char * last_lines [MAX_LOG_LINES_DISPLAY ] = {NULL };
267
278
int line_count = 0 ;
268
- char * curr = work_buffer + bytes_read ;
279
+ char * curr = work_buffer + log_buffer_bytes_read ;
269
280
270
281
while (curr > work_buffer && line_count < MAX_LOG_LINES_DISPLAY ) {
271
282
/* Find end of previous line */
@@ -326,8 +337,7 @@ void display_dircache_statistics(void)
326
337
{
327
338
char * log_file_path = NULL ;
328
339
char * stats_line = NULL ;
329
- static char buffer [LOG_BUFFER_SIZE + 1 ];
330
- ssize_t bytes_read ;
340
+ bool read_success ;
331
341
log_file_path = parse_config_for_log_path ();
332
342
333
343
if (!log_file_path ) {
@@ -337,21 +347,24 @@ void display_dircache_statistics(void)
337
347
}
338
348
339
349
DEBUG_LOG ("Log file path: %s" , log_file_path );
340
- bytes_read = read_log_tail (log_file_path , buffer , sizeof ( buffer ) );
350
+ read_success = read_log_tail (log_file_path );
341
351
342
- if (bytes_read <= 0 ) {
352
+ if (! read_success ) {
343
353
free (log_file_path );
344
354
return ;
345
355
}
346
356
347
- /* Validate bytes_read is positive and reasonable before casting */
348
- if (bytes_read < 0 || bytes_read > LOG_BUFFER_SIZE ) {
349
- fprintf (stderr , "ERROR: Invalid bytes_read value: %zd\n" , bytes_read );
357
+ /* Check if we actually read any content */
358
+ if (log_buffer_bytes_read == 0 ) {
359
+ printf ("\nDircache Statistics (%s):\n" , log_file_path );
360
+ printf ("------------------------------------------------------------------\n" );
361
+ printf ("Log file is empty\n" );
350
362
free (log_file_path );
351
363
return ;
352
364
}
353
365
354
- stats_line = find_dircache_stats_line (buffer , (size_t )bytes_read );
366
+ /* Search for dircache statistics line using global buffer */
367
+ stats_line = find_dircache_stats_line ();
355
368
printf ("\nDircache Statistics (%s):\n" , log_file_path );
356
369
printf ("------------------------------------------------------------------\n" );
357
370
@@ -366,7 +379,7 @@ void display_dircache_statistics(void)
366
379
367
380
free (stats_line );
368
381
} else {
369
- display_last_log_lines (buffer , ( size_t ) bytes_read );
382
+ display_last_log_lines ();
370
383
}
371
384
372
385
free (log_file_path );
0 commit comments