a5cca53bc7617f801bc33ddd1ca95f3ed113ee28
[sfrench/samba-autobuild/.git] / source3 / smbd / dosmode.c
1 /* 
2    Unix SMB/CIFS implementation.
3    dos mode handling functions
4    Copyright (C) Andrew Tridgell 1992-1998
5    Copyright (C) James Peach 2006
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "includes.h"
22
23 static int set_sparse_flag(const SMB_STRUCT_STAT * const sbuf)
24 {
25 #if defined (HAVE_STAT_ST_BLOCKS) && defined(STAT_ST_BLOCKSIZE)
26         if (sbuf->st_size > sbuf->st_blocks * (SMB_OFF_T)STAT_ST_BLOCKSIZE) {
27                 return FILE_ATTRIBUTE_SPARSE;
28         }
29 #endif
30         return 0;
31 }
32
33 /****************************************************************************
34  Change a dos mode to a unix mode.
35     Base permission for files:
36          if creating file and inheriting (i.e. parent_dir != NULL)
37            apply read/write bits from parent directory.
38          else   
39            everybody gets read bit set
40          dos readonly is represented in unix by removing everyone's write bit
41          dos archive is represented in unix by the user's execute bit
42          dos system is represented in unix by the group's execute bit
43          dos hidden is represented in unix by the other's execute bit
44          if !inheriting {
45            Then apply create mask,
46            then add force bits.
47          }
48     Base permission for directories:
49          dos directory is represented in unix by unix's dir bit and the exec bit
50          if !inheriting {
51            Then apply create mask,
52            then add force bits.
53          }
54 ****************************************************************************/
55
56 mode_t unix_mode(connection_struct *conn, int dosmode, const char *fname,
57                  const char *inherit_from_dir)
58 {
59         mode_t result = (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
60         mode_t dir_mode = 0; /* Mode of the inherit_from directory if
61                               * inheriting. */
62
63         if (!lp_store_dos_attributes(SNUM(conn)) && IS_DOS_READONLY(dosmode)) {
64                 result &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
65         }
66
67         if (fname && (inherit_from_dir != NULL)
68             && lp_inherit_perms(SNUM(conn))) {
69                 SMB_STRUCT_STAT sbuf;
70
71                 DEBUG(2, ("unix_mode(%s) inheriting from %s\n", fname,
72                           inherit_from_dir));
73                 if (SMB_VFS_STAT(conn, inherit_from_dir, &sbuf) != 0) {
74                         DEBUG(4,("unix_mode(%s) failed, [dir %s]: %s\n", fname,
75                                  inherit_from_dir, strerror(errno)));
76                         return(0);      /* *** shouldn't happen! *** */
77                 }
78
79                 /* Save for later - but explicitly remove setuid bit for safety. */
80                 dir_mode = sbuf.st_mode & ~S_ISUID;
81                 DEBUG(2,("unix_mode(%s) inherit mode %o\n",fname,(int)dir_mode));
82                 /* Clear "result" */
83                 result = 0;
84         } 
85
86         if (IS_DOS_DIR(dosmode)) {
87                 /* We never make directories read only for the owner as under DOS a user
88                 can always create a file in a read-only directory. */
89                 result |= (S_IFDIR | S_IWUSR);
90
91                 if (dir_mode) {
92                         /* Inherit mode of parent directory. */
93                         result |= dir_mode;
94                 } else {
95                         /* Provisionally add all 'x' bits */
96                         result |= (S_IXUSR | S_IXGRP | S_IXOTH);                 
97
98                         /* Apply directory mask */
99                         result &= lp_dir_mask(SNUM(conn));
100                         /* Add in force bits */
101                         result |= lp_force_dir_mode(SNUM(conn));
102                 }
103         } else { 
104                 if (lp_map_archive(SNUM(conn)) && IS_DOS_ARCHIVE(dosmode))
105                         result |= S_IXUSR;
106
107                 if (lp_map_system(SNUM(conn)) && IS_DOS_SYSTEM(dosmode))
108                         result |= S_IXGRP;
109  
110                 if (lp_map_hidden(SNUM(conn)) && IS_DOS_HIDDEN(dosmode))
111                         result |= S_IXOTH;  
112
113                 if (dir_mode) {
114                         /* Inherit 666 component of parent directory mode */
115                         result |= dir_mode & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
116                 } else {
117                         /* Apply mode mask */
118                         result &= lp_create_mask(SNUM(conn));
119                         /* Add in force bits */
120                         result |= lp_force_create_mode(SNUM(conn));
121                 }
122         }
123
124         DEBUG(3,("unix_mode(%s) returning 0%o\n",fname,(int)result ));
125         return(result);
126 }
127
128 /****************************************************************************
129  Change a unix mode to a dos mode.
130 ****************************************************************************/
131
132 static uint32 dos_mode_from_sbuf(connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf)
133 {
134         int result = 0;
135         enum mapreadonly_options ro_opts = (enum mapreadonly_options)lp_map_readonly(SNUM(conn));
136
137         if (ro_opts == MAP_READONLY_YES) {
138                 /* Original Samba method - map inverse of user "w" bit. */
139                 if ((sbuf->st_mode & S_IWUSR) == 0) {
140                         result |= aRONLY;
141                 }
142         } else if (ro_opts == MAP_READONLY_PERMISSIONS) {
143                 /* Check actual permissions for read-only. */
144                 if (!can_write_to_file(conn, path, sbuf)) {
145                         result |= aRONLY;
146                 }
147         } /* Else never set the readonly bit. */
148
149         if (MAP_ARCHIVE(conn) && ((sbuf->st_mode & S_IXUSR) != 0))
150                 result |= aARCH;
151
152         if (MAP_SYSTEM(conn) && ((sbuf->st_mode & S_IXGRP) != 0))
153                 result |= aSYSTEM;
154         
155         if (MAP_HIDDEN(conn) && ((sbuf->st_mode & S_IXOTH) != 0))
156                 result |= aHIDDEN;   
157   
158         if (S_ISDIR(sbuf->st_mode))
159                 result = aDIR | (result & aRONLY);
160
161         result |= set_sparse_flag(sbuf);
162  
163 #ifdef S_ISLNK
164 #if LINKS_READ_ONLY
165         if (S_ISLNK(sbuf->st_mode) && S_ISDIR(sbuf->st_mode))
166                 result |= aRONLY;
167 #endif
168 #endif
169
170         DEBUG(8,("dos_mode_from_sbuf returning "));
171
172         if (result & aHIDDEN) DEBUG(8, ("h"));
173         if (result & aRONLY ) DEBUG(8, ("r"));
174         if (result & aSYSTEM) DEBUG(8, ("s"));
175         if (result & aDIR   ) DEBUG(8, ("d"));
176         if (result & aARCH  ) DEBUG(8, ("a"));
177         
178         DEBUG(8,("\n"));
179         return result;
180 }
181
182 /****************************************************************************
183  Get DOS attributes from an EA.
184 ****************************************************************************/
185
186 static bool get_ea_dos_attribute(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf, uint32 *pattr)
187 {
188         ssize_t sizeret;
189         fstring attrstr;
190         unsigned int dosattr;
191
192         if (!lp_store_dos_attributes(SNUM(conn))) {
193                 return False;
194         }
195
196         /* Don't reset pattr to zero as we may already have filename-based attributes we
197            need to preserve. */
198
199         sizeret = SMB_VFS_GETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, sizeof(attrstr));
200         if (sizeret == -1) {
201 #if defined(ENOTSUP) && defined(ENOATTR)
202                 if ((errno != ENOTSUP) && (errno != ENOATTR) && (errno != EACCES) && (errno != EPERM)) {
203                         DEBUG(1,("get_ea_dos_attributes: Cannot get attribute from EA on file %s: Error = %s\n",
204                                 path, strerror(errno) ));
205                         set_store_dos_attributes(SNUM(conn), False);
206                 }
207 #endif
208                 return False;
209         }
210         /* Null terminate string. */
211         attrstr[sizeret] = 0;
212         DEBUG(10,("get_ea_dos_attribute: %s attrstr = %s\n", path, attrstr));
213
214         if (sizeret < 2 || attrstr[0] != '0' || attrstr[1] != 'x' ||
215                         sscanf(attrstr, "%x", &dosattr) != 1) {
216                 DEBUG(1,("get_ea_dos_attributes: Badly formed DOSATTRIB on file %s - %s\n", path, attrstr));
217                 return False;
218         }
219
220         if (S_ISDIR(sbuf->st_mode)) {
221                 dosattr |= aDIR;
222         }
223         *pattr = (uint32)(dosattr & SAMBA_ATTRIBUTES_MASK);
224
225         DEBUG(8,("get_ea_dos_attribute returning (0x%x)", dosattr));
226
227         if (dosattr & aHIDDEN) DEBUG(8, ("h"));
228         if (dosattr & aRONLY ) DEBUG(8, ("r"));
229         if (dosattr & aSYSTEM) DEBUG(8, ("s"));
230         if (dosattr & aDIR   ) DEBUG(8, ("d"));
231         if (dosattr & aARCH  ) DEBUG(8, ("a"));
232         
233         DEBUG(8,("\n"));
234
235         return True;
236 }
237
238 /****************************************************************************
239  Set DOS attributes in an EA.
240 ****************************************************************************/
241
242 static bool set_ea_dos_attribute(connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf, uint32 dosmode)
243 {
244         fstring attrstr;
245         files_struct *fsp = NULL;
246         bool ret = False;
247
248         if (!lp_store_dos_attributes(SNUM(conn))) {
249                 return False;
250         }
251
252         snprintf(attrstr, sizeof(attrstr)-1, "0x%x", dosmode & SAMBA_ATTRIBUTES_MASK);
253         if (SMB_VFS_SETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, strlen(attrstr), 0) == -1) {
254                 if((errno != EPERM) && (errno != EACCES)) {
255                         if (errno == ENOSYS
256 #if defined(ENOTSUP)
257                                 || errno == ENOTSUP) {
258 #else
259                                 ) {
260 #endif
261                                 set_store_dos_attributes(SNUM(conn), False);
262                         }
263                         return False;
264                 }
265
266                 /* We want DOS semantics, ie allow non owner with write permission to change the
267                         bits on a file. Just like file_ntimes below.
268                 */
269
270                 /* Check if we have write access. */
271                 if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
272                         return False;
273
274                 /*
275                  * We need to open the file with write access whilst
276                  * still in our current user context. This ensures we
277                  * are not violating security in doing the setxattr.
278                  */
279
280                 if (!NT_STATUS_IS_OK(open_file_fchmod(NULL, conn, path, sbuf,
281                                                       &fsp)))
282                         return ret;
283                 become_root();
284                 if (SMB_VFS_SETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, strlen(attrstr), 0) == 0) {
285                         ret = True;
286                 }
287                 unbecome_root();
288                 close_file_fchmod(fsp);
289                 return ret;
290         }
291         DEBUG(10,("set_ea_dos_attribute: set EA %s on file %s\n", attrstr, path));
292         return True;
293 }
294
295 /****************************************************************************
296  Change a unix mode to a dos mode for an ms dfs link.
297 ****************************************************************************/
298
299 uint32 dos_mode_msdfs(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf)
300 {
301         uint32 result = 0;
302
303         DEBUG(8,("dos_mode_msdfs: %s\n", path));
304
305         if (!VALID_STAT(*sbuf)) {
306                 return 0;
307         }
308
309         /* First do any modifications that depend on the path name. */
310         /* hide files with a name starting with a . */
311         if (lp_hide_dot_files(SNUM(conn))) {
312                 const char *p = strrchr_m(path,'/');
313                 if (p) {
314                         p++;
315                 } else {
316                         p = path;
317                 }
318                 
319                 if (p[0] == '.' && p[1] != '.' && p[1] != 0) {
320                         result |= aHIDDEN;
321                 }
322         }
323         
324         result |= dos_mode_from_sbuf(conn, path, sbuf);
325
326         /* Optimization : Only call is_hidden_path if it's not already
327            hidden. */
328         if (!(result & aHIDDEN) && IS_HIDDEN_PATH(conn,path)) {
329                 result |= aHIDDEN;
330         }
331
332         DEBUG(8,("dos_mode_msdfs returning "));
333
334         if (result & aHIDDEN) DEBUG(8, ("h"));
335         if (result & aRONLY ) DEBUG(8, ("r"));
336         if (result & aSYSTEM) DEBUG(8, ("s"));
337         if (result & aDIR   ) DEBUG(8, ("d"));
338         if (result & aARCH  ) DEBUG(8, ("a"));
339         if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
340         
341         DEBUG(8,("\n"));
342
343         return(result);
344 }
345
346 /****************************************************************************
347  Change a unix mode to a dos mode.
348 ****************************************************************************/
349
350 uint32 dos_mode(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf)
351 {
352         uint32 result = 0;
353         bool offline;
354
355         DEBUG(8,("dos_mode: %s\n", path));
356
357         if (!VALID_STAT(*sbuf)) {
358                 return 0;
359         }
360
361         /* First do any modifications that depend on the path name. */
362         /* hide files with a name starting with a . */
363         if (lp_hide_dot_files(SNUM(conn))) {
364                 const char *p = strrchr_m(path,'/');
365                 if (p) {
366                         p++;
367                 } else {
368                         p = path;
369                 }
370                 
371                 if (p[0] == '.' && p[1] != '.' && p[1] != 0) {
372                         result |= aHIDDEN;
373                 }
374         }
375         
376         /* Get the DOS attributes from an EA by preference. */
377         if (get_ea_dos_attribute(conn, path, sbuf, &result)) {
378                 result |= set_sparse_flag(sbuf);
379         } else {
380                 result |= dos_mode_from_sbuf(conn, path, sbuf);
381         }
382
383         
384         offline = SMB_VFS_IS_OFFLINE(conn, path, sbuf);
385         if (S_ISREG(sbuf->st_mode) && offline) {
386                 result |= FILE_ATTRIBUTE_OFFLINE;
387         }
388
389         /* Optimization : Only call is_hidden_path if it's not already
390            hidden. */
391         if (!(result & aHIDDEN) && IS_HIDDEN_PATH(conn,path)) {
392                 result |= aHIDDEN;
393         }
394
395         DEBUG(8,("dos_mode returning "));
396
397         if (result & aHIDDEN) DEBUG(8, ("h"));
398         if (result & aRONLY ) DEBUG(8, ("r"));
399         if (result & aSYSTEM) DEBUG(8, ("s"));
400         if (result & aDIR   ) DEBUG(8, ("d"));
401         if (result & aARCH  ) DEBUG(8, ("a"));
402         if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
403         
404         DEBUG(8,("\n"));
405
406         return(result);
407 }
408
409 /*******************************************************************
410  chmod a file - but preserve some bits.
411 ********************************************************************/
412
413 int file_set_dosmode(connection_struct *conn, const char *fname,
414                      uint32 dosmode, SMB_STRUCT_STAT *st,
415                      const char *parent_dir,
416                      bool newfile)
417 {
418         SMB_STRUCT_STAT st1;
419         int mask=0;
420         mode_t tmp;
421         mode_t unixmode;
422         int ret = -1, lret = -1;
423         uint32_t old_mode;
424
425         /* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */
426         dosmode &= (SAMBA_ATTRIBUTES_MASK | FILE_ATTRIBUTE_OFFLINE);
427
428         DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n", dosmode, fname));
429
430         if (st == NULL) {
431                 SET_STAT_INVALID(st1);
432                 st = &st1;
433         }
434
435         if (!VALID_STAT(*st)) {
436                 if (SMB_VFS_STAT(conn,fname,st))
437                         return(-1);
438         }
439
440         unixmode = st->st_mode;
441
442         get_acl_group_bits(conn, fname, &st->st_mode);
443
444         if (S_ISDIR(st->st_mode))
445                 dosmode |= aDIR;
446         else
447                 dosmode &= ~aDIR;
448
449         old_mode = dos_mode(conn,fname,st);
450         
451         if (dosmode & FILE_ATTRIBUTE_OFFLINE) {
452                 if (!(old_mode & FILE_ATTRIBUTE_OFFLINE)) {
453                         lret = SMB_VFS_SET_OFFLINE(conn, fname);
454                         if (lret == -1) {
455                                 DEBUG(0, ("set_dos_mode: client has asked to set "
456                                           "FILE_ATTRIBUTE_OFFLINE to %s/%s but there was "
457                                           "an error while setting it or it is not supported.\n",
458                                           parent_dir, fname));
459                         }
460                 }
461         }
462
463         dosmode  &= ~FILE_ATTRIBUTE_OFFLINE;
464         old_mode &= ~FILE_ATTRIBUTE_OFFLINE;
465
466         if (old_mode == dosmode) {
467                 st->st_mode = unixmode;
468                 return(0);
469         }
470
471         /* Store the DOS attributes in an EA by preference. */
472         if (set_ea_dos_attribute(conn, fname, st, dosmode)) {
473                 if (!newfile) {
474                         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
475                                 FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
476                 }
477                 st->st_mode = unixmode;
478                 return 0;
479         }
480
481         unixmode = unix_mode(conn,dosmode,fname, parent_dir);
482
483         /* preserve the s bits */
484         mask |= (S_ISUID | S_ISGID);
485
486         /* preserve the t bit */
487 #ifdef S_ISVTX
488         mask |= S_ISVTX;
489 #endif
490
491         /* possibly preserve the x bits */
492         if (!MAP_ARCHIVE(conn))
493                 mask |= S_IXUSR;
494         if (!MAP_SYSTEM(conn))
495                 mask |= S_IXGRP;
496         if (!MAP_HIDDEN(conn))
497                 mask |= S_IXOTH;
498
499         unixmode |= (st->st_mode & mask);
500
501         /* if we previously had any r bits set then leave them alone */
502         if ((tmp = st->st_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
503                 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
504                 unixmode |= tmp;
505         }
506
507         /* if we previously had any w bits set then leave them alone 
508                 whilst adding in the new w bits, if the new mode is not rdonly */
509         if (!IS_DOS_READONLY(dosmode)) {
510                 unixmode |= (st->st_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
511         }
512
513         ret = SMB_VFS_CHMOD(conn, fname, unixmode);
514         if (ret == 0) {
515                 if(!newfile || (lret != -1)) {
516                         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
517                                      FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
518                 }
519                 st->st_mode = unixmode;
520                 return 0;
521         }
522
523         if((errno != EPERM) && (errno != EACCES))
524                 return -1;
525
526         if(!lp_dos_filemode(SNUM(conn)))
527                 return -1;
528
529         /* We want DOS semantics, ie allow non owner with write permission to change the
530                 bits on a file. Just like file_ntimes below.
531         */
532
533         /* Check if we have write access. */
534         if (CAN_WRITE(conn)) {
535                 /*
536                  * We need to open the file with write access whilst
537                  * still in our current user context. This ensures we
538                  * are not violating security in doing the fchmod.
539                  * This file open does *not* break any oplocks we are
540                  * holding. We need to review this.... may need to
541                  * break batch oplocks open by others. JRA.
542                  */
543                 files_struct *fsp;
544                 if (!NT_STATUS_IS_OK(open_file_fchmod(NULL, conn, fname, st,
545                                                       &fsp)))
546                         return -1;
547                 become_root();
548                 ret = SMB_VFS_FCHMOD(fsp, unixmode);
549                 unbecome_root();
550                 close_file_fchmod(fsp);
551                 if (!newfile) {
552                         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
553                                 FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
554                 }
555                 if (ret == 0) {
556                         st->st_mode = unixmode;
557                 }
558         }
559
560         return( ret );
561 }
562
563 /*******************************************************************
564  Wrapper around the VFS ntimes that possibly allows DOS semantics rather
565  than POSIX.
566 *******************************************************************/
567
568 int file_ntimes(connection_struct *conn, const char *fname, const struct timespec ts[2])
569 {
570         SMB_STRUCT_STAT sbuf;
571         int ret = -1;
572
573         errno = 0;
574         ZERO_STRUCT(sbuf);
575
576         DEBUG(6, ("file_ntime: actime: %s",
577                   time_to_asc(convert_timespec_to_time_t(ts[0]))));
578         DEBUG(6, ("file_ntime: modtime: %s",
579                   time_to_asc(convert_timespec_to_time_t(ts[1]))));
580
581         /* Don't update the time on read-only shares */
582         /* We need this as set_filetime (which can be called on
583            close and other paths) can end up calling this function
584            without the NEED_WRITE protection. Found by : 
585            Leo Weppelman <leo@wau.mis.ah.nl>
586         */
587
588         if (!CAN_WRITE(conn)) {
589                 return 0;
590         }
591
592         if(SMB_VFS_NTIMES(conn, fname, ts) == 0) {
593                 return 0;
594         }
595
596         if((errno != EPERM) && (errno != EACCES)) {
597                 return -1;
598         }
599
600         if(!lp_dos_filetimes(SNUM(conn))) {
601                 return -1;
602         }
603
604         /* We have permission (given by the Samba admin) to
605            break POSIX semantics and allow a user to change
606            the time on a file they don't own but can write to
607            (as DOS does).
608          */
609
610         /* Check if we have write access. */
611         if (can_write_to_file(conn, fname, &sbuf)) {
612                 /* We are allowed to become root and change the filetime. */
613                 become_root();
614                 ret = SMB_VFS_NTIMES(conn, fname, ts);
615                 unbecome_root();
616         }
617
618         return ret;
619 }
620
621 /******************************************************************
622  Force a "sticky" write time on a pathname. This will always be
623  returned on all future write time queries and set on close.
624 ******************************************************************/
625
626 bool set_sticky_write_time_path(connection_struct *conn, const char *fname,
627                          struct file_id fileid, const struct timespec mtime)
628 {
629         if (null_timespec(mtime)) {
630                 return true;
631         }
632
633         if (!set_sticky_write_time(fileid, mtime)) {
634                 return false;
635         }
636
637         return true;
638 }
639
640 /******************************************************************
641  Force a "sticky" write time on an fsp. This will always be
642  returned on all future write time queries and set on close.
643 ******************************************************************/
644
645 bool set_sticky_write_time_fsp(struct files_struct *fsp, const struct timespec mtime)
646 {
647         fsp->write_time_forced = true;
648         TALLOC_FREE(fsp->update_write_time_event);
649
650         return set_sticky_write_time_path(fsp->conn, fsp->fsp_name,
651                         fsp->file_id, mtime);
652 }
653
654 /******************************************************************
655  Update a write time immediately, without the 2 second delay.
656 ******************************************************************/
657
658 bool update_write_time(struct files_struct *fsp)
659 {
660         if (!set_write_time(fsp->file_id, timespec_current())) {
661                 return false;
662         }
663
664         notify_fname(fsp->conn, NOTIFY_ACTION_MODIFIED,
665                         FILE_NOTIFY_CHANGE_LAST_WRITE, fsp->fsp_name);
666
667         return true;
668 }