Skip to content

Commit 2086967

Browse files
authored
Merge pull request #382 from dpw13/fix/hls-directory
HLS directory creation refactoring
2 parents 2dd074a + 8da051b commit 2086967

4 files changed

Lines changed: 44 additions & 367 deletions

File tree

include/video/hls/hls_directory.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#ifndef HLS_DIRECTORY_H
22
#define HLS_DIRECTORY_H
33

4+
#include <stddef.h>
5+
46
/**
57
* Clean up HLS directories during shutdown
68
* This function removes old HLS segments and playlist files
@@ -10,11 +12,12 @@ void cleanup_hls_directories(void);
1012
/**
1113
* Ensure the HLS output directory exists and is writable
1214
*
13-
* @param output_dir The directory to check/create
15+
* @param output_dir A buffer to write the output directory to
16+
* @param output_dir_size The size of the output directory buffer
1417
* @param stream_name The name of the stream (for logging)
1518
* @return 0 on success, non-zero on failure
1619
*/
17-
int ensure_hls_directory(const char *output_dir, const char *stream_name);
20+
int ensure_hls_directory(char *output_dir, size_t output_dir_size, const char *stream_name);
1821

1922
/**
2023
* Clear HLS segments for a specific stream

src/video/hls/hls_directory.c

Lines changed: 30 additions & 208 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,11 @@
1919
/**
2020
* Ensure the HLS output directory exists and is writable
2121
*/
22-
int ensure_hls_directory(const char *output_dir, const char *stream_name) {
22+
int ensure_hls_directory(char *output_dir, size_t output_dir_size, const char *stream_name) {
23+
if (output_dir == NULL || output_dir_size == 0 || stream_name == NULL) {
24+
return -1;
25+
}
26+
2327
// Get the global config for storage path
2428
const config_t *global_config = get_streaming_config();
2529
if (!global_config) {
@@ -29,7 +33,6 @@ int ensure_hls_directory(const char *output_dir, const char *stream_name) {
2933

3034
// Always use the consistent path structure for HLS
3135
// Use storage_path_hls if specified, otherwise fall back to storage_path
32-
char safe_output_dir[MAX_PATH_LENGTH];
3336
const char *base_storage_path = global_config->storage_path;
3437

3538
// Check if storage_path_hls is specified and not empty
@@ -42,153 +45,18 @@ int ensure_hls_directory(const char *output_dir, const char *stream_name) {
4245
char stream_path[MAX_STREAM_NAME];
4346
sanitize_stream_name(stream_name, stream_path, MAX_STREAM_NAME);
4447

45-
snprintf(safe_output_dir, sizeof(safe_output_dir), "%s/hls/%s",
48+
snprintf(output_dir, output_dir_size, "%s/hls/%s",
4649
base_storage_path, stream_path);
4750

48-
// Log if we're redirecting from a different path
49-
if (strcmp(output_dir, safe_output_dir) != 0) {
50-
log_warn("Redirecting HLS output from %s to %s to ensure consistent path structure",
51-
output_dir, safe_output_dir);
52-
}
53-
54-
// Always use the safe path
55-
output_dir = safe_output_dir;
56-
5751
// Verify output directory exists and is writable
58-
struct stat st;
59-
if (stat(output_dir, &st) != 0 || !S_ISDIR(st.st_mode)) {
60-
log_warn("Output directory does not exist or is not a directory: %s", output_dir);
61-
62-
// Recreate it using direct C functions to handle paths with spaces
63-
char temp_path[MAX_PATH_LENGTH];
64-
safe_strcpy(temp_path, output_dir, MAX_PATH_LENGTH, 0);
65-
66-
// Create parent directories one by one
67-
for (char *p = temp_path + 1; *p; p++) {
68-
if (*p == '/') {
69-
*p = '\0';
70-
if (ensure_dir(temp_path)) {
71-
log_warn("Failed to create parent directory: %s (error: %s)", temp_path, strerror(errno));
72-
}
73-
*p = '/';
74-
}
75-
}
76-
77-
// Create the final directory
78-
if (ensure_dir(temp_path)) {
79-
log_error("Failed to create output directory: %s (error: %s)", temp_path, strerror(errno));
80-
return -1;
81-
}
82-
83-
// Verify the directory was created and set permissions via fd to avoid TOCTOU
84-
int dir_fd = open(output_dir, O_RDONLY | O_DIRECTORY | O_NOFOLLOW);
85-
if (dir_fd < 0 || fstat(dir_fd, &st) != 0 || !S_ISDIR(st.st_mode)) {
86-
log_error("Failed to verify output directory: %s", output_dir);
87-
if (dir_fd >= 0) close(dir_fd);
88-
return -1;
89-
}
90-
91-
// Use fchmod on the open fd to avoid TOCTOU race between stat and chmod (#32)
92-
if (fchmod(dir_fd, 0755) != 0) {
93-
log_warn("Failed to set permissions on directory: %s (error: %s)", output_dir, strerror(errno));
94-
}
95-
close(dir_fd);
96-
97-
log_info("Successfully created output directory: %s", output_dir);
98-
}
99-
100-
// Ensure the directory is writable. Use open()+fstatat()+fchmod() exclusively
101-
// so that there is no TOCTOU race between an access() check and the open().
102-
{
103-
int dir_fd = open(output_dir, O_RDONLY | O_DIRECTORY | O_NOFOLLOW);
104-
if (dir_fd < 0) {
105-
log_error("Failed to open output directory: %s (error: %s)", output_dir, strerror(errno));
106-
return -1;
107-
}
108-
109-
struct stat dir_st;
110-
if (fstat(dir_fd, &dir_st) != 0) {
111-
log_error("Failed to stat output directory fd: %s (error: %s)", output_dir, strerror(errno));
112-
close(dir_fd);
113-
return -1;
114-
}
115-
116-
// If the owner-write bit is missing, try to add full permissions via fd
117-
if (!(dir_st.st_mode & S_IWUSR)) {
118-
log_warn("Output directory may not be writable: %s, attempting permission fix", output_dir);
119-
if (fchmod(dir_fd, 0755) != 0) {
120-
log_warn("Failed to set permissions on directory: %s (error: %s)", output_dir, strerror(errno));
121-
}
122-
}
123-
124-
close(dir_fd);
52+
if (mkdir_recursive(output_dir)) {
53+
log_error("Failed to create output directory: %s", output_dir);
54+
return -1;
12555
}
12656

127-
// Create a parent directory check file to ensure the parent directory exists
128-
const char *last_slash = strrchr(output_dir, '/');
129-
if (last_slash) {
130-
char parent_dir[MAX_PATH_LENGTH];
131-
size_t parent_len = last_slash - output_dir;
132-
safe_strcpy(parent_dir, output_dir, MAX_PATH_LENGTH, parent_len);
133-
parent_dir[parent_len] = '\0';
134-
135-
// Create parent directory using direct C functions
136-
char temp_path[MAX_PATH_LENGTH];
137-
safe_strcpy(temp_path, parent_dir, MAX_PATH_LENGTH, 0);
138-
139-
// Create parent directories one by one
140-
for (char *p = temp_path + 1; *p; p++) {
141-
if (*p == '/') {
142-
*p = '\0';
143-
if (ensure_dir(temp_path)) {
144-
log_warn("Failed to create parent directory: %s (error: %s)", temp_path, strerror(errno));
145-
}
146-
*p = '/';
147-
}
148-
}
149-
150-
// Create the final directory
151-
if (ensure_dir(temp_path)) {
152-
log_warn("Failed to create parent directory: %s (error: %s)", temp_path, strerror(errno));
153-
}
154-
155-
// Create a test file in the parent directory (with restricted permissions 0644)
156-
char test_file[MAX_PATH_LENGTH];
157-
snprintf(test_file, sizeof(test_file), "%s/.hls_parent_check", parent_dir);
158-
int test_fd = open(test_file, O_WRONLY | O_CREAT | O_TRUNC, 0644);
159-
if (test_fd >= 0) {
160-
close(test_fd);
161-
// Leave the file there as a marker
162-
log_info("Verified parent directory is writable: %s", parent_dir);
163-
} else {
164-
log_warn("Parent directory may not be writable: %s (error: %s)",
165-
parent_dir, strerror(errno));
166-
167-
// Try to create parent directory with full permissions using direct C functions
168-
char retry_path[MAX_PATH_LENGTH];
169-
safe_strcpy(retry_path, parent_dir, MAX_PATH_LENGTH, 0);
170-
171-
// Create parent directories one by one
172-
for (char *p = retry_path + 1; *p; p++) {
173-
if (*p == '/') {
174-
*p = '\0';
175-
if (ensure_dir(retry_path)) {
176-
log_warn("Failed to create parent directory: %s (error: %s)", retry_path, strerror(errno));
177-
} else {
178-
// Set permissions (owner rwx, group/other rx)
179-
chmod(retry_path, 0755);
180-
}
181-
*p = '/';
182-
}
183-
}
184-
185-
// Create the final directory
186-
if (ensure_dir(retry_path)) {
187-
log_warn("Failed to create parent directory: %s (error: %s)", retry_path, strerror(errno));
188-
}
189-
190-
log_info("Attempted to recreate parent directory with full permissions: %s", parent_dir);
191-
}
57+
// Ensure the directory is writable.
58+
if (chmod_path(output_dir, 0755) != 0) {
59+
log_warn("Failed to set permissions on output directory: %s", output_dir);
19260
}
19361

19462
return 0;
@@ -234,62 +102,45 @@ int clear_stream_hls_segments(const char *stream_name) {
234102

235103
log_info("Clearing HLS segments for stream: %s in directory: %s", stream_name, stream_hls_dir);
236104

105+
const char *to_remove[] = {".ts", ".m4s", ".m3u8"};
106+
237107
// Remove all .ts segment files using direct C functions
238108
DIR *dir = opendir(stream_hls_dir);
239109
if (dir) {
240110
const struct dirent *entry;
241111
int removed_count = 0;
242112

243113
while ((entry = readdir(dir)) != NULL) {
244-
// Check if this is a .ts file
245-
if (strstr(entry->d_name, ".ts") != NULL) {
246-
char file_path[MAX_PATH_LENGTH];
247-
snprintf(file_path, sizeof(file_path), "%s/%s", stream_hls_dir, entry->d_name);
248-
249-
if (unlink(file_path) == 0) {
250-
removed_count++;
251-
} else {
252-
log_warn("Failed to remove HLS .ts file: %s (error: %s)",
253-
file_path, strerror(errno));
114+
bool match = false;
115+
// Check if any extension matches
116+
for (int i = 0; i < sizeof(to_remove)/sizeof(char *); i++) {
117+
if (ends_with(entry->d_name, to_remove[i])) {
118+
match = true;
119+
break;
254120
}
255121
}
256-
}
257122

258-
closedir(dir);
259-
log_info("Removed %d HLS .ts segment files in %s", removed_count, stream_hls_dir);
260-
} else {
261-
log_warn("Failed to open directory to remove .ts files: %s (error: %s)",
262-
stream_hls_dir, strerror(errno));
263-
}
264-
265-
// Remove all .m4s segment files (for fMP4) using direct C functions
266-
dir = opendir(stream_hls_dir);
267-
if (dir) {
268-
const struct dirent *entry;
269-
int removed_count = 0;
270-
271-
while ((entry = readdir(dir)) != NULL) {
272-
// Check if this is a .m4s file
273-
if (strstr(entry->d_name, ".m4s") != NULL) {
123+
if (match) {
274124
char file_path[MAX_PATH_LENGTH];
275125
snprintf(file_path, sizeof(file_path), "%s/%s", stream_hls_dir, entry->d_name);
276126

277127
if (unlink(file_path) == 0) {
278128
removed_count++;
279129
} else {
280-
log_warn("Failed to remove HLS .m4s file: %s (error: %s)",
130+
log_warn("Failed to remove HLS file: %s (error: %s)",
281131
file_path, strerror(errno));
282132
}
283133
}
284134
}
285135

286136
closedir(dir);
287-
log_info("Removed %d HLS .m4s segment files in %s", removed_count, stream_hls_dir);
137+
log_info("Removed %d HLS files in %s", removed_count, stream_hls_dir);
288138
} else {
289-
log_warn("Failed to open directory to remove .m4s files: %s (error: %s)",
139+
log_warn("Failed to open directory to remove HLS files: %s (error: %s)",
290140
stream_hls_dir, strerror(errno));
291141
}
292142

143+
293144
// Remove init.mp4 file (for fMP4) using direct C function
294145
char init_file_path[MAX_PATH_LENGTH];
295146
snprintf(init_file_path, sizeof(init_file_path), "%s/init.mp4", stream_hls_dir);
@@ -301,38 +152,9 @@ int clear_stream_hls_segments(const char *stream_name) {
301152
stream_hls_dir, strerror(errno));
302153
}
303154

304-
// Remove all .m3u8 playlist files using direct C functions
305-
dir = opendir(stream_hls_dir);
306-
if (dir) {
307-
const struct dirent *entry;
308-
int removed_count = 0;
309-
310-
while ((entry = readdir(dir)) != NULL) {
311-
// Check if this is a .m3u8 file
312-
if (strstr(entry->d_name, ".m3u8") != NULL) {
313-
char file_path[MAX_PATH_LENGTH];
314-
snprintf(file_path, sizeof(file_path), "%s/%s", stream_hls_dir, entry->d_name);
315-
316-
if (unlink(file_path) == 0) {
317-
removed_count++;
318-
} else {
319-
log_warn("Failed to remove HLS .m3u8 file: %s (error: %s)",
320-
file_path, strerror(errno));
321-
}
322-
}
323-
}
324-
325-
closedir(dir);
326-
log_info("Removed %d HLS .m3u8 playlist files in %s", removed_count, stream_hls_dir);
327-
} else {
328-
log_warn("Failed to open directory to remove .m3u8 files: %s (error: %s)",
329-
stream_hls_dir, strerror(errno));
330-
}
331-
332-
// Ensure the directory has proper permissions using direct chmod
333-
if (chmod(stream_hls_dir, 0755) != 0) {
334-
log_warn("Failed to set permissions on directory: %s (error: %s)",
335-
stream_hls_dir, strerror(errno));
155+
// Ensure the directory has proper permissions
156+
if (chmod_path(stream_hls_dir, 0755) != 0) {
157+
log_warn("Failed to set permissions on directory: %s", stream_hls_dir);
336158
}
337159

338160
return 0;
@@ -613,8 +435,8 @@ void cleanup_hls_directories(void) {
613435
}
614436
}
615437

616-
// Ensure the directory has proper permissions using direct chmod
617-
if (chmod(stream_hls_dir, 0755) != 0) {
438+
// Ensure the directory has proper permissions
439+
if (chmod_path(stream_hls_dir, 0755) != 0) {
618440
log_warn("Failed to set permissions on directory: %s (error: %s)",
619441
stream_hls_dir, strerror(errno));
620442
}

0 commit comments

Comments
 (0)