LCOV - code coverage report
Current view: top level - source4/ntvfs/posix - pvfs_setfileinfo.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 377 459 82.1 %
Date: 2024-04-21 15:09:00 Functions: 8 8 100.0 %

          Line data    Source code
       1             : /* 
       2             :    Unix SMB/CIFS implementation.
       3             : 
       4             :    POSIX NTVFS backend - setfileinfo
       5             : 
       6             :    Copyright (C) Andrew Tridgell 2004
       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 3 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, see <http://www.gnu.org/licenses/>.
      20             : */
      21             : 
      22             : #include "includes.h"
      23             : #include "vfs_posix.h"
      24             : #include "system/time.h"
      25             : #include "librpc/gen_ndr/xattr.h"
      26             : 
      27             : 
      28             : /*
      29             :   determine what access bits are needed for a call
      30             : */
      31        2627 : static uint32_t pvfs_setfileinfo_access(union smb_setfileinfo *info)
      32             : {
      33           0 :         uint32_t needed;
      34             : 
      35        2627 :         switch (info->generic.level) {
      36         287 :         case RAW_SFILEINFO_EA_SET:
      37         287 :                 needed = SEC_FILE_WRITE_EA;
      38         287 :                 break;
      39             : 
      40         339 :         case RAW_SFILEINFO_DISPOSITION_INFO:
      41             :         case RAW_SFILEINFO_DISPOSITION_INFORMATION:
      42         339 :                 needed = SEC_STD_DELETE;
      43         339 :                 break;
      44             : 
      45         552 :         case RAW_SFILEINFO_END_OF_FILE_INFO:
      46         552 :                 needed = SEC_FILE_WRITE_DATA;
      47         552 :                 break;
      48             : 
      49          11 :         case RAW_SFILEINFO_POSITION_INFORMATION:
      50          11 :                 needed = 0;
      51          11 :                 break;
      52             : 
      53         628 :         case RAW_SFILEINFO_SEC_DESC:
      54         628 :                 needed = 0;
      55         628 :                 if (info->set_secdesc.in.secinfo_flags & (SECINFO_OWNER|SECINFO_GROUP)) {
      56         122 :                         needed |= SEC_STD_WRITE_OWNER;
      57             :                 }
      58         628 :                 if (info->set_secdesc.in.secinfo_flags & SECINFO_DACL) {
      59         628 :                         needed |= SEC_STD_WRITE_DAC;
      60             :                 }
      61         628 :                 if (info->set_secdesc.in.secinfo_flags & SECINFO_SACL) {
      62          35 :                         needed |= SEC_FLAG_SYSTEM_SECURITY;
      63             :                 }
      64         628 :                 break;
      65             : 
      66          47 :         case RAW_SFILEINFO_RENAME_INFORMATION:
      67             :         case RAW_SFILEINFO_RENAME_INFORMATION_SMB2:
      68          47 :                 needed = SEC_STD_DELETE;
      69          47 :                 break;
      70             : 
      71         763 :         default:
      72         763 :                 needed = SEC_FILE_WRITE_ATTRIBUTE;
      73         763 :                 break;
      74             :         }
      75             : 
      76        2627 :         return needed;
      77             : }
      78             : 
      79             : /*
      80             :   rename_information level for streams
      81             : */
      82          11 : static NTSTATUS pvfs_setfileinfo_rename_stream(struct pvfs_state *pvfs, 
      83             :                                                struct ntvfs_request *req, 
      84             :                                                struct pvfs_filename *name,
      85             :                                                int fd,
      86             :                                                DATA_BLOB *odb_locking_key,
      87             :                                                union smb_setfileinfo *info)
      88             : {
      89           0 :         NTSTATUS status;
      90          11 :         struct odb_lock *lck = NULL;
      91             : 
      92             :         /* strangely, this gives a sharing violation, not invalid
      93             :            parameter */
      94          11 :         if (info->rename_information.in.new_name[0] != ':') {
      95           1 :                 return NT_STATUS_SHARING_VIOLATION;
      96             :         }
      97             : 
      98          10 :         status = pvfs_access_check_simple(pvfs, req, name, SEC_FILE_WRITE_ATTRIBUTE);
      99          10 :         if (!NT_STATUS_IS_OK(status)) {
     100           0 :                 return status;
     101             :         }
     102             : 
     103          10 :         lck = odb_lock(req, pvfs->odb_context, odb_locking_key);
     104          10 :         if (lck == NULL) {
     105           0 :                 DEBUG(0,("Unable to lock opendb for can_stat\n"));
     106           0 :                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
     107             :         }
     108             : 
     109             : 
     110          10 :         status = pvfs_stream_rename(pvfs, name, fd, 
     111          10 :                                     info->rename_information.in.new_name+1,
     112          10 :                                     info->rename_information.in.overwrite);
     113          10 :         return status;
     114             : }
     115             : 
     116             : /*
     117             :   rename_information level
     118             : */
     119          46 : static NTSTATUS pvfs_setfileinfo_rename(struct pvfs_state *pvfs, 
     120             :                                         struct ntvfs_request *req, 
     121             :                                         struct pvfs_filename *name,
     122             :                                         int fd,
     123             :                                         DATA_BLOB *odb_locking_key,
     124             :                                         union smb_setfileinfo *info)
     125             : {
     126           0 :         NTSTATUS status;
     127           0 :         struct pvfs_filename *name2;
     128           0 :         char *new_name, *p;
     129          46 :         struct odb_lock *lck = NULL;
     130             : 
     131             :         /* renames are only allowed within a directory */
     132          46 :         if (strchr_m(info->rename_information.in.new_name, '\\') &&
     133           6 :             (req->ctx->protocol < PROTOCOL_SMB2_02)) {
     134           2 :                 return NT_STATUS_NOT_SUPPORTED;
     135             :         }
     136             : 
     137             :         /* handle stream renames specially */
     138          44 :         if (name->stream_name) {
     139          11 :                 return pvfs_setfileinfo_rename_stream(pvfs, req, name, fd, 
     140             :                                                       odb_locking_key, info);
     141             :         }
     142             : 
     143             :         /* w2k3 does not appear to allow relative rename. On SMB2, vista sends it sometimes,
     144             :            but I suspect it is just uninitialised memory */
     145          33 :         if (info->rename_information.in.root_fid != 0 && 
     146           1 :             (req->ctx->protocol < PROTOCOL_SMB2_02)) {
     147           1 :                 return NT_STATUS_INVALID_PARAMETER;
     148             :         }
     149             : 
     150             :         /* construct the fully qualified windows name for the new file name */
     151          32 :         if (req->ctx->protocol >= PROTOCOL_SMB2_02) {
     152             :                 /* SMB2 sends the full path of the new name */
     153           3 :                 new_name = talloc_asprintf(req, "\\%s", info->rename_information.in.new_name);
     154             :         } else {
     155          29 :                 new_name = talloc_strdup(req, name->original_name);
     156          29 :                 if (new_name == NULL) {
     157           0 :                         return NT_STATUS_NO_MEMORY;
     158             :                 }
     159          29 :                 p = strrchr_m(new_name, '\\');
     160          29 :                 if (p == NULL) {
     161           0 :                         return NT_STATUS_OBJECT_NAME_INVALID;
     162             :                 } else {
     163          29 :                         *p = 0;
     164             :                 }
     165             : 
     166          29 :                 new_name = talloc_asprintf(req, "%s\\%s", new_name,
     167             :                                            info->rename_information.in.new_name);
     168             :         }
     169          32 :         if (new_name == NULL) {
     170           0 :                 return NT_STATUS_NO_MEMORY;
     171             :         }
     172             : 
     173             :         /* resolve the new name */
     174          32 :         status = pvfs_resolve_name(pvfs, req, new_name, 0, &name2);
     175          32 :         if (!NT_STATUS_IS_OK(status)) {
     176           3 :                 return status;
     177             :         }
     178             : 
     179             :         /* if the destination exists, then check the rename is allowed */
     180          29 :         if (name2->exists) {
     181           8 :                 if (strcmp(name2->full_name, name->full_name) == 0) {
     182             :                         /* rename to same name is null-op */
     183           2 :                         return NT_STATUS_OK;
     184             :                 }
     185             : 
     186           6 :                 if (!info->rename_information.in.overwrite) {
     187           2 :                         return NT_STATUS_OBJECT_NAME_COLLISION;
     188             :                 }
     189             : 
     190           4 :                 status = pvfs_can_delete(pvfs, req, name2, NULL);
     191           4 :                 if (NT_STATUS_EQUAL(status, NT_STATUS_DELETE_PENDING)) {
     192           1 :                         return NT_STATUS_ACCESS_DENIED;
     193             :                 }
     194           3 :                 if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) {
     195           1 :                         return NT_STATUS_ACCESS_DENIED;
     196             :                 }
     197           2 :                 if (!NT_STATUS_IS_OK(status)) {
     198           0 :                         return status;
     199             :                 }
     200             :         }
     201             : 
     202          23 :         status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
     203          23 :         if (!NT_STATUS_IS_OK(status)) {
     204           0 :                 return status;
     205             :         }
     206             : 
     207          23 :         lck = odb_lock(req, pvfs->odb_context, odb_locking_key);
     208          23 :         if (lck == NULL) {
     209           0 :                 DEBUG(0,("Unable to lock opendb for can_stat\n"));
     210           0 :                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
     211             :         }
     212             : 
     213          23 :         status = pvfs_do_rename(pvfs, lck, name, name2->full_name);
     214          23 :         talloc_free(lck);
     215          23 :         NT_STATUS_NOT_OK_RETURN(status);
     216          23 :         if (NT_STATUS_IS_OK(status)) {
     217          23 :                 name->full_name = talloc_steal(name, name2->full_name);
     218          23 :                 name->original_name = talloc_steal(name, name2->original_name);
     219             :         }
     220             : 
     221          23 :         return NT_STATUS_OK;
     222             : }
     223             : 
     224             : /*
     225             :   add a single DOS EA
     226             : */
     227       66602 : NTSTATUS pvfs_setfileinfo_ea_set(struct pvfs_state *pvfs, 
     228             :                                  struct pvfs_filename *name,
     229             :                                  int fd, uint16_t num_eas,
     230             :                                  struct ea_struct *eas)
     231             : {
     232           0 :         struct xattr_DosEAs *ealist;
     233           0 :         int i, j;
     234           0 :         NTSTATUS status;
     235             : 
     236       66602 :         if (num_eas == 0) {
     237       66306 :                 return NT_STATUS_OK;
     238             :         }
     239             : 
     240         296 :         if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
     241           0 :                 return NT_STATUS_NOT_SUPPORTED;
     242             :         }
     243             : 
     244         296 :         ealist = talloc(name, struct xattr_DosEAs);
     245             : 
     246             :         /* load the current list */
     247         296 :         status = pvfs_doseas_load(pvfs, name, fd, ealist);
     248         296 :         if (!NT_STATUS_IS_OK(status)) {
     249           1 :                 return status;
     250             :         }
     251             : 
     252         886 :         for (j=0;j<num_eas;j++) {
     253         591 :                 struct ea_struct *ea = &eas[j];
     254             :                 /* see if its already there */
     255         897 :                 for (i=0;i<ealist->num_eas;i++) {
     256         309 :                         if (strcasecmp_m(ealist->eas[i].name, ea->name.s) == 0) {
     257           3 :                                 ealist->eas[i].value = ea->value;
     258           3 :                                 break;
     259             :                         }
     260             :                 }
     261             : 
     262         591 :                 if (i==ealist->num_eas) {
     263             :                         /* add it */
     264         588 :                         ealist->eas = talloc_realloc(ealist, ealist->eas, 
     265             :                                                        struct xattr_EA, 
     266             :                                                        ealist->num_eas+1);
     267         588 :                         if (ealist->eas == NULL) {
     268           0 :                                 return NT_STATUS_NO_MEMORY;
     269             :                         }
     270         588 :                         ealist->eas[i].name = ea->name.s;
     271         588 :                         ealist->eas[i].value = ea->value;
     272         588 :                         ealist->num_eas++;
     273             :                 }
     274             :         }
     275             :         
     276             :         /* pull out any null EAs */
     277         892 :         for (i=0;i<ealist->num_eas;i++) {
     278         597 :                 if (ealist->eas[i].value.length == 0) {
     279           2 :                         memmove(&ealist->eas[i],
     280           2 :                                 &ealist->eas[i+1],
     281           2 :                                 (ealist->num_eas-(i+1)) * sizeof(ealist->eas[i]));
     282           2 :                         ealist->num_eas--;
     283           2 :                         i--;
     284             :                 }
     285             :         }
     286             : 
     287         295 :         status = pvfs_doseas_save(pvfs, name, fd, ealist);
     288         295 :         if (!NT_STATUS_IS_OK(status)) {
     289           0 :                 return status;
     290             :         }
     291             : 
     292         295 :         notify_trigger(pvfs->notify_context, 
     293             :                        NOTIFY_ACTION_MODIFIED, 
     294             :                        FILE_NOTIFY_CHANGE_EA,
     295         295 :                        name->full_name);
     296             : 
     297         295 :         name->dos.ea_size = 4;
     298         890 :         for (i=0;i<ealist->num_eas;i++) {
     299         595 :                 name->dos.ea_size += 4 + strlen(ealist->eas[i].name)+1 + 
     300         595 :                         ealist->eas[i].value.length;
     301             :         }
     302             : 
     303             :         /* update the ea_size attrib */
     304         295 :         return pvfs_dosattrib_save(pvfs, name, fd);
     305             : }
     306             : 
     307             : /*
     308             :   set info on a open file
     309             : */
     310        2414 : NTSTATUS pvfs_setfileinfo(struct ntvfs_module_context *ntvfs,
     311             :                           struct ntvfs_request *req, 
     312             :                           union smb_setfileinfo *info)
     313             : {
     314        2414 :         struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
     315             :                                   struct pvfs_state);
     316           0 :         struct pvfs_file *f;
     317           0 :         struct pvfs_file_handle *h;
     318           0 :         struct pvfs_filename newstats;
     319           0 :         NTSTATUS status;
     320           0 :         uint32_t access_needed;
     321        2414 :         uint32_t change_mask = 0;
     322             : 
     323        2414 :         f = pvfs_find_fd(pvfs, req, info->generic.in.file.ntvfs);
     324        2414 :         if (!f) {
     325           0 :                 return NT_STATUS_INVALID_HANDLE;
     326             :         }
     327             : 
     328        2414 :         h = f->handle;
     329             : 
     330        2414 :         access_needed = pvfs_setfileinfo_access(info);
     331        2414 :         if ((f->access_mask & access_needed) != access_needed) {
     332         266 :                 return NT_STATUS_ACCESS_DENIED;
     333             :         }
     334             : 
     335             :         /* update the file information */
     336        2148 :         status = pvfs_resolve_name_handle(pvfs, h);
     337        2148 :         if (!NT_STATUS_IS_OK(status)) {
     338           0 :                 return status;
     339             :         }
     340             : 
     341             :         /* we take a copy of the current file stats, then update
     342             :            newstats in each of the elements below. At the end we
     343             :            compare, and make any changes needed */
     344        2148 :         newstats = *h->name;
     345             : 
     346        2148 :         switch (info->generic.level) {
     347           0 :         case RAW_SFILEINFO_SETATTR:
     348           0 :                 if (!null_time(info->setattr.in.write_time)) {
     349           0 :                         unix_to_nt_time(&newstats.dos.write_time, info->setattr.in.write_time);
     350             :                 }
     351           0 :                 if (info->setattr.in.attrib != FILE_ATTRIBUTE_NORMAL) {
     352           0 :                         newstats.dos.attrib = info->setattr.in.attrib;
     353             :                 }
     354           0 :                 break;
     355             : 
     356           9 :         case RAW_SFILEINFO_SETATTRE:
     357             :         case RAW_SFILEINFO_STANDARD:
     358           9 :                 if (!null_time(info->setattre.in.create_time)) {
     359           2 :                         unix_to_nt_time(&newstats.dos.create_time, info->setattre.in.create_time);
     360             :                 }
     361           9 :                 if (!null_time(info->setattre.in.access_time)) {
     362           2 :                         unix_to_nt_time(&newstats.dos.access_time, info->setattre.in.access_time);
     363             :                 }
     364           9 :                 if (!null_time(info->setattre.in.write_time)) {
     365           6 :                         unix_to_nt_time(&newstats.dos.write_time, info->setattre.in.write_time);
     366             :                 }
     367           9 :                 break;
     368             : 
     369         283 :         case RAW_SFILEINFO_EA_SET:
     370        1277 :                 return pvfs_setfileinfo_ea_set(pvfs, h->name, h->fd,
     371         283 :                                                info->ea_set.in.num_eas,
     372             :                                                info->ea_set.in.eas);
     373             : 
     374         524 :         case RAW_SFILEINFO_BASIC_INFO:
     375             :         case RAW_SFILEINFO_BASIC_INFORMATION:
     376         524 :                 if (!null_nttime(info->basic_info.in.create_time)) {
     377         323 :                         newstats.dos.create_time = info->basic_info.in.create_time;
     378             :                 }
     379         524 :                 if (!null_nttime(info->basic_info.in.access_time)) {
     380         322 :                         newstats.dos.access_time = info->basic_info.in.access_time;
     381             :                 }
     382         524 :                 if (!null_nttime(info->basic_info.in.write_time)) {
     383         327 :                         newstats.dos.write_time = info->basic_info.in.write_time;
     384             :                 }
     385         524 :                 if (!null_nttime(info->basic_info.in.change_time)) {
     386          43 :                         newstats.dos.change_time = info->basic_info.in.change_time;
     387             :                 }
     388         524 :                 if (info->basic_info.in.attrib != 0) {
     389         113 :                         newstats.dos.attrib = info->basic_info.in.attrib;
     390             :                 }
     391         524 :                 break;
     392             : 
     393         324 :         case RAW_SFILEINFO_DISPOSITION_INFO:
     394             :         case RAW_SFILEINFO_DISPOSITION_INFORMATION:
     395         324 :                 return pvfs_set_delete_on_close(pvfs, req, f, 
     396         324 :                                                 info->disposition_info.in.delete_on_close);
     397             : 
     398           9 :         case RAW_SFILEINFO_ALLOCATION_INFO:
     399             :         case RAW_SFILEINFO_ALLOCATION_INFORMATION:
     400           9 :                 status = pvfs_break_level2_oplocks(f);
     401           9 :                 NT_STATUS_NOT_OK_RETURN(status);
     402             : 
     403           9 :                 newstats.dos.alloc_size = info->allocation_info.in.alloc_size;
     404           9 :                 if (newstats.dos.alloc_size < newstats.st.st_size) {
     405           3 :                         newstats.st.st_size = newstats.dos.alloc_size;
     406             :                 }
     407           9 :                 newstats.dos.alloc_size = pvfs_round_alloc_size(pvfs, 
     408             :                                                                 newstats.dos.alloc_size);
     409           9 :                 break;
     410             : 
     411         316 :         case RAW_SFILEINFO_END_OF_FILE_INFO:
     412             :         case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
     413         316 :                 status = pvfs_break_level2_oplocks(f);
     414         316 :                 NT_STATUS_NOT_OK_RETURN(status);
     415             : 
     416         316 :                 newstats.st.st_size = info->end_of_file_info.in.size;
     417         316 :                 break;
     418             : 
     419           9 :         case RAW_SFILEINFO_POSITION_INFORMATION:
     420           9 :                 h->position = info->position_information.in.position;
     421           9 :                 break;
     422             : 
     423           3 :         case RAW_SFILEINFO_FULL_EA_INFORMATION:
     424           3 :                 return pvfs_setfileinfo_ea_set(pvfs, h->name, h->fd,
     425           3 :                                 info->full_ea_information.in.eas.num_eas,
     426             :                                 info->full_ea_information.in.eas.eas);
     427             : 
     428           8 :         case RAW_SFILEINFO_MODE_INFORMATION:
     429             :                 /* this one is a puzzle */
     430           8 :                 if (info->mode_information.in.mode != 0 &&
     431           6 :                     info->mode_information.in.mode != 2 &&
     432           4 :                     info->mode_information.in.mode != 4 &&
     433           4 :                     info->mode_information.in.mode != 6) {
     434           4 :                         return NT_STATUS_INVALID_PARAMETER;
     435             :                 }
     436           4 :                 h->mode = info->mode_information.in.mode;
     437           4 :                 break;
     438             : 
     439          35 :         case RAW_SFILEINFO_RENAME_INFORMATION:
     440             :         case RAW_SFILEINFO_RENAME_INFORMATION_SMB2:
     441          35 :                 return pvfs_setfileinfo_rename(pvfs, req, h->name, f->handle->fd,
     442             :                                                &h->odb_locking_key,
     443             :                                                info);
     444             : 
     445         628 :         case RAW_SFILEINFO_SEC_DESC:
     446         628 :                 notify_trigger(pvfs->notify_context, 
     447             :                                NOTIFY_ACTION_MODIFIED, 
     448             :                                FILE_NOTIFY_CHANGE_SECURITY,
     449         628 :                                h->name->full_name);
     450         628 :                 return pvfs_acl_set(pvfs, req, h->name, h->fd, f->access_mask, info);
     451             : 
     452           0 :         default:
     453           0 :                 return NT_STATUS_INVALID_LEVEL;
     454             :         }
     455             : 
     456             :         /* possibly change the file size */
     457         871 :         if (newstats.st.st_size != h->name->st.st_size) {
     458         318 :                 if (h->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) {
     459           0 :                         return NT_STATUS_FILE_IS_A_DIRECTORY;
     460             :                 }
     461         318 :                 if (h->name->stream_name) {
     462           3 :                         status = pvfs_stream_truncate(pvfs, h->name, h->fd, newstats.st.st_size);
     463           3 :                         if (!NT_STATUS_IS_OK(status)) {
     464           0 :                                 return status;
     465             :                         }
     466             :                         
     467           3 :                         change_mask |= FILE_NOTIFY_CHANGE_STREAM_SIZE;
     468             :                 } else {
     469           0 :                         int ret;
     470         315 :                         if (f->access_mask & 
     471             :                             (SEC_FILE_WRITE_DATA|SEC_FILE_APPEND_DATA)) {
     472         315 :                                 ret = ftruncate(h->fd, newstats.st.st_size);
     473             :                         } else {
     474           0 :                                 ret = truncate(h->name->full_name, newstats.st.st_size);
     475             :                         }
     476         315 :                         if (ret == -1) {
     477           0 :                                 return pvfs_map_errno(pvfs, errno);
     478             :                         }
     479         315 :                         change_mask |= FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_ATTRIBUTES;
     480             :                 }
     481             :         }
     482             : 
     483             :         /* possibly change the file timestamps */
     484         871 :         if (newstats.dos.create_time != h->name->dos.create_time) {
     485         322 :                 change_mask |= FILE_NOTIFY_CHANGE_CREATION;
     486             :         }
     487         871 :         if (newstats.dos.access_time != h->name->dos.access_time) {
     488         321 :                 change_mask |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
     489             :         }
     490         871 :         if (newstats.dos.write_time != h->name->dos.write_time) {
     491         330 :                 change_mask |= FILE_NOTIFY_CHANGE_LAST_WRITE;
     492             :         }
     493         871 :         if ((change_mask & FILE_NOTIFY_CHANGE_LAST_ACCESS) ||
     494         550 :             (change_mask & FILE_NOTIFY_CHANGE_LAST_WRITE)) {
     495           0 :                 struct timeval tv[2];
     496             : 
     497         363 :                 nttime_to_timeval(&tv[0], newstats.dos.access_time);
     498         363 :                 nttime_to_timeval(&tv[1], newstats.dos.write_time);
     499             : 
     500         363 :                 if (!timeval_is_zero(&tv[0]) || !timeval_is_zero(&tv[1])) {
     501         363 :                         if (utimes(h->name->full_name, tv) == -1) {
     502           0 :                                 DEBUG(0,("pvfs_setfileinfo: utimes() failed '%s' - %s\n",
     503             :                                          h->name->full_name, strerror(errno)));
     504           0 :                                 return pvfs_map_errno(pvfs, errno);
     505             :                         }
     506             :                 }
     507             :         }
     508         871 :         if (change_mask & FILE_NOTIFY_CHANGE_LAST_WRITE) {
     509           0 :                 struct odb_lock *lck;
     510             : 
     511         330 :                 lck = odb_lock(req, h->pvfs->odb_context, &h->odb_locking_key);
     512         330 :                 if (lck == NULL) {
     513           0 :                         DEBUG(0,("Unable to lock opendb for write time update\n"));
     514           0 :                         return NT_STATUS_INTERNAL_ERROR;
     515             :                 }
     516             : 
     517         330 :                 status = odb_set_write_time(lck, newstats.dos.write_time, true);
     518         330 :                 if (!NT_STATUS_IS_OK(status)) {
     519           0 :                         DEBUG(0,("Unable to update write time: %s\n",
     520             :                                 nt_errstr(status)));
     521           0 :                         talloc_free(lck);
     522           0 :                         return status;
     523             :                 }
     524             : 
     525         330 :                 talloc_free(lck);
     526             : 
     527         330 :                 h->write_time.update_forced = true;
     528         330 :                 h->write_time.update_on_close = false;
     529         330 :                 talloc_free(h->write_time.update_event);
     530         330 :                 h->write_time.update_event = NULL;
     531             :         }
     532             : 
     533             :         /* possibly change the attribute */
     534         871 :         if (newstats.dos.attrib != h->name->dos.attrib) {
     535           0 :                 mode_t mode;
     536         107 :                 if ((newstats.dos.attrib & FILE_ATTRIBUTE_DIRECTORY) &&
     537           4 :                     !(h->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
     538           2 :                         return NT_STATUS_INVALID_PARAMETER;
     539             :                 }
     540         105 :                 mode = pvfs_fileperms(pvfs, newstats.dos.attrib);
     541         105 :                 if (!(h->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
     542          86 :                         if (pvfs_sys_fchmod(pvfs, h->fd, mode, h->name->allow_override) == -1) {
     543           0 :                                 return pvfs_map_errno(pvfs, errno);
     544             :                         }
     545             :                 }
     546         105 :                 change_mask |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
     547             :         }
     548             : 
     549         869 :         *h->name = newstats;
     550             : 
     551         869 :         notify_trigger(pvfs->notify_context, 
     552             :                        NOTIFY_ACTION_MODIFIED, 
     553             :                        change_mask,
     554         869 :                        h->name->full_name);
     555             : 
     556         869 :         return pvfs_dosattrib_save(pvfs, h->name, h->fd);
     557             : }
     558             : 
     559             : /*
     560             :   retry an open after a sharing violation
     561             : */
     562           5 : static void pvfs_retry_setpathinfo(struct pvfs_odb_retry *r,
     563             :                                    struct ntvfs_module_context *ntvfs,
     564             :                                    struct ntvfs_request *req,
     565             :                                    void *_info,
     566             :                                    void *private_data,
     567             :                                    enum pvfs_wait_notice reason)
     568             : {
     569           5 :         union smb_setfileinfo *info = talloc_get_type(_info,
     570             :                                       union smb_setfileinfo);
     571           5 :         NTSTATUS status = NT_STATUS_INTERNAL_ERROR;
     572             : 
     573           5 :         talloc_free(r);
     574             : 
     575           5 :         switch (reason) {
     576           0 :         case PVFS_WAIT_CANCEL:
     577             : /*TODO*/
     578           0 :                 status = NT_STATUS_CANCELLED;
     579           5 :                 break;
     580           2 :         case PVFS_WAIT_TIMEOUT:
     581             :                 /* if it timed out, then give the failure
     582             :                    immediately */
     583             : /*TODO*/
     584           2 :                 status = NT_STATUS_SHARING_VIOLATION;
     585           2 :                 break;
     586           3 :         case PVFS_WAIT_EVENT:
     587             : 
     588             :                 /* try the open again, which could trigger another retry setup
     589             :                    if it wants to, so we have to unmark the async flag so we
     590             :                    will know if it does a second async reply */
     591           3 :                 req->async_states->state &= ~NTVFS_ASYNC_STATE_ASYNC;
     592             : 
     593           3 :                 status = pvfs_setpathinfo(ntvfs, req, info);
     594           3 :                 if (req->async_states->state & NTVFS_ASYNC_STATE_ASYNC) {
     595             :                         /* the 2nd try also replied async, so we don't send
     596             :                            the reply yet */
     597           0 :                         return;
     598             :                 }
     599             : 
     600             :                 /* re-mark it async, just in case someone up the chain does
     601             :                    paranoid checking */
     602           3 :                 req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
     603           3 :                 break;
     604             :         }
     605             : 
     606             :         /* send the reply up the chain */
     607           5 :         req->async_states->status = status;
     608           5 :         req->async_states->send_fn(req);
     609             : }
     610             : 
     611             : /*
     612             :   setup for a unlink retry after a sharing violation
     613             :   or a non granted oplock
     614             : */
     615           5 : static NTSTATUS pvfs_setpathinfo_setup_retry(struct ntvfs_module_context *ntvfs,
     616             :                                              struct ntvfs_request *req,
     617             :                                              union smb_setfileinfo *info,
     618             :                                              struct odb_lock *lck,
     619             :                                              NTSTATUS status)
     620             : {
     621           5 :         struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
     622             :                                   struct pvfs_state);
     623           0 :         struct timeval end_time;
     624             : 
     625           5 :         if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) {
     626           2 :                 end_time = timeval_add(&req->statistics.request_time,
     627             :                                        0, pvfs->sharing_violation_delay);
     628           3 :         } else if (NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) {
     629           3 :                 end_time = timeval_add(&req->statistics.request_time,
     630             :                                        pvfs->oplock_break_timeout, 0);
     631             :         } else {
     632           0 :                 return NT_STATUS_INTERNAL_ERROR;
     633             :         }
     634             : 
     635           5 :         return pvfs_odb_retry_setup(ntvfs, req, lck, end_time, info, NULL,
     636             :                                     pvfs_retry_setpathinfo);
     637             : }
     638             : 
     639             : /*
     640             :   set info on a pathname
     641             : */
     642         443 : NTSTATUS pvfs_setpathinfo(struct ntvfs_module_context *ntvfs,
     643             :                           struct ntvfs_request *req, union smb_setfileinfo *info)
     644             : {
     645         443 :         struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
     646             :                                   struct pvfs_state);
     647           0 :         struct pvfs_filename *name;
     648           0 :         struct pvfs_filename newstats;
     649           0 :         NTSTATUS status;
     650           0 :         uint32_t access_needed;
     651         443 :         uint32_t change_mask = 0;
     652         443 :         struct odb_lock *lck = NULL;
     653           0 :         DATA_BLOB odb_locking_key;
     654             : 
     655             :         /* resolve the cifs name to a posix name */
     656         443 :         status = pvfs_resolve_name(pvfs, req, info->generic.in.file.path, 
     657             :                                    PVFS_RESOLVE_STREAMS, &name);
     658         443 :         if (!NT_STATUS_IS_OK(status)) {
     659          64 :                 return status;
     660             :         }
     661             : 
     662         379 :         if (!name->exists) {
     663         166 :                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
     664             :         }
     665             : 
     666         213 :         access_needed = pvfs_setfileinfo_access(info);
     667         213 :         status = pvfs_access_check_simple(pvfs, req, name, access_needed);
     668         213 :         if (!NT_STATUS_IS_OK(status)) {
     669           0 :                 return status;
     670             :         }
     671             : 
     672             :         /* we take a copy of the current file stats, then update
     673             :            newstats in each of the elements below. At the end we
     674             :            compare, and make any changes needed */
     675         213 :         newstats = *name;
     676             : 
     677         213 :         switch (info->generic.level) {
     678         151 :         case RAW_SFILEINFO_SETATTR:
     679         151 :                 if (!null_time(info->setattr.in.write_time)) {
     680          36 :                         unix_to_nt_time(&newstats.dos.write_time, info->setattr.in.write_time);
     681             :                 }
     682         151 :                 if (info->setattr.in.attrib == 0) {
     683          77 :                         newstats.dos.attrib = FILE_ATTRIBUTE_NORMAL;
     684          74 :                 } else if (info->setattr.in.attrib != FILE_ATTRIBUTE_NORMAL) {
     685          41 :                         newstats.dos.attrib = info->setattr.in.attrib;
     686             :                 }
     687         151 :                 break;
     688             : 
     689           2 :         case RAW_SFILEINFO_SETATTRE:
     690             :         case RAW_SFILEINFO_STANDARD:
     691           2 :                 if (!null_time(info->setattre.in.create_time)) {
     692           0 :                         unix_to_nt_time(&newstats.dos.create_time, info->setattre.in.create_time);
     693             :                 }
     694           2 :                 if (!null_time(info->setattre.in.access_time)) {
     695           0 :                         unix_to_nt_time(&newstats.dos.access_time, info->setattre.in.access_time);
     696             :                 }
     697           2 :                 if (!null_time(info->setattre.in.write_time)) {
     698           0 :                         unix_to_nt_time(&newstats.dos.write_time, info->setattre.in.write_time);
     699             :                 }
     700           2 :                 break;
     701             : 
     702           4 :         case RAW_SFILEINFO_EA_SET:
     703          30 :                 return pvfs_setfileinfo_ea_set(pvfs, name, -1, 
     704           4 :                                                info->ea_set.in.num_eas,
     705             :                                                info->ea_set.in.eas);
     706             : 
     707          22 :         case RAW_SFILEINFO_BASIC_INFO:
     708             :         case RAW_SFILEINFO_BASIC_INFORMATION:
     709          22 :                 if (!null_nttime(info->basic_info.in.create_time)) {
     710           1 :                         newstats.dos.create_time = info->basic_info.in.create_time;
     711             :                 }
     712          22 :                 if (!null_nttime(info->basic_info.in.access_time)) {
     713           1 :                         newstats.dos.access_time = info->basic_info.in.access_time;
     714             :                 }
     715          22 :                 if (!null_nttime(info->basic_info.in.write_time)) {
     716           1 :                         newstats.dos.write_time = info->basic_info.in.write_time;
     717             :                 }
     718          22 :                 if (!null_nttime(info->basic_info.in.change_time)) {
     719           1 :                         newstats.dos.change_time = info->basic_info.in.change_time;
     720             :                 }
     721          22 :                 if (info->basic_info.in.attrib != 0) {
     722          19 :                         newstats.dos.attrib = info->basic_info.in.attrib;
     723             :                 }
     724          22 :                 break;
     725             : 
     726           4 :         case RAW_SFILEINFO_ALLOCATION_INFO:
     727             :         case RAW_SFILEINFO_ALLOCATION_INFORMATION:
     728           4 :                 status = pvfs_can_update_file_size(pvfs, req, name, &lck);
     729             :                 /*
     730             :                  * on a sharing violation we need to retry when the file is closed by
     731             :                  * the other user, or after 1 second
     732             :                  * on a non granted oplock we need to retry when the file is closed by
     733             :                  * the other user, or after 30 seconds
     734             :                 */
     735           4 :                 if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
     736           4 :                      NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) &&
     737           1 :                     (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
     738           1 :                         return pvfs_setpathinfo_setup_retry(pvfs->ntvfs, req, info, lck, status);
     739             :                 }
     740           3 :                 NT_STATUS_NOT_OK_RETURN(status);
     741             : 
     742           3 :                 if (info->allocation_info.in.alloc_size > newstats.dos.alloc_size) {
     743             :                         /* strange. Increasing the allocation size via setpathinfo 
     744             :                            should be silently ignored */
     745           2 :                         break;
     746             :                 }
     747           1 :                 newstats.dos.alloc_size = info->allocation_info.in.alloc_size;
     748           1 :                 if (newstats.dos.alloc_size < newstats.st.st_size) {
     749           1 :                         newstats.st.st_size = newstats.dos.alloc_size;
     750             :                 }
     751           1 :                 newstats.dos.alloc_size = pvfs_round_alloc_size(pvfs, 
     752             :                                                                 newstats.dos.alloc_size);
     753           1 :                 break;
     754             : 
     755           9 :         case RAW_SFILEINFO_END_OF_FILE_INFO:
     756             :         case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
     757           9 :                 status = pvfs_can_update_file_size(pvfs, req, name, &lck);
     758             :                 /*
     759             :                  * on a sharing violation we need to retry when the file is closed by
     760             :                  * the other user, or after 1 second
     761             :                  * on a non granted oplock we need to retry when the file is closed by
     762             :                  * the other user, or after 30 seconds
     763             :                 */
     764           9 :                 if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
     765           7 :                      NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) &&
     766           4 :                     (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
     767           4 :                         return pvfs_setpathinfo_setup_retry(pvfs->ntvfs, req, info, lck, status);
     768             :                 }
     769           5 :                 NT_STATUS_NOT_OK_RETURN(status);
     770             : 
     771           5 :                 newstats.st.st_size = info->end_of_file_info.in.size;
     772           5 :                 break;
     773             : 
     774           3 :         case RAW_SFILEINFO_MODE_INFORMATION:
     775           3 :                 if (info->mode_information.in.mode != 0 &&
     776           2 :                     info->mode_information.in.mode != 2 &&
     777           1 :                     info->mode_information.in.mode != 4 &&
     778           1 :                     info->mode_information.in.mode != 6) {
     779           1 :                         return NT_STATUS_INVALID_PARAMETER;
     780             :                 }
     781           2 :                 return NT_STATUS_OK;
     782             : 
     783          11 :         case RAW_SFILEINFO_RENAME_INFORMATION:
     784             :         case RAW_SFILEINFO_RENAME_INFORMATION_SMB2:
     785          11 :                 status = pvfs_locking_key(name, name, &odb_locking_key);
     786          11 :                 NT_STATUS_NOT_OK_RETURN(status);
     787          11 :                 status = pvfs_setfileinfo_rename(pvfs, req, name, -1,
     788             :                                                  &odb_locking_key, info);
     789          11 :                 NT_STATUS_NOT_OK_RETURN(status);
     790          10 :                 return NT_STATUS_OK;
     791             : 
     792           7 :         case RAW_SFILEINFO_DISPOSITION_INFO:
     793             :         case RAW_SFILEINFO_DISPOSITION_INFORMATION:
     794             :         case RAW_SFILEINFO_POSITION_INFORMATION:
     795           7 :                 return NT_STATUS_OK;
     796             : 
     797           0 :         default:
     798           0 :                 return NT_STATUS_INVALID_LEVEL;
     799             :         }
     800             : 
     801             :         /* possibly change the file size */
     802         183 :         if (newstats.st.st_size != name->st.st_size) {
     803           6 :                 if (name->stream_name) {
     804           0 :                         status = pvfs_stream_truncate(pvfs, name, -1, newstats.st.st_size);
     805           0 :                         if (!NT_STATUS_IS_OK(status)) {
     806           0 :                                 return status;
     807             :                         }
     808           6 :                 } else if (truncate(name->full_name, newstats.st.st_size) == -1) {
     809           0 :                         return pvfs_map_errno(pvfs, errno);
     810             :                 }
     811           6 :                 change_mask |= FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_ATTRIBUTES;
     812             :         }
     813             : 
     814             :         /* possibly change the file timestamps */
     815         183 :         if (newstats.dos.create_time != name->dos.create_time) {
     816           1 :                 change_mask |= FILE_NOTIFY_CHANGE_CREATION;
     817             :         }
     818         183 :         if (newstats.dos.access_time != name->dos.access_time) {
     819           1 :                 change_mask |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
     820             :         }
     821         183 :         if (newstats.dos.write_time != name->dos.write_time) {
     822          37 :                 change_mask |= FILE_NOTIFY_CHANGE_LAST_WRITE;
     823             :         }
     824         183 :         if ((change_mask & FILE_NOTIFY_CHANGE_LAST_ACCESS) ||
     825         182 :             (change_mask & FILE_NOTIFY_CHANGE_LAST_WRITE)) {
     826           0 :                 struct timeval tv[2];
     827             : 
     828          37 :                 nttime_to_timeval(&tv[0], newstats.dos.access_time);
     829          37 :                 nttime_to_timeval(&tv[1], newstats.dos.write_time);
     830             : 
     831          37 :                 if (!timeval_is_zero(&tv[0]) || !timeval_is_zero(&tv[1])) {
     832          37 :                         if (utimes(name->full_name, tv) == -1) {
     833           0 :                                 DEBUG(0,("pvfs_setpathinfo: utimes() failed '%s' - %s\n",
     834             :                                          name->full_name, strerror(errno)));
     835           0 :                                 return pvfs_map_errno(pvfs, errno);
     836             :                         }
     837             :                 }
     838             :         }
     839         183 :         if (change_mask & FILE_NOTIFY_CHANGE_LAST_WRITE) {
     840          37 :                 if (lck == NULL) {
     841           0 :                         DATA_BLOB lkey;
     842          37 :                         status = pvfs_locking_key(name, name, &lkey);
     843          37 :                         NT_STATUS_NOT_OK_RETURN(status);
     844             : 
     845          37 :                         lck = odb_lock(req, pvfs->odb_context, &lkey);
     846          37 :                         data_blob_free(&lkey);
     847          37 :                         if (lck == NULL) {
     848           0 :                                 DEBUG(0,("Unable to lock opendb for write time update\n"));
     849           0 :                                 return NT_STATUS_INTERNAL_ERROR;
     850             :                         }
     851             :                 }
     852             : 
     853          37 :                 status = odb_set_write_time(lck, newstats.dos.write_time, true);
     854          37 :                 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
     855             :                         /* it could be that nobody has opened the file */
     856           0 :                 } else if (!NT_STATUS_IS_OK(status)) {
     857           0 :                         DEBUG(0,("Unable to update write time: %s\n",
     858             :                                 nt_errstr(status)));
     859           0 :                         return status;
     860             :                 }
     861             :         }
     862             : 
     863             :         /* possibly change the attribute */
     864         183 :         newstats.dos.attrib |= (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY);
     865         183 :         if (newstats.dos.attrib != name->dos.attrib) {
     866         135 :                 mode_t mode = pvfs_fileperms(pvfs, newstats.dos.attrib);
     867         135 :                 if (pvfs_sys_chmod(pvfs, name->full_name, mode, name->allow_override) == -1) {
     868           0 :                         return pvfs_map_errno(pvfs, errno);
     869             :                 }
     870         135 :                 change_mask |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
     871             :         }
     872             : 
     873         183 :         *name = newstats;
     874             : 
     875         183 :         if (change_mask != 0) {
     876         174 :                 notify_trigger(pvfs->notify_context, 
     877             :                                NOTIFY_ACTION_MODIFIED, 
     878             :                                change_mask,
     879         174 :                                name->full_name);
     880             :         }
     881             : 
     882         183 :         return pvfs_dosattrib_save(pvfs, name, -1);
     883             : }
     884             : 

Generated by: LCOV version 1.14