3462a3b9fa57ff03324b838019e0104a50aea479
[tprouty/samba.git] / source / smbd / fileio.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 1.9.
4    read/write to a files_struct
5    Copyright (C) Andrew Tridgell 1992-1998
6    Copyright (C) Jeremy Allison 2000-2002. - write cache.
7    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #include "includes.h"
24
25 static BOOL setup_write_cache(files_struct *, SMB_OFF_T);
26
27 /****************************************************************************
28  Read from write cache if we can.
29 ****************************************************************************/
30
31
32 static BOOL read_from_write_cache(files_struct *fsp,char *data,SMB_OFF_T pos,size_t n)
33 {
34         write_cache *wcp = fsp->wcp;
35
36         if(!wcp)
37                 return False;
38
39         if(n > wcp->data_size || pos < wcp->offset || pos + n > wcp->offset + wcp->data_size)
40                 return False;
41
42         memcpy(data, wcp->data + (pos - wcp->offset), n);
43
44         DO_PROFILE_INC(writecache_read_hits);
45
46         return True;
47 }
48
49 /****************************************************************************
50  Read from a file.
51 ****************************************************************************/
52
53 ssize_t read_file(files_struct *fsp,char *data,SMB_OFF_T pos,size_t n)
54 {
55         ssize_t ret=0,readret;
56
57         /* you can't read from print files */
58         if (fsp->print_file)
59                 return -1;
60
61         /*
62          * Serve from write cache if we can.
63          */
64
65         if(read_from_write_cache(fsp, data, pos, n)) {
66                 fsp->pos = pos + n;
67                 fsp->position_information = fsp->pos;
68                 return n;
69         }
70
71         flush_write_cache(fsp, READ_FLUSH);
72
73         fsp->pos = pos;
74
75         if (n > 0) {
76 #ifdef DMF_FIX
77                 int numretries = 3;
78 tryagain:
79                 readret = SMB_VFS_PREAD(fsp,fsp->fd,data,n,pos);
80
81                 if (readret == -1) {
82                         if ((errno == EAGAIN) && numretries) {
83                                 DEBUG(3,("read_file EAGAIN retry in 10 seconds\n"));
84                                 (void)sleep(10);
85                                 --numretries;
86                                 goto tryagain;
87                         }
88                         return -1;
89                 }
90 #else /* NO DMF fix. */
91                 readret = SMB_VFS_PREAD(fsp,fsp->fd,data,n,pos);
92
93                 if (readret == -1)
94                         return -1;
95 #endif
96                 if (readret > 0)
97                         ret += readret;
98         }
99
100         DEBUG(10,("read_file (%s): pos = %.0f, size = %lu, returned %lu\n",
101                 fsp->fsp_name, (double)pos, (unsigned long)n, (long)ret ));
102
103         fsp->pos += ret;
104         fsp->position_information = fsp->pos;
105
106         return(ret);
107 }
108
109 /* how many write cache buffers have been allocated */
110 static unsigned int allocated_write_caches;
111
112 /****************************************************************************
113  *Really* write to a file.
114 ****************************************************************************/
115
116 static ssize_t real_write_file(files_struct *fsp,char *data,SMB_OFF_T pos, size_t n)
117 {
118         ssize_t ret;
119
120         if (pos == -1)
121                 ret = vfs_write_data(fsp, data, n);
122         else {
123                 fsp->pos = pos;
124                 ret = vfs_pwrite_data(fsp, data, n, pos);
125         }
126
127         DEBUG(10,("real_write_file (%s): pos = %.0f, size = %lu, returned %ld\n",
128                 fsp->fsp_name, (double)pos, (unsigned long)n, (long)ret ));
129
130         if (ret != -1) {
131                 fsp->pos += ret;
132
133 /* Yes - this is correct - writes don't update this. JRA. */
134 /* Found by Samba4 tests. */
135 #if 0
136                 fsp->position_information = fsp->pos;
137 #endif
138         }
139
140         return ret;
141 }
142
143 /****************************************************************************
144 write to a file
145 ****************************************************************************/
146
147 ssize_t write_file(files_struct *fsp, char *data, SMB_OFF_T pos, size_t n)
148 {
149         write_cache *wcp = fsp->wcp;
150         ssize_t total_written = 0;
151         int write_path = -1; 
152
153         if (fsp->print_file) {
154                 int snum;
155                 uint32 jobid;
156
157                 if (!rap_to_pjobid(fsp->rap_print_jobid, &snum, &jobid)) {
158                         DEBUG(3,("write_file: Unable to map RAP jobid %u to jobid.\n",
159                                                 (unsigned int)fsp->rap_print_jobid ));
160                         errno = EBADF;
161                         return -1;
162                 }
163
164                 return print_job_write(SNUM(fsp->conn), jobid, data, n);
165         }
166
167         if (!fsp->can_write) {
168                 errno = EPERM;
169                 return(0);
170         }
171
172         if (!fsp->modified) {
173                 SMB_STRUCT_STAT st;
174                 fsp->modified = True;
175
176                 if (SMB_VFS_FSTAT(fsp,fsp->fd,&st) == 0) {
177                         int dosmode = dos_mode(fsp->conn,fsp->fsp_name,&st);
178                         fsp->size = (SMB_BIG_UINT)st.st_size;
179                         if (MAP_ARCHIVE(fsp->conn) && !IS_DOS_ARCHIVE(dosmode))
180                                 file_chmod(fsp->conn,fsp->fsp_name,dosmode | aARCH,&st);
181
182                         /*
183                          * If this is the first write and we have an exclusive oplock then setup
184                          * the write cache.
185                          */
186
187                         if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type) && !wcp) {
188                                 setup_write_cache(fsp, st.st_size);
189                                 wcp = fsp->wcp;
190                         } 
191                 }  
192         }
193
194 #ifdef WITH_PROFILE
195         DO_PROFILE_INC(writecache_total_writes);
196         if (!fsp->oplock_type) {
197                 DO_PROFILE_INC(writecache_non_oplock_writes);
198         }
199 #endif
200
201         /*
202          * If this file is level II oplocked then we need
203          * to grab the shared memory lock and inform all
204          * other files with a level II lock that they need
205          * to flush their read caches. We keep the lock over
206          * the shared memory area whilst doing this.
207          */
208
209         release_level_2_oplocks_on_change(fsp);
210
211 #ifdef WITH_PROFILE
212         if (profile_p && profile_p->writecache_total_writes % 500 == 0) {
213                 DEBUG(3,("WRITECACHE: initwrites=%u abutted=%u total=%u \
214 nonop=%u allocated=%u active=%u direct=%u perfect=%u readhits=%u\n",
215                         profile_p->writecache_init_writes,
216                         profile_p->writecache_abutted_writes,
217                         profile_p->writecache_total_writes,
218                         profile_p->writecache_non_oplock_writes,
219                         profile_p->writecache_allocated_write_caches,
220                         profile_p->writecache_num_write_caches,
221                         profile_p->writecache_direct_writes,
222                         profile_p->writecache_num_perfect_writes,
223                         profile_p->writecache_read_hits ));
224
225                 DEBUG(3,("WRITECACHE: Flushes SEEK=%d, READ=%d, WRITE=%d, READRAW=%d, OPLOCK=%d, CLOSE=%d, SYNC=%d\n",
226                         profile_p->writecache_flushed_writes[SEEK_FLUSH],
227                         profile_p->writecache_flushed_writes[READ_FLUSH],
228                         profile_p->writecache_flushed_writes[WRITE_FLUSH],
229                         profile_p->writecache_flushed_writes[READRAW_FLUSH],
230                         profile_p->writecache_flushed_writes[OPLOCK_RELEASE_FLUSH],
231                         profile_p->writecache_flushed_writes[CLOSE_FLUSH],
232                         profile_p->writecache_flushed_writes[SYNC_FLUSH] ));
233         }
234 #endif
235
236         if(!wcp) {
237                 DO_PROFILE_INC(writecache_direct_writes);
238                 total_written = real_write_file(fsp, data, pos, n);
239                 if ((total_written != -1) && (pos + total_written > (SMB_OFF_T)fsp->size)) 
240                         fsp->size = (SMB_BIG_UINT)(pos + total_written);
241                 return total_written;
242         }
243
244         DEBUG(9,("write_file (%s)(fd=%d pos=%.0f size=%u) wcp->offset=%.0f wcp->data_size=%u\n",
245                 fsp->fsp_name, fsp->fd, (double)pos, (unsigned int)n, (double)wcp->offset, (unsigned int)wcp->data_size));
246
247         fsp->pos = pos + n;
248
249         /* 
250          * If we have active cache and it isn't contiguous then we flush.
251          * NOTE: There is a small problem with running out of disk ....
252          */
253
254         if (wcp->data_size) {
255
256                 BOOL cache_flush_needed = False;
257
258                 if ((pos >= wcp->offset) && (pos <= wcp->offset + wcp->data_size)) {
259       
260                         /* ASCII art.... JRA.
261
262       +--------------+-----
263       | Cached data  | Rest of allocated cache buffer....
264       +--------------+-----
265
266             +-------------------+
267             | Data to write     |
268             +-------------------+
269
270                         */
271
272                         /*
273                          * Start of write overlaps or abutts the existing data.
274                          */
275
276                         size_t data_used = MIN((wcp->alloc_size - (pos - wcp->offset)), n);
277
278                         memcpy(wcp->data + (pos - wcp->offset), data, data_used);
279
280                         /*
281                          * Update the current buffer size with the new data.
282                          */
283
284                         if(pos + data_used > wcp->offset + wcp->data_size)
285                                 wcp->data_size = pos + data_used - wcp->offset;
286
287                         /*
288                          * Update the file size if changed.
289                          */
290
291                         if (wcp->offset + wcp->data_size > wcp->file_size) {
292                                 wcp->file_size = wcp->offset + wcp->data_size;
293                                 fsp->size = (SMB_BIG_UINT)wcp->file_size;
294                         }
295
296                         /*
297                          * If we used all the data then
298                          * return here.
299                          */
300
301                         if(n == data_used)
302                                 return n;
303                         else
304                                 cache_flush_needed = True;
305
306                         /*
307                          * Move the start of data forward by the amount used,
308                          * cut down the amount left by the same amount.
309                          */
310
311                         data += data_used;
312                         pos += data_used;
313                         n -= data_used;
314
315                         DO_PROFILE_INC(writecache_abutted_writes);
316                         total_written = data_used;
317
318                         write_path = 1;
319
320                 } else if ((pos < wcp->offset) && (pos + n > wcp->offset) && 
321                                         (pos + n <= wcp->offset + wcp->alloc_size)) {
322
323                         /* ASCII art.... JRA.
324
325                         +---------------+
326                         | Cache buffer  |
327                         +---------------+
328
329             +-------------------+
330             | Data to write     |
331             +-------------------+
332
333                         */
334
335                         /*
336                          * End of write overlaps the existing data.
337                          */
338
339                         size_t data_used = pos + n - wcp->offset;
340
341                         memcpy(wcp->data, data + n - data_used, data_used);
342
343                         /*
344                          * Update the current buffer size with the new data.
345                          */
346
347                         if(pos + n > wcp->offset + wcp->data_size)
348                                 wcp->data_size = pos + n - wcp->offset;
349
350                         /*
351                          * Update the file size if changed.
352                          */
353
354                         if (wcp->offset + wcp->data_size > wcp->file_size) {
355                                 wcp->file_size = wcp->offset + wcp->data_size;
356                                 fsp->size = (SMB_BIG_UINT)wcp->file_size;
357                         }
358
359                         /*
360                          * We don't need to move the start of data, but we
361                          * cut down the amount left by the amount used.
362                          */
363
364                         n -= data_used;
365
366                         /*
367                          * We cannot have used all the data here.
368                          */
369
370                         cache_flush_needed = True;
371
372                         DO_PROFILE_INC(writecache_abutted_writes);
373                         total_written = data_used;
374
375                         write_path = 2;
376
377                 } else if ( (pos >= wcp->file_size) && 
378                                         (wcp->offset + wcp->data_size == wcp->file_size) &&
379                                         (pos > wcp->offset + wcp->data_size) && 
380                                         (pos < wcp->offset + wcp->alloc_size) ) {
381
382                         /* ASCII art.... JRA.
383
384                        End of file ---->|
385
386                         +---------------+---------------+
387                         | Cached data   | Cache buffer  |
388                         +---------------+---------------+
389
390                                               +-------------------+
391                                               | Data to write     |
392                                               +-------------------+
393
394                         */
395
396                         /*
397                          * Non-contiguous write part of which fits within
398                          * the cache buffer and is extending the file
399                          * and the cache contents reflect the current
400                          * data up to the current end of the file.
401                          */
402
403                         size_t data_used;
404
405                         if(pos + n <= wcp->offset + wcp->alloc_size)
406                                 data_used = n;
407                         else
408                                 data_used = wcp->offset + wcp->alloc_size - pos;
409
410                         /*
411                          * Fill in the non-continuous area with zeros.
412                          */
413
414                         memset(wcp->data + wcp->data_size, '\0',
415                                 pos - (wcp->offset + wcp->data_size) );
416
417                         memcpy(wcp->data + (pos - wcp->offset), data, data_used);
418
419                         /*
420                          * Update the current buffer size with the new data.
421                          */
422
423                         if(pos + data_used > wcp->offset + wcp->data_size)
424                                 wcp->data_size = pos + data_used - wcp->offset;
425
426                         /*
427                          * Update the file size if changed.
428                          */
429
430                         if (wcp->offset + wcp->data_size > wcp->file_size) {
431                                 wcp->file_size = wcp->offset + wcp->data_size;
432                                 fsp->size = (SMB_BIG_UINT)wcp->file_size;
433                         }
434
435                         /*
436                          * If we used all the data then
437                          * return here.
438                          */
439
440                         if(n == data_used)
441                                 return n;
442                         else
443                                 cache_flush_needed = True;
444
445                         /*
446                          * Move the start of data forward by the amount used,
447                          * cut down the amount left by the same amount.
448                          */
449
450                         data += data_used;
451                         pos += data_used;
452                         n -= data_used;
453
454                         DO_PROFILE_INC(writecache_abutted_writes);
455                         total_written = data_used;
456
457                         write_path = 3;
458
459                 } else {
460
461                         /* ASCII art..... JRA.
462
463    Case 1).
464
465                         +---------------+---------------+
466                         | Cached data   | Cache buffer  |
467                         +---------------+---------------+
468
469                                                               +-------------------+
470                                                               | Data to write     |
471                                                               +-------------------+
472
473    Case 2).
474
475                            +---------------+---------------+
476                            | Cached data   | Cache buffer  |
477                            +---------------+---------------+
478
479    +-------------------+
480    | Data to write     |
481    +-------------------+
482
483     Case 3).
484
485                            +---------------+---------------+
486                            | Cached data   | Cache buffer  |
487                            +---------------+---------------+
488
489                   +-----------------------------------------------------+
490                   | Data to write                                       |
491                   +-----------------------------------------------------+
492
493                   */
494
495                         /*
496                          * Write is bigger than buffer, or there is no overlap on the
497                          * low or high ends.
498                          */
499
500                         DEBUG(9,("write_file: non cacheable write : fd = %d, pos = %.0f, len = %u, current cache pos = %.0f \
501 len = %u\n",fsp->fd, (double)pos, (unsigned int)n, (double)wcp->offset, (unsigned int)wcp->data_size ));
502
503                         /*
504                          * Update the file size if needed.
505                          */
506
507                         if(pos + n > wcp->file_size) {
508                                 wcp->file_size = pos + n;
509                                 fsp->size = (SMB_BIG_UINT)wcp->file_size;
510                         }
511
512                         /*
513                          * If write would fit in the cache, and is larger than
514                          * the data already in the cache, flush the cache and
515                          * preferentially copy the data new data into it. Otherwise
516                          * just write the data directly.
517                          */
518
519                         if ( n <= wcp->alloc_size && n > wcp->data_size) {
520                                 cache_flush_needed = True;
521                         } else {
522                                 ssize_t ret = real_write_file(fsp, data, pos, n);
523
524                                 /*
525                                  * If the write overlaps the entire cache, then
526                                  * discard the current contents of the cache.
527                                  * Fix from Rasmus Borup Hansen rbh@math.ku.dk.
528                                  */
529
530                                 if ((pos <= wcp->offset) &&
531                                                 (pos + n >= wcp->offset + wcp->data_size) ) {
532                                         DEBUG(9,("write_file: discarding overwritten write \
533 cache: fd = %d, off=%.0f, size=%u\n", fsp->fd, (double)wcp->offset, (unsigned int)wcp->data_size ));
534                                         wcp->data_size = 0;
535                                 }
536
537                                 DO_PROFILE_INC(writecache_direct_writes);
538                                 if (ret == -1)
539                                         return ret;
540
541                                 if (pos + ret > wcp->file_size) {
542                                         wcp->file_size = pos + ret;
543                                         fsp->size = (SMB_BIG_UINT)wcp->file_size;
544                                 }
545
546                                 return ret;
547                         }
548
549                         write_path = 4;
550
551                 }
552
553                 if(wcp->data_size > wcp->file_size) {
554                         wcp->file_size = wcp->data_size;
555                         fsp->size = (SMB_BIG_UINT)wcp->file_size;
556                 }
557
558                 if (cache_flush_needed) {
559                         DEBUG(3,("WRITE_FLUSH:%d: due to noncontinuous write: fd = %d, size = %.0f, pos = %.0f, \
560 n = %u, wcp->offset=%.0f, wcp->data_size=%u\n",
561                                 write_path, fsp->fd, (double)wcp->file_size, (double)pos, (unsigned int)n,
562                                 (double)wcp->offset, (unsigned int)wcp->data_size ));
563
564                         flush_write_cache(fsp, WRITE_FLUSH);
565                 }
566         }
567
568         /*
569          * If the write request is bigger than the cache
570          * size, write it all out.
571          */
572
573         if (n > wcp->alloc_size ) {
574                 ssize_t ret = real_write_file(fsp, data, pos, n);
575                 if (ret == -1)
576                         return -1;
577
578                 if (pos + ret > wcp->file_size) {
579                         wcp->file_size = pos + n;
580                         fsp->size = (SMB_BIG_UINT)wcp->file_size;
581                 }
582
583                 DO_PROFILE_INC(writecache_direct_writes);
584                 return total_written + n;
585         }
586
587         /*
588          * If there's any data left, cache it.
589          */
590
591         if (n) {
592 #ifdef WITH_PROFILE
593                 if (wcp->data_size) {
594                         DO_PROFILE_INC(writecache_abutted_writes);
595                 } else {
596                         DO_PROFILE_INC(writecache_init_writes);
597                 }
598 #endif
599                 memcpy(wcp->data+wcp->data_size, data, n);
600                 if (wcp->data_size == 0) {
601                         wcp->offset = pos;
602                         DO_PROFILE_INC(writecache_num_write_caches);
603                 }
604                 wcp->data_size += n;
605
606                 /*
607                  * Update the file size if changed.
608                  */
609
610                 if (wcp->offset + wcp->data_size > wcp->file_size) {
611                         wcp->file_size = wcp->offset + wcp->data_size;
612                         fsp->size = (SMB_BIG_UINT)wcp->file_size;
613                 }
614                 DEBUG(9,("wcp->offset = %.0f wcp->data_size = %u cache return %u\n",
615                         (double)wcp->offset, (unsigned int)wcp->data_size, (unsigned int)n));
616
617                 total_written += n;
618                 return total_written; /* .... that's a write :) */
619         }
620   
621         return total_written;
622 }
623
624 /****************************************************************************
625  Delete the write cache structure.
626 ****************************************************************************/
627
628 void delete_write_cache(files_struct *fsp)
629 {
630         write_cache *wcp;
631
632         if(!fsp)
633                 return;
634
635         if(!(wcp = fsp->wcp))
636                 return;
637
638         DO_PROFILE_DEC(writecache_allocated_write_caches);
639         allocated_write_caches--;
640
641         SMB_ASSERT(wcp->data_size == 0);
642
643         SAFE_FREE(wcp->data);
644         SAFE_FREE(fsp->wcp);
645
646         DEBUG(10,("delete_write_cache: File %s deleted write cache\n", fsp->fsp_name ));
647 }
648
649 /****************************************************************************
650  Setup the write cache structure.
651 ****************************************************************************/
652
653 static BOOL setup_write_cache(files_struct *fsp, SMB_OFF_T file_size)
654 {
655         ssize_t alloc_size = lp_write_cache_size(SNUM(fsp->conn));
656         write_cache *wcp;
657
658         if (allocated_write_caches >= MAX_WRITE_CACHES) 
659                 return False;
660
661         if(alloc_size == 0 || fsp->wcp)
662                 return False;
663
664         if((wcp = (write_cache *)malloc(sizeof(write_cache))) == NULL) {
665                 DEBUG(0,("setup_write_cache: malloc fail.\n"));
666                 return False;
667         }
668
669         wcp->file_size = file_size;
670         wcp->offset = 0;
671         wcp->alloc_size = alloc_size;
672         wcp->data_size = 0;
673         if((wcp->data = malloc(wcp->alloc_size)) == NULL) {
674                 DEBUG(0,("setup_write_cache: malloc fail for buffer size %u.\n",
675                         (unsigned int)wcp->alloc_size ));
676                 SAFE_FREE(wcp);
677                 return False;
678         }
679
680         memset(wcp->data, '\0', wcp->alloc_size );
681
682         fsp->wcp = wcp;
683         DO_PROFILE_INC(writecache_allocated_write_caches);
684         allocated_write_caches++;
685
686         DEBUG(10,("setup_write_cache: File %s allocated write cache size %lu\n",
687                 fsp->fsp_name, (unsigned long)wcp->alloc_size ));
688
689         return True;
690 }
691
692 /****************************************************************************
693  Cope with a size change.
694 ****************************************************************************/
695
696 void set_filelen_write_cache(files_struct *fsp, SMB_OFF_T file_size)
697 {
698         fsp->size = (SMB_BIG_UINT)file_size;
699         if(fsp->wcp) {
700                 /* The cache *must* have been flushed before we do this. */
701                 if (fsp->wcp->data_size != 0) {
702                         pstring msg;
703                         slprintf(msg, sizeof(msg)-1, "set_filelen_write_cache: size change \
704 on file %s with write cache size = %lu\n", fsp->fsp_name, (unsigned long)fsp->wcp->data_size );
705                         smb_panic(msg);
706                 }
707                 fsp->wcp->file_size = file_size;
708         }
709 }
710
711 /*******************************************************************
712  Flush a write cache struct to disk.
713 ********************************************************************/
714
715 ssize_t flush_write_cache(files_struct *fsp, enum flush_reason_enum reason)
716 {
717         write_cache *wcp = fsp->wcp;
718         size_t data_size;
719         ssize_t ret;
720
721         if(!wcp || !wcp->data_size)
722                 return 0;
723
724         data_size = wcp->data_size;
725         wcp->data_size = 0;
726
727         DO_PROFILE_DEC_INC(writecache_num_write_caches,writecache_flushed_writes[reason]);
728
729         DEBUG(9,("flushing write cache: fd = %d, off=%.0f, size=%u\n",
730                 fsp->fd, (double)wcp->offset, (unsigned int)data_size));
731
732 #ifdef WITH_PROFILE
733         if(data_size == wcp->alloc_size)
734                 DO_PROFILE_INC(writecache_num_perfect_writes);
735 #endif
736
737         ret = real_write_file(fsp, wcp->data, wcp->offset, data_size);
738
739         /*
740          * Ensure file size if kept up to date if write extends file.
741          */
742
743         if ((ret != -1) && (wcp->offset + ret > wcp->file_size))
744                 wcp->file_size = wcp->offset + ret;
745
746         return ret;
747 }
748
749 /*******************************************************************
750 sync a file
751 ********************************************************************/
752
753 void sync_file(connection_struct *conn, files_struct *fsp)
754 {
755         if(lp_strict_sync(SNUM(conn)) && fsp->fd != -1) {
756                 flush_write_cache(fsp, SYNC_FLUSH);
757                 SMB_VFS_FSYNC(fsp,fsp->fd);
758         }
759 }
760
761
762 /************************************************************
763  Perform a stat whether a valid fd or not.
764 ************************************************************/
765
766 int fsp_stat(files_struct *fsp, SMB_STRUCT_STAT *pst)
767 {
768         if (fsp->fd == -1)
769                 return SMB_VFS_STAT(fsp->conn, fsp->fsp_name, pst);
770         else
771                 return SMB_VFS_FSTAT(fsp,fsp->fd, pst);
772 }