OSDN Git Service

cleanup
[openpts/openpts.git] / src / misc.c
1 /*
2  * This file is part of the OpenPTS project.
3  *
4  * The Initial Developer of the Original Code is International
5  * Business Machines Corporation. Portions created by IBM
6  * Corporation are Copyright (C) 2010 International Business
7  * Machines Corporation. All Rights Reserved.
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the Common Public License as published by
11  * IBM Corporation; either version 1 of the License, or (at your option)
12  * any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * Common Public License for more details.
18  *
19  * You should have received a copy of the Common Public License
20  * along with this program; if not, a copy can be viewed at
21  * http://www.opensource.org/licenses/cpl1.0.php.
22  */
23
24 /**
25  * \file src/misc.c
26  * \brief misc functions
27  * @author Seiji Munetoh <munetoh@users.sourceforge.jp>
28  * @date 2010-09-08
29  * cleanup 2012-01-05 SM (remains one lint error)
30  *  src/misc.c:448:  If you can, use sizeof(ptr) instead of 3 as the 2nd arg to snprintf.  [runtime/printf] [3]
31  */
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <dirent.h>
37
38 #include <netdb.h>
39 #include <errno.h>
40
41 #define __USE_GNU
42 #include <search.h>  // hash table
43
44 #include <sys/types.h>
45 #include <sys/socket.h>
46 #include <sys/stat.h>
47 #include <netinet/in.h>
48 #include <unistd.h>
49 #include <fcntl.h>
50
51 #include <openpts.h>
52
53 /**
54    Due to the frequent use of malloc/free in the code base (as opposed to
55    stack based allocation) these wrapper routines were added for easier debugging
56    - after their introduction several asserts fired that found genuine bugs. In
57    theory, for most programs that are not daemons we never really need to free memory
58    since it gets freed on program exit anyway. In addition, malloc should never
59    really fail - if so it usually indicates a programming error.
60    NOTE: On AIX the address 0x00000000 is a valid address, corresponding to a 
61    read-only map present in the address space of all running programs.
62 **/
63
64 #ifndef ALWAYS_ASSERT_ON_BAD_ALLOC
65 void *xmalloc(size_t size) {
66     char *result = malloc(size);
67     if (NULL == result) {
68         LOG(LOG_ERR, "Failed to allocate %d bytes of memory\n", size);
69         ERROR(NLS(MS_OPENPTS, OPENPTS_MALLOC_FAIL,
70             "No memory\n"));
71     }
72     return result;
73 }
74 #endif
75
76 void *xmalloc_assert(size_t size) {
77     char *result = malloc(size);
78     if (NULL == result) {
79         LOG(LOG_ERR, "Failed to allocate %d bytes of memory\n", size);
80         ERROR(NLS(MS_OPENPTS, OPENPTS_ABORT,
81             "Abort to return NULL pointer - cannot continue\n"));
82         exit(1);
83     }
84     return result;
85 }
86
87 void xfree(void *buf) {
88     if (buf == NULL) {
89         LOG(LOG_ERR, "Freeing a NULL pointer is bad");
90         return;
91     }
92 #ifndef NEVER_FREE_MEMORY
93     free(buf);
94 #endif
95 }
96
97
98 #ifndef ALWAYS_ASSERT_ON_BAD_ALLOC
99 /**
100  * malloc and copy string
101  */
102 char *smalloc(char *str) {
103     char *out;
104
105     if (str == NULL) {
106         DEBUG("null input\n");
107         return NULL;
108     }
109
110     /* check string length */
111     out = strdup(str);
112     if (out == NULL) {
113         LOG(LOG_ERR, "Failed to duplicate string '%s'\n", str);
114     }
115
116     return out;
117 }
118 #endif
119
120
121 /**
122  * malloc and copy string
123  */
124 char *smalloc_assert(char *str) {
125     char *out;
126
127     if (str == NULL) {
128         DEBUG("smalloc - string is NULL\n");
129         return NULL;
130     }
131
132     /* check string length */
133     out = strdup(str);
134     if (NULL == out) {
135         LOG(LOG_ERR, "Failed to duplicate string '%s'\n", str);
136         ERROR(NLS(MS_OPENPTS, OPENPTS_ABORT,
137             "Abort to return NULL pointer - cannot continue\n"));
138         exit(1);
139     }
140
141     return out;
142 }
143
144 /**
145  * malloc and copy string with length
146  * add \n
147  * len(str) > len
148  *
149  *  str    len   out
150  *  AAAAA  5     AAAA\n
151  *  AAAAA  4     AAA\n
152  */
153 char *snmalloc(char *str, int len) {
154     char *out;
155
156     /* check */
157     if (str == NULL) {
158         LOG(LOG_ERR, "smalloc - string is NULL\n");
159         return NULL;
160     }
161
162     if (len == 0) {
163         return NULL;
164     }
165
166 #ifdef MACOS
167     out = xmalloc_assert(len);
168     strncpy(out, str, len);
169     /* ensure always NULL-terminated */
170     out[len - 1] = '\0';
171 #else
172     out = strndup(str, len);
173 #endif
174     return out;
175 }
176
177 /**
178  * get NEW string buffer
179  *
180  *  snmalloc2("ABCDEF", 2,3) => "CDE"
181  *
182  *
183  * @param buf input
184  * @param offset
185  * @param len
186  */
187 BYTE *snmalloc2(BYTE *buf, int offset, int len) {
188     /* check */
189     if (buf == NULL) {
190         LOG(LOG_ERR, "null input");
191         return NULL;
192     }
193     if (offset < 0) {
194         LOG(LOG_ERR, "offset < 0");
195         return NULL;
196     }
197     if (len < 0) {
198         LOG(LOG_ERR, "len < 0");
199         return NULL;
200     }
201
202     /* alloc */
203     BYTE *output = (BYTE *) xmalloc(len + 1);
204     if (output == NULL) {
205         return NULL;
206     }
207
208     memcpy((void *) output, (void *) &buf[offset], len);
209     output[len] = 0;
210
211     return output;
212 }
213
214
215 /**
216  * free string buffer
217  */
218 void sfree(char *str) {
219     /* check */
220     if (str == NULL) {
221         LOG(LOG_ERR, "null input");
222         return;
223     }
224
225     /* free*/
226     xfree(str);
227 }
228
229
230
231 /**
232  * get fullpathname of file
233  * This malloc new buf for the fullpathname. 
234  * In config
235  *
236  * basepath must be start from /
237  *  
238  * UnitTest : check_conf
239  *
240  */
241 char *getFullpathName(char *basepath, char *filename) {
242     char *fullpath = NULL;
243     int basepath_len;
244     int filename_len;
245     int slash = 0;
246
247     /* check */
248     if (basepath == NULL) {
249         LOG(LOG_ERR, "null input");
250         return NULL;
251     }
252     if (filename == NULL) {
253         LOG(LOG_ERR, "null input");
254         return NULL;
255     }
256
257     /* start from root */
258     if (filename[0] == '/') {
259         /* seems fullpath, copy the filename to new buf */
260         return smalloc(filename);
261     }
262
263     /* basepath + filename */
264     if (basepath[0] != '/') {
265         /* relative path -> error when it run as daemon */
266         LOG(LOG_TODO, "getFullpathName() - basepath, '%s' is not started from root\n", basepath);
267     }
268
269
270     /*
271       rule
272
273         0x00 /AAA/ +   BBB => /AAA/BBB
274         0x01 /AAA/ + ./BBB => /AAA/BBB
275         0x10 /AAA  +   BBB => /AAA/BBB
276         0x11 /AAA  + ./BBB => /AAA/BBB  
277     */
278     basepath_len = strlen(basepath);
279     filename_len = strlen(filename);
280
281     if (filename_len < 2) {
282         LOG(LOG_ERR, "ilename len < 2\n");
283         return NULL;
284     }
285
286     /* basepath has "/" at end. else add "/" */
287     if (basepath[basepath_len - 1] !=  '/') {
288         slash = 0x10;
289     }
290     /* filename has "./" at start ? remove */
291     if ((filename[0] ==  '.') && (filename[1] ==  '/')) {
292         slash |= 0x01;
293     }
294
295     /* */
296     switch (slash) {
297         case 0x00:
298             /* /AAA/ +   BBB => /AAA/BBB */
299             fullpath = xmalloc_assert(basepath_len + filename_len + 1);
300             memcpy(fullpath, basepath, basepath_len);
301             memcpy(&fullpath[basepath_len], filename, filename_len);
302             fullpath[basepath_len + filename_len] = 0;
303             break;
304         case 0x01:
305             /* /AAA/ + ./BBB => /AAA/BBB */
306             fullpath = xmalloc_assert(basepath_len + filename_len + 1 - 2);
307             memcpy(fullpath, basepath, basepath_len);
308             memcpy(&fullpath[basepath_len], filename + 2, filename_len - 2);
309             fullpath[basepath_len + filename_len - 2] = 0;
310             break;
311         case 0x10:
312             /* /AAA  +   BBB => /AAA/BBB */
313             fullpath = xmalloc_assert(basepath_len + 1 + filename_len + 1);
314             memcpy(fullpath, basepath, basepath_len);
315             fullpath[basepath_len] = '/';
316             memcpy(&fullpath[basepath_len + 1], filename, filename_len);
317             fullpath[basepath_len + filename_len + 1] = 0;
318             break;
319         case 0x11:
320             /* /AAA  + ./BBB => /AAA/BBB */
321             fullpath = xmalloc_assert(basepath_len + 1 + filename_len + 1 - 2);
322             memcpy(fullpath, basepath, basepath_len);
323             fullpath[basepath_len] = '/';
324             memcpy(&fullpath[basepath_len + 1], filename + 2, filename_len - 2);
325             fullpath[basepath_len + filename_len - 1] = 0;
326             break;
327         default:
328             LOG(LOG_ERR, "internal error\n");
329             break;
330     }  // switch
331
332     return fullpath;
333 }
334
335 /**
336  * Get dirname from fullpath filename
337  * 
338  * this malloc new string
339  *
340  * /AAA/BBB/CCC/DDD => /AAA/BBB/CCC/ 
341  *
342  */
343 char *getFullpathDir(char *filename) {
344     char *fullpath = NULL;
345     int filename_len;
346     int i;
347
348     /* check */
349     if (filename == NULL) {
350         LOG(LOG_ERR, "null input");
351         return NULL;
352     }
353
354     filename_len = strlen(filename);
355
356     for (i = filename_len; i > 0; i--) {
357         if (filename[i] == '/') {
358             // slash = &filename[i];
359             break;
360         }
361     }
362
363     fullpath = xmalloc_assert(i+2);  // check/abort
364     memcpy(fullpath, filename, i+1);
365     fullpath[i+1] = 0;
366     return fullpath;
367 }
368
369
370
371 /**
372  * Byte to Uint32 
373  * Little Endian (Intel)
374  */
375 UINT32 byte2uint32(BYTE *b) {
376     UINT32 a = 0;
377
378     if (b == NULL) {
379         LOG(LOG_ERR, "byte2uint32 - NULL");
380         OUTPUT("About to return NULL pointer - cannot continue\n");  // TODO
381         exit(1);
382     }
383
384     a = b[3];
385     a = a << 8;
386     a += b[2];
387     a = a << 8;
388     a += b[1];
389     a = a << 8;
390     a += b[0];
391
392     return a;
393 }
394
395 /**
396  * remove space
397  *
398  * Unit Test
399  * TODO
400  */
401 char * trim(char *str) {
402     size_t strLen;
403     char *start, *end;
404
405     /* check */
406     if (str == NULL) {
407         LOG(LOG_ERR, "null input");
408         return NULL;
409     }
410
411     /* check len */
412     strLen = strlen(str);
413     if (0 == strLen) {
414         return str;
415     }
416
417     start = str;
418     end = str + strLen - 1;
419
420     /* skip space at start */
421     while (*str == ' ') {
422         str++;
423     }
424     start = str;
425
426     /* remove space at tail */
427     // TBD
428     while (*end == ' ') {
429         *end = 0;
430         end--;
431     }
432
433     return start;
434 }
435
436 /**
437  * BYTE* -> Hex string (malloc)
438  * 
439  */
440 char *getHexString(BYTE *bin, int size) {
441     char * buf;
442     char * ptr;
443     int i;
444     int len;
445
446     /* check */
447     if (bin == NULL) {
448         LOG(LOG_ERR, "null input");
449         return NULL;
450     }
451
452     buf = xmalloc_assert(size * 2 + 1);  // check/abort
453     ptr = buf;
454     for (i = 0; i < size; i++) {
455         // len = snprintf(ptr, sizeof(ptr), "%02x", bin[i]);
456         len = snprintf(ptr, 3, "%02x", bin[i]);
457         if (len != 2) {
458             LOG(LOG_ERR, "FATAL");
459             free(buf);
460             return NULL;
461         }
462         ptr += 2;  // len;
463     }
464     ptr[0] = '\0';
465
466     return buf;
467 }
468
469 /**
470  * print Hex string 
471  */
472 void snprintHex(
473     char *outBuf, int outBufLen, char *head, BYTE *data, int num, char *tail) {
474     int outSoFar = 0;
475     int i;
476
477     /* check */
478     if (outBuf == NULL) {
479         LOG(LOG_ERR, "null input");
480         return;
481     }
482     if (head == NULL) {
483         LOG(LOG_ERR, "null input");
484         return;
485     }
486     if (data == NULL) {
487         LOG(LOG_ERR, "null input");
488         return;
489     }
490     if (tail == NULL) {
491         LOG(LOG_ERR, "null input");
492         return;
493     }
494
495     /* */
496     outSoFar += snprintf(outBuf, outBufLen, "%s[%d]=", head, num);
497
498     for (i = 0; i < num; i++) {
499         if ( outSoFar < outBufLen ) {
500             outSoFar += snprintf(&outBuf[outSoFar], outBufLen - outSoFar, "%02X", data[i]);
501         }
502     }
503     if ( outSoFar < outBufLen ) {
504         snprintf(&outBuf[outSoFar], outBufLen - outSoFar, "%s", tail);
505     }
506 }
507
508 void printHex(char *head, BYTE *data, int num, char *tail) {
509     char outBuf[1024];
510
511     snprintHex(outBuf, 1023, head, data, num, tail);
512     /* I could just use OUTPUT(outBuf), but since warnings are errors
513        I have to use this less efficient form */
514     OUTPUT("%s", outBuf);
515 }
516
517 void debugHex(char *head, BYTE *data, int num, char *tail) {
518     char outBuf[1024];
519     snprintHex(outBuf, 1023, head, data, num, tail);
520     writeLog(LOG_DEBUG, outBuf);
521 }
522
523 void fprintHex(FILE *fp, BYTE *data, int num) {
524     int i;
525
526     /* check */
527     if (fp == NULL) {
528         LOG(LOG_ERR, "null input");
529         return;
530     }
531     if (data == NULL) {
532         LOG(LOG_ERR, "null input");
533         return;
534     }
535
536     /* fprintf */
537     for (i = 0; i < num; i++) {
538         fprintf(fp, "%02X", data[i]);
539     }
540 }
541
542 /**
543  *  Convert Endian 
544  */
545 UINT32 b2l(UINT32 in) {
546     UINT32 out;
547
548     out  = in & 0xff;
549     in   = in  >> 8;
550     out  = out << 8;
551     out += in & 0xff;
552     in   = in  >> 8;
553     out  = out << 8;
554     out += in & 0xff;
555     in   = in  >> 8;
556     out  = out << 8;
557     out += in & 0xff;
558
559     return out;
560 }
561
562 /**
563  * save to file
564  *
565  * @param filename output filename
566  * @param len message length
567  * @param msg message
568  *
569  *
570  */
571 int saveToFile(
572     char *filename, int len, BYTE * msg) {
573     FILE *fp;
574     const int max_retries = 10;
575     int n_tries = 0;
576     int ptr = 0;
577
578     /* check */
579     if (len < 0) {
580         LOG(LOG_ERR, "len <0 \n");
581         return PTS_FATAL;
582     }
583     if (msg == NULL) {
584         LOG(LOG_ERR, "msg is NULL \n");
585         return PTS_FATAL;
586     }
587     if (filename == NULL) {
588         LOG(LOG_ERR, "filename is NULL \n");
589         return PTS_FATAL;
590     }
591
592     if ((fp = fopen(filename, "w+b")) == NULL) {
593         LOG(LOG_ERR, "File open failed, %s \n", filename);
594         return PTS_FATAL;  // TODO(munetoh): set PTS error code.
595     }
596
597     /* If the filesystem is full, we shouldn't hang whilst trying to
598        write the file to disk -> we only allow so many attempts. */
599     while (n_tries < max_retries) {
600         int bytes_written = fwrite(&msg[ptr], 1, len, fp);
601         /* DEBUG_IFM(" %s %d %d\n", filename, rc, len); */
602         ptr += bytes_written;
603         len -= bytes_written;
604         n_tries++;
605         if (len <= 0) {
606             break;
607         }
608     }
609
610     /* DEBUG_IFM(" %s %d \n", filename, len); */
611
612     fclose(fp);
613
614     if (len > 0) {
615         LOG(LOG_ERR, "After %d retries still have %d bytes unwritten to '%s'\n", max_retries, len, filename);
616         return PTS_FATAL;
617     } else {
618         return PTS_SUCCESS;
619     }
620 }
621
622 /**
623  * byte[4] => UINT32
624  */
625 UINT32 getUint32(BYTE *buf) {
626     UINT32 data;
627
628     /* check */
629     if (buf == NULL) {
630         LOG(LOG_ERR, "null input");
631         return 0;  // TODO
632     }
633     // TODO check the size?
634
635     data = (buf[0] << 24) |
636            (buf[1] << 16) |
637            (buf[2] << 8)  |
638             buf[3];
639
640     return data;
641 }
642
643 /**
644  * make Dir 
645  *
646  */
647 int makeDir(char *dirname) {
648     int rc = PTS_SUCCESS;
649     struct stat st;
650
651     /* check */
652     if (dirname == NULL) {
653         LOG(LOG_ERR, "null input");
654         return PTS_FATAL;
655     }
656
657     /* create anyway */
658     rc = mkdir(dirname, S_IRUSR | S_IWUSR | S_IXUSR |
659                         S_IRGRP | S_IWGRP | S_IXGRP);
660     if (rc != 0) {
661         switch (errno) {
662         case EACCES:
663             LOG(LOG_ERR, "mkdir %s failed, EACCES", dirname);
664             rc = PTS_FATAL;
665             break;
666         case EEXIST:
667             /* already exist */
668             rc = lstat(dirname, &st);
669             if (rc == 0) {
670                 if ((st.st_mode & S_IFMT) != S_IFDIR) {
671                     LOG(LOG_ERR, "directory, %s is not a directory %x %x\n",
672                         dirname, (st.st_mode & S_IFMT), S_IFDIR);
673                     rc = PTS_INTERNAL_ERROR;
674                 } else {
675                     // OK
676                     rc = PTS_SUCCESS;
677                 }
678             } else {
679                 LOG(LOG_ERR, "lstat(%s) failed, errno=%d\n", dirname, errno);
680                 rc = PTS_FATAL;
681             }
682             break;
683         case EFAULT:
684             LOG(LOG_ERR, "mkdir %s failed, EFAULT", dirname);
685             rc = PTS_FATAL;
686             break;
687         // TODO add others :-)
688         default:
689             LOG(LOG_ERR, "mkdir %s failed, errono = 0x%X", dirname, errno);
690             rc = PTS_FATAL;
691             break;
692         }
693     }
694
695     return rc;
696 }
697
698 /**
699  * check Dir 
700  *
701  * Return
702  *   PTS_SUCCESS           - exist
703  *   PTS_INTERNAL_ERROR    - not exist or not a dir
704  *
705  */
706 int checkDir(char *dirname) {
707     struct stat st;
708
709     /* check */
710     if (dirname == NULL) {
711         LOG(LOG_ERR, "null input");
712         return PTS_FATAL;
713     }
714
715     if (lstat(dirname, &st) == -1) {
716         /* Missing dir */
717         return PTS_INTERNAL_ERROR;  // TODO OPENPTS_DIR_MISSING;
718     } else if ((st.st_mode & S_IFMT) != S_IFDIR) {
719         /* not DIR */
720         return PTS_INTERNAL_ERROR;
721     }
722
723     return PTS_SUCCESS;  // TODO OPENPTS_DIR_EXIST;
724 }
725
726 /**
727  * Check file (reguler file)
728  */
729 int checkFile(char *filename) {
730     struct stat st;
731
732     /* check */
733     if (filename == NULL) {
734         LOG(LOG_ERR, "null input");
735         return PTS_FATAL;
736     }
737
738     if (lstat(filename, &st) == -1) {
739         /* Missing dir */
740         return OPENPTS_FILE_MISSING;
741     } else if ((st.st_mode & S_IFMT) != S_IFREG) {
742         /* not FILE */
743         return PTS_INTERNAL_ERROR;
744     }
745
746     return OPENPTS_FILE_EXISTS;
747 }
748
749 /**
750  * wrap read()
751  */
752 ssize_t wrapRead(int fd, void *buf, size_t count) {
753     ssize_t len;
754
755     /* check */
756     if (buf == NULL) {
757         LOG(LOG_ERR, "null input");
758         return 0;  // TODO
759     }
760
761     while (1) {
762         len = read(fd, buf, count);
763         if ((len < 0) && (errno == EAGAIN || errno == EINTR)) {
764             continue;
765         }
766         return len;
767     }
768 }
769
770 /**
771  * wrap write()
772  */
773 ssize_t wrapWrite(int fd, const void *buf, size_t count) {
774     ssize_t len;
775
776     /* check */
777     if (buf == NULL) {
778         LOG(LOG_ERR, "null input");
779         return 0;  // TODO
780     }
781
782     while (1) {
783         len = write(fd, buf, count);
784         if ((len < 0) && (errno == EAGAIN || errno == EINTR)) {
785             continue;
786         }
787         return len;
788     }
789 }
790
791 /**
792  * recursive part of unlinkDir()
793  */
794 static int unlinkDir_(char *dirPath) {
795     DIR *dirHandle;
796     struct dirent *entry;
797     char path[PATH_MAX + 1];
798     struct dirent dr;
799     int rc;
800
801     /* check */
802     if (dirPath == NULL) {
803         LOG(LOG_ERR, "null input");
804         return PTS_FATAL;
805     }
806
807     dirHandle = opendir(dirPath);
808     if (dirHandle == NULL) {
809         LOG(LOG_ERR, "opendir(%s) fail", dirPath);
810         return PTS_FATAL;
811     }
812
813     while (1) {
814         struct stat st;
815
816         rc = readdir_r(dirHandle, &dr, &entry);
817         if (rc != 0) break;
818         if (entry == NULL) break;
819
820         if (strcmp(".", entry->d_name) == 0) continue;
821         if (strcmp("..", entry->d_name) == 0) continue;
822
823         snprintf(path, sizeof(path), "%s/%s", dirPath, entry->d_name);
824         if (stat(path, &st) != 0) {
825             LOG(LOG_ERR, "stat(%s) fail", path);
826             rc = PTS_FATAL;
827             goto free_error;
828         }
829
830         if (S_ISDIR(st.st_mode)) {
831             if (unlinkDir_(path) != 0) {
832                 rc = PTS_FATAL;
833                 goto free_error;
834             }
835         } else if (S_ISREG(st.st_mode)) {
836             if (unlink(path) != 0) {
837                 LOG(LOG_ERR, "unlink(%s) fail", path);
838                 rc = PTS_FATAL;
839                 goto free_error;
840             }
841         }
842     }
843
844     /* rm this dir */
845     if (rmdir(dirPath) != 0) {
846         LOG(LOG_ERR, "rmdir(%s) fail", dirPath);
847         rc = PTS_FATAL;
848         goto free_error;
849     }
850
851     rc = PTS_SUCCESS;
852
853   free_error:
854     closedir(dirHandle);
855
856     return rc;
857 }
858
859 /**
860  * Recursively destroy the content of a directory
861  */
862 int unlinkDir(const char *dirPath) {
863     char path[PATH_MAX + 1];
864
865     /* check */
866     if (dirPath == NULL) {
867         LOG(LOG_ERR, "null input");
868         return PTS_FATAL;
869     }
870     if (dirPath[0] == '\0' || strlen(dirPath) >= PATH_MAX) {
871         LOG(LOG_ERR, "bad dirPath, %s", dirPath);
872         return PTS_FATAL;
873     }
874
875     strncpy(path, dirPath, sizeof(path));
876     // there is at least one byte free before path[PATH_MAX]
877
878     return unlinkDir_(path);
879 }
880
881
882