add libvncserver
[presencevnc] / libvnc / libvncserver / tightvnc-filetransfer / filetransfermsg.c
1 /*
2  * Copyright (c) 2005 Novell, Inc.
3  * All Rights Reserved.
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of version 2 of the GNU General Public License as
7  * published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, contact Novell, Inc.
16  *
17  * To contact Novell about this file by physical or electronic mail,
18  * you may find current contact information at www.novell.com 
19  *
20  * Author               : Rohit Kumar
21  * Email ID     : rokumar@novell.com
22  * Date         : 14th July 2005
23  */
24  
25
26 #include <stdio.h>
27 #include <string.h>
28 #include <stdlib.h>
29 #include <fcntl.h>
30 #include <dirent.h>
31 #include <utime.h>
32 #include <errno.h>
33 #include <unistd.h>
34 #include <sys/stat.h>
35 #include <sys/types.h>
36
37 #include <rfb/rfb.h>
38 #include "rfbtightproto.h"
39 #include "filelistinfo.h"
40 #include "filetransfermsg.h"
41 #include "handlefiletransferrequest.h"
42
43 #define SZ_RFBBLOCKSIZE 8192
44
45
46 void
47 FreeFileTransferMsg(FileTransferMsg ftm)
48 {
49
50         if(ftm.data != NULL) {
51                 free(ftm.data);
52                 ftm.data = NULL;
53         }
54
55         ftm.length = 0;
56
57 }
58
59
60 /******************************************************************************
61  * Methods to handle file list request.
62  ******************************************************************************/
63
64 int CreateFileListInfo(FileListInfoPtr pFileListInfo, char* path, int flag);
65 FileTransferMsg CreateFileListErrMsg(char flags);
66 FileTransferMsg CreateFileListMsg(FileListInfo fileListInfo, char flags);
67
68
69 /*
70  * This is the method called by HandleFileListRequest to get the file list
71  */
72
73 FileTransferMsg 
74 GetFileListResponseMsg(char* path, char flags)
75 {
76         FileTransferMsg fileListMsg;
77         FileListInfo fileListInfo;
78         int status = -1;
79         
80         memset(&fileListMsg, 0, sizeof(FileTransferMsg));
81         memset(&fileListInfo, 0, sizeof(FileListInfo));
82
83         
84          /* fileListInfo can have null data if the folder is Empty 
85         or if some error condition has occured.
86         The return value is 'failure' only if some error condition has occured.
87          */
88         status = CreateFileListInfo(&fileListInfo, path, !(flags  & 0x10));
89
90         if(status == FAILURE) {
91                 fileListMsg = CreateFileListErrMsg(flags);
92         }
93         else {
94                 /* DisplayFileList(fileListInfo); For Debugging  */
95                 
96                 fileListMsg = CreateFileListMsg(fileListInfo, flags);
97                 FreeFileListInfo(fileListInfo);
98         }
99         
100         return fileListMsg;
101 }
102
103 #ifndef __GNUC__
104 #define __FUNCTION__ "unknown"
105 #endif
106
107 int
108 CreateFileListInfo(FileListInfoPtr pFileListInfo, char* path, int flag)
109 {
110         DIR* pDir = NULL;
111         struct dirent* pDirent = NULL;
112         
113         if((path == NULL) || (strlen(path) == 0)) {
114                 /* In this case we will send the list of entries in ftp root*/
115                 sprintf(path, "%s%s", GetFtpRoot(), "/");
116         }
117
118         if((pDir = opendir(path)) == NULL) {
119                 rfbLog("File [%s]: Method [%s]: not able to open the dir\n",
120                                 __FILE__, __FUNCTION__);
121                 return FAILURE;                 
122         }
123
124         while((pDirent = readdir(pDir))) {
125                 if(strcmp(pDirent->d_name, ".") && strcmp(pDirent->d_name, "..")) {
126                         struct stat stat_buf;
127                         /*
128                         int fpLen = sizeof(char)*(strlen(pDirent->d_name)+strlen(path)+2);
129                         */
130                         char fullpath[PATH_MAX];
131
132                         memset(fullpath, 0, PATH_MAX);
133
134                         strcpy(fullpath, path);
135                         if(path[strlen(path)-1] != '/')
136                                 strcat(fullpath, "/");
137                         strcat(fullpath, pDirent->d_name);
138
139                         if(stat(fullpath, &stat_buf) < 0) {
140                                 rfbLog("File [%s]: Method [%s]: Reading stat for file %s failed\n", 
141                                                 __FILE__, __FUNCTION__, fullpath);
142                                 continue;
143                         }
144
145                         if(S_ISDIR(stat_buf.st_mode)) {
146                                 if(AddFileListItemInfo(pFileListInfo, pDirent->d_name, -1, 0) == 0) {
147                                         rfbLog("File [%s]: Method [%s]: Add directory %s in the"
148                                                         " list failed\n", __FILE__, __FUNCTION__, fullpath);
149                                         continue;
150                                 }
151                         }
152                         else {
153                                 if(flag) {
154                                         if(AddFileListItemInfo(pFileListInfo, pDirent->d_name, 
155                                                                                                 stat_buf.st_size, 
156                                                                                                 stat_buf.st_mtime) == 0) {
157                                                 rfbLog("File [%s]: Method [%s]: Add file %s in the "
158                                                                 "list failed\n", __FILE__, __FUNCTION__, fullpath);
159                                                 continue;
160                                         }                       
161                                 }
162                         }
163                 }
164         }
165         if(closedir(pDir) < 0) {
166             rfbLog("File [%s]: Method [%s]: ERROR Couldn't close dir\n",
167                 __FILE__, __FUNCTION__);
168         }
169         
170         return SUCCESS;
171 }
172
173
174 FileTransferMsg
175 CreateFileListErrMsg(char flags)
176 {
177         FileTransferMsg fileListMsg;
178         rfbFileListDataMsg* pFLD = NULL;
179         char* data = NULL;
180         unsigned int length = 0;
181
182         memset(&fileListMsg, 0, sizeof(FileTransferMsg));
183
184         data = (char*) calloc(sizeof(rfbFileListDataMsg), sizeof(char));
185         if(data == NULL) {
186                 return fileListMsg;
187         }
188         length = sizeof(rfbFileListDataMsg) * sizeof(char);
189         pFLD = (rfbFileListDataMsg*) data;
190         
191         pFLD->type = rfbFileListData;
192         pFLD->numFiles = Swap16IfLE(0);
193         pFLD->dataSize = Swap16IfLE(0);
194         pFLD->compressedSize = Swap16IfLE(0);
195         pFLD->flags = flags | 0x80;
196
197         fileListMsg.data = data;
198         fileListMsg.length = length;
199
200         return fileListMsg;
201 }
202
203
204 FileTransferMsg
205 CreateFileListMsg(FileListInfo fileListInfo, char flags)
206 {
207         FileTransferMsg fileListMsg;
208         rfbFileListDataMsg* pFLD = NULL;
209         char *data = NULL, *pFileNames = NULL;
210         unsigned int length = 0, dsSize = 0, i = 0;
211         FileListItemSizePtr pFileListItemSize = NULL;
212
213         memset(&fileListMsg, 0, sizeof(FileTransferMsg));
214         dsSize = fileListInfo.numEntries * 8;
215         length = sz_rfbFileListDataMsg + dsSize + 
216                         GetSumOfFileNamesLength(fileListInfo) + 
217                         fileListInfo.numEntries;
218
219         data = (char*) calloc(length, sizeof(char));
220         if(data == NULL) {
221                 return fileListMsg;
222         }
223         pFLD = (rfbFileListDataMsg*) data;
224         pFileListItemSize = (FileListItemSizePtr) &data[sz_rfbFileListDataMsg];
225         pFileNames = &data[sz_rfbFileListDataMsg + dsSize];
226
227         pFLD->type            = rfbFileListData;
228     pFLD->flags                   = flags & 0xF0;
229     pFLD->numFiles                = Swap16IfLE(fileListInfo.numEntries);
230     pFLD->dataSize                = Swap16IfLE(GetSumOfFileNamesLength(fileListInfo) + 
231                                                                         fileListInfo.numEntries);
232     pFLD->compressedSize  = pFLD->dataSize;
233
234         for(i =0; i <fileListInfo.numEntries; i++) {
235                 pFileListItemSize[i].size = Swap32IfLE(GetFileSizeAt(fileListInfo, i));
236                 pFileListItemSize[i].data = Swap32IfLE(GetFileDataAt(fileListInfo, i));
237                 strcpy(pFileNames, GetFileNameAt(fileListInfo, i));
238                 
239                 if(i+1 < fileListInfo.numEntries)
240                         pFileNames += strlen(pFileNames) + 1;
241         }
242
243         fileListMsg.data        = data;
244         fileListMsg.length      = length;
245
246         return fileListMsg;
247 }
248
249
250 /******************************************************************************
251  * Methods to handle File Download Request.
252  ******************************************************************************/
253
254 FileTransferMsg CreateFileDownloadErrMsg(char* reason, unsigned int reasonLen);
255 FileTransferMsg CreateFileDownloadZeroSizeDataMsg(unsigned long mTime);
256 FileTransferMsg CreateFileDownloadBlockSizeDataMsg(unsigned short sizeFile, char *pFile);
257
258 FileTransferMsg 
259 GetFileDownLoadErrMsg()
260 {
261         FileTransferMsg fileDownloadErrMsg;
262
263         char reason[] = "An internal error on the server caused download failure";
264         int reasonLen = strlen(reason);
265
266         memset(&fileDownloadErrMsg, 0, sizeof(FileTransferMsg));
267         
268         fileDownloadErrMsg = CreateFileDownloadErrMsg(reason, reasonLen);
269
270         return fileDownloadErrMsg;
271 }
272
273
274 FileTransferMsg
275 GetFileDownloadReadDataErrMsg()
276 {
277         char reason[] = "Cannot open file, perhaps it is absent or is a directory";
278         int reasonLen = strlen(reason);
279
280         return CreateFileDownloadErrMsg(reason, reasonLen);
281
282 }
283
284
285 FileTransferMsg
286 GetFileDownloadLengthErrResponseMsg()
287 {
288         char reason [] = "Path length exceeds PATH_MAX (4096) bytes";
289         int reasonLen = strlen(reason);
290
291         return CreateFileDownloadErrMsg(reason, reasonLen);
292 }
293
294
295 FileTransferMsg
296 GetFileDownloadResponseMsgInBlocks(rfbClientPtr cl, rfbTightClientPtr rtcp)
297 {
298         /* const unsigned int sz_rfbBlockSize = SZ_RFBBLOCKSIZE; */
299     int numOfBytesRead = 0;
300         char pBuf[SZ_RFBBLOCKSIZE];
301         char* path = rtcp->rcft.rcfd.fName;
302
303         memset(pBuf, 0, SZ_RFBBLOCKSIZE);
304
305         if((rtcp->rcft.rcfd.downloadInProgress == FALSE) && (rtcp->rcft.rcfd.downloadFD == -1)) {
306                 if((rtcp->rcft.rcfd.downloadFD = open(path, O_RDONLY)) == -1) {
307                         rfbLog("File [%s]: Method [%s]: Error: Couldn't open file\n", 
308                                         __FILE__, __FUNCTION__);
309                         return GetFileDownloadReadDataErrMsg();
310                 }
311                 rtcp->rcft.rcfd.downloadInProgress = TRUE;
312         }
313         if((rtcp->rcft.rcfd.downloadInProgress == TRUE) && (rtcp->rcft.rcfd.downloadFD != -1)) {
314                 if( (numOfBytesRead = read(rtcp->rcft.rcfd.downloadFD, pBuf, SZ_RFBBLOCKSIZE)) <= 0) {
315                         close(rtcp->rcft.rcfd.downloadFD);
316                         rtcp->rcft.rcfd.downloadFD = -1;
317                         rtcp->rcft.rcfd.downloadInProgress = FALSE;
318                         if(numOfBytesRead == 0) {
319                                 return CreateFileDownloadZeroSizeDataMsg(rtcp->rcft.rcfd.mTime);
320                         }                       
321                         return GetFileDownloadReadDataErrMsg();
322                 }
323         return CreateFileDownloadBlockSizeDataMsg(numOfBytesRead, pBuf);
324         }
325         return GetFileDownLoadErrMsg();
326 }
327
328
329 FileTransferMsg
330 ChkFileDownloadErr(rfbClientPtr cl, rfbTightClientPtr rtcp)
331 {
332     FileTransferMsg fileDownloadMsg;
333         struct stat stat_buf;
334         int sz_rfbFileSize = 0;
335         char* path = rtcp->rcft.rcfd.fName;
336
337         memset(&fileDownloadMsg, 0, sizeof(FileTransferMsg));
338
339         if( (path == NULL) || (strlen(path) == 0) ||
340                 (stat(path, &stat_buf) < 0) || (!(S_ISREG(stat_buf.st_mode))) ) {
341
342                         char reason[] = "Cannot open file, perhaps it is absent or is not a regular file";
343                         int reasonLen = strlen(reason);
344
345                         rfbLog("File [%s]: Method [%s]: Reading stat for path %s failed\n", 
346                                         __FILE__, __FUNCTION__, path);  
347                         
348                         fileDownloadMsg = CreateFileDownloadErrMsg(reason, reasonLen);
349         }
350         else {
351                 rtcp->rcft.rcfd.mTime = stat_buf.st_mtime;
352                 sz_rfbFileSize = stat_buf.st_size;
353                 if(sz_rfbFileSize <= 0) {
354                         fileDownloadMsg = CreateFileDownloadZeroSizeDataMsg(stat_buf.st_mtime);
355                 }
356
357         }
358         return fileDownloadMsg;
359 }
360
361
362 FileTransferMsg
363 CreateFileDownloadErrMsg(char* reason, unsigned int reasonLen)
364 {
365         FileTransferMsg fileDownloadErrMsg;
366         int length = sz_rfbFileDownloadFailedMsg + reasonLen + 1;
367         rfbFileDownloadFailedMsg *pFDF = NULL;
368         char *pFollow = NULL;
369         
370         char *pData = (char*) calloc(length, sizeof(char));
371         memset(&fileDownloadErrMsg, 0, sizeof(FileTransferMsg));
372         if(pData == NULL) {
373                 rfbLog("File [%s]: Method [%s]: pData is NULL\n",
374                                 __FILE__, __FUNCTION__);        
375                 return fileDownloadErrMsg;
376         }
377
378         pFDF = (rfbFileDownloadFailedMsg *) pData;
379         pFollow = &pData[sz_rfbFileDownloadFailedMsg];
380         
381         pFDF->type = rfbFileDownloadFailed;
382         pFDF->reasonLen = Swap16IfLE(reasonLen);
383         memcpy(pFollow, reason, reasonLen);
384
385         fileDownloadErrMsg.data = pData;
386         fileDownloadErrMsg.length       = length;
387
388         return fileDownloadErrMsg;
389 }
390
391
392 FileTransferMsg
393 CreateFileDownloadZeroSizeDataMsg(unsigned long mTime)
394 {
395         FileTransferMsg fileDownloadZeroSizeDataMsg;
396         int length = sz_rfbFileDownloadDataMsg + sizeof(int);
397         rfbFileDownloadDataMsg *pFDD = NULL;
398         char *pFollow = NULL;
399         
400         char *pData = (char*) calloc(length, sizeof(char));
401         memset(&fileDownloadZeroSizeDataMsg, 0, sizeof(FileTransferMsg));
402         if(pData == NULL) {
403                 rfbLog("File [%s]: Method [%s]: pData is NULL\n",
404                                 __FILE__, __FUNCTION__);        
405                 return fileDownloadZeroSizeDataMsg;
406         }
407
408         pFDD = (rfbFileDownloadDataMsg *) pData;
409         pFollow = &pData[sz_rfbFileDownloadDataMsg];
410         
411         pFDD->type = rfbFileDownloadData;
412         pFDD->compressLevel = 0;
413         pFDD->compressedSize = Swap16IfLE(0);
414         pFDD->realSize = Swap16IfLE(0);
415         
416         memcpy(pFollow, &mTime, sizeof(unsigned long));
417
418         fileDownloadZeroSizeDataMsg.data        = pData;
419         fileDownloadZeroSizeDataMsg.length      = length;
420
421         return fileDownloadZeroSizeDataMsg;
422
423 }
424
425
426 FileTransferMsg
427 CreateFileDownloadBlockSizeDataMsg(unsigned short sizeFile, char *pFile)
428 {
429         FileTransferMsg fileDownloadBlockSizeDataMsg;
430         int length = sz_rfbFileDownloadDataMsg + sizeFile;
431         rfbFileDownloadDataMsg *pFDD = NULL;
432         char *pFollow = NULL;
433         
434         char *pData = (char*) calloc(length, sizeof(char));
435         memset(&fileDownloadBlockSizeDataMsg, 0, sizeof(FileTransferMsg));
436         if(NULL == pData) {
437                 rfbLog("File [%s]: Method [%s]: pData is NULL\n",
438                                 __FILE__, __FUNCTION__);        
439                 return fileDownloadBlockSizeDataMsg;
440         }
441
442         pFDD = (rfbFileDownloadDataMsg *) pData;
443         pFollow = &pData[sz_rfbFileDownloadDataMsg];
444         
445         pFDD->type = rfbFileDownloadData;
446         pFDD->compressLevel = 0;
447         pFDD->compressedSize = Swap16IfLE(sizeFile);
448         pFDD->realSize = Swap16IfLE(sizeFile);
449         
450         memcpy(pFollow, pFile, sizeFile);
451
452         fileDownloadBlockSizeDataMsg.data       = pData;
453         fileDownloadBlockSizeDataMsg.length     = length;
454
455         return fileDownloadBlockSizeDataMsg;
456
457 }
458
459
460 /******************************************************************************
461  * Methods to handle file upload request
462  ******************************************************************************/
463
464 FileTransferMsg CreateFileUploadErrMsg(char* reason, unsigned int reasonLen);
465
466 FileTransferMsg 
467 GetFileUploadLengthErrResponseMsg()
468 {
469         char reason [] = "Path length exceeds PATH_MAX (4096) bytes";
470         int reasonLen = strlen(reason);
471
472         return CreateFileUploadErrMsg(reason, reasonLen);
473 }
474
475
476 FileTransferMsg
477 ChkFileUploadErr(rfbClientPtr cl, rfbTightClientPtr rtcp)
478 {
479     FileTransferMsg fileUploadErrMsg;
480
481         memset(&fileUploadErrMsg, 0, sizeof(FileTransferMsg));
482         if( (rtcp->rcft.rcfu.fName == NULL) ||
483                 (strlen(rtcp->rcft.rcfu.fName) == 0) ||
484                 ((rtcp->rcft.rcfu.uploadFD = creat(rtcp->rcft.rcfu.fName, 
485                 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)) == -1)) {
486
487                         char reason[] = "Could not create file";
488                         int reasonLen = strlen(reason);
489                         fileUploadErrMsg = CreateFileUploadErrMsg(reason, reasonLen);
490         }
491         else
492                 rtcp->rcft.rcfu.uploadInProgress = TRUE;
493         
494         return fileUploadErrMsg;
495 }
496
497
498 FileTransferMsg
499 GetFileUploadCompressedLevelErrMsg()
500 {
501         char reason[] = "Server does not support data compression on upload";
502         int reasonLen = strlen(reason);
503
504         return CreateFileUploadErrMsg(reason, reasonLen);
505 }
506
507
508 FileTransferMsg
509 ChkFileUploadWriteErr(rfbClientPtr cl, rfbTightClientPtr rtcp, char* pBuf)
510 {
511         FileTransferMsg ftm;
512         unsigned long numOfBytesWritten = 0;
513
514         memset(&ftm, 0, sizeof(FileTransferMsg));
515
516         numOfBytesWritten = write(rtcp->rcft.rcfu.uploadFD, pBuf, rtcp->rcft.rcfu.fSize);
517
518         if(numOfBytesWritten != rtcp->rcft.rcfu.fSize) {                
519                 char reason[] = "Error writing file data";
520                 int reasonLen = strlen(reason);
521                 ftm = CreateFileUploadErrMsg(reason, reasonLen);
522                 CloseUndoneFileTransfer(cl, rtcp);
523         }               
524         return ftm;
525 }
526
527
528 void
529 FileUpdateComplete(rfbClientPtr cl, rfbTightClientPtr rtcp)
530 {
531         /* Here we are settimg the modification and access time of the file */
532         /* Windows code stes mod/access/creation time of the file */
533         struct utimbuf utb;
534
535         utb.actime = utb.modtime = rtcp->rcft.rcfu.mTime;
536         if(utime(rtcp->rcft.rcfu.fName, &utb) == -1) {
537                 rfbLog("File [%s]: Method [%s]: Setting the modification/access"
538                                 " time for the file <%s> failed\n", __FILE__, 
539                                 __FUNCTION__, rtcp->rcft.rcfu.fName);
540         }
541
542         if(rtcp->rcft.rcfu.uploadFD != -1) {
543                 close(rtcp->rcft.rcfu.uploadFD);
544                 rtcp->rcft.rcfu.uploadFD = -1;
545                 rtcp->rcft.rcfu.uploadInProgress = FALSE;
546         }
547 }
548
549
550 FileTransferMsg
551 CreateFileUploadErrMsg(char* reason, unsigned int reasonLen)
552 {
553         FileTransferMsg fileUploadErrMsg;
554         int length = sz_rfbFileUploadCancelMsg + reasonLen;
555         rfbFileUploadCancelMsg *pFDF = NULL;
556         char *pFollow = NULL;
557         
558         char *pData = (char*) calloc(length, sizeof(char));
559         memset(&fileUploadErrMsg, 0, sizeof(FileTransferMsg));
560         if(pData == NULL) {
561                 rfbLog("File [%s]: Method [%s]: pData is NULL\n",
562                                 __FILE__, __FUNCTION__);        
563                 return fileUploadErrMsg;
564         }
565
566         pFDF = (rfbFileUploadCancelMsg *) pData;
567         pFollow = &pData[sz_rfbFileUploadCancelMsg];
568         
569         pFDF->type = rfbFileUploadCancel;
570         pFDF->reasonLen = Swap16IfLE(reasonLen);
571         memcpy(pFollow, reason, reasonLen);
572
573         fileUploadErrMsg.data           = pData;
574         fileUploadErrMsg.length         = length;
575
576         return fileUploadErrMsg;
577 }
578
579
580 /******************************************************************************
581  * Method to cancel File Transfer operation.
582  ******************************************************************************/
583
584 void
585 CloseUndoneFileTransfer(rfbClientPtr cl, rfbTightClientPtr rtcp)
586 {
587         /* TODO :: File Upload case is not handled currently */
588         /* TODO :: In case of concurrency we need to use Critical Section */
589
590         if(cl == NULL)
591                 return;
592
593         
594         if(rtcp->rcft.rcfu.uploadInProgress == TRUE) {
595                 rtcp->rcft.rcfu.uploadInProgress = FALSE;
596
597                 if(rtcp->rcft.rcfu.uploadFD != -1) {
598                         close(rtcp->rcft.rcfu.uploadFD);
599                         rtcp->rcft.rcfu.uploadFD = -1;
600                 }
601
602                 if(unlink(rtcp->rcft.rcfu.fName) == -1) {
603                         rfbLog("File [%s]: Method [%s]: Delete operation on file <%s> failed\n", 
604                                         __FILE__, __FUNCTION__, rtcp->rcft.rcfu.fName);
605                 }
606
607                 memset(rtcp->rcft.rcfu.fName, 0 , PATH_MAX);
608         }
609         
610         if(rtcp->rcft.rcfd.downloadInProgress == TRUE) {
611                 rtcp->rcft.rcfd.downloadInProgress = FALSE;
612
613                 if(rtcp->rcft.rcfd.downloadFD != -1) {                  
614                         close(rtcp->rcft.rcfd.downloadFD);
615                         rtcp->rcft.rcfd.downloadFD = -1;
616                 }
617                 memset(rtcp->rcft.rcfd.fName, 0 , PATH_MAX);
618         }
619 }
620
621
622 /******************************************************************************
623  * Method to handle create directory request.
624  ******************************************************************************/
625
626 void
627 CreateDirectory(char* dirName)
628 {
629         if(dirName == NULL) return;
630
631         if(mkdir(dirName, 0700) == -1) {
632                 rfbLog("File [%s]: Method [%s]: Create operation for directory <%s> failed\n", 
633                                 __FILE__, __FUNCTION__, dirName);
634         }
635 }
636