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