r3240: - update the rules for what error codes should be given on the
authorAndrew Tridgell <tridge@samba.org>
Tue, 26 Oct 2004 05:39:54 +0000 (05:39 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 18:04:45 +0000 (13:04 -0500)
  different type of unlink an seach mismatches

- wildcard directory listings that have attribute
  FILE_ATTRIBUTE_DIRECTORY and match "." or ".." should be failed.

- don't set the write_time on SMBclose unless it is non-zero

- added much better support for setfileinfo and setpathinfo in pvfs

- better (and more efficient) handling of .. and . components in filenames

source/ntvfs/posix/pvfs_open.c
source/ntvfs/posix/pvfs_read.c
source/ntvfs/posix/pvfs_resolve.c
source/ntvfs/posix/pvfs_search.c
source/ntvfs/posix/pvfs_setfileinfo.c
source/ntvfs/posix/pvfs_unlink.c
source/ntvfs/posix/pvfs_util.c

index 5e162ad147743a3cc484a2a2b6d302e7b2451f31..ffd1520b07010635548a5082814cdf475bab7b7d 100644 (file)
@@ -643,9 +643,11 @@ NTSTATUS pvfs_close(struct ntvfs_module_context *ntvfs,
                return NT_STATUS_INVALID_HANDLE;
        }
 
-       unix_times.actime = 0;
-       unix_times.modtime = io->close.in.write_time;
-       utime(f->name->full_name, &unix_times);
+       if (!null_time(io->close.in.write_time)) {
+               unix_times.actime = 0;
+               unix_times.modtime = io->close.in.write_time;
+               utime(f->name->full_name, &unix_times);
+       }
        
        if (f->fd != -1 && 
            close(f->fd) == -1) {
index eb821d1f3152c393981cdd91f0e5cda9adc6e279..ee750a138fe97becd5192145bb61bf7420e9e85b 100644 (file)
@@ -51,6 +51,12 @@ NTSTATUS pvfs_read(struct ntvfs_module_context *ntvfs,
                return NT_STATUS_ACCESS_VIOLATION;
        }
 
+       /* this matches w2k3 behaviour for attempted large reads */
+       if (rd->readx.in.maxcnt > UINT16_MAX) {
+               ret = 0;
+               goto done_read;
+       }
+       
        status = pvfs_check_lock(pvfs, f, req->smbpid, 
                                 rd->readx.in.offset,
                                 rd->readx.in.maxcnt,
@@ -67,10 +73,11 @@ NTSTATUS pvfs_read(struct ntvfs_module_context *ntvfs,
                return pvfs_map_errno(pvfs, errno);
        }
 
+done_read:
        f->position = f->seek_offset = rd->readx.in.offset + ret;
 
        rd->readx.out.nread = ret;
-       rd->readx.out.remaining = 0; /* should fill this in? */
+       rd->readx.out.remaining = 0xFFFF;
        rd->readx.out.compaction_mode = 0; 
 
        return NT_STATUS_OK;
index 271dbc2b9a11e46d840d61795ff5dd714a29c543..a7a693b21703f7e1116c11efc9d3a96789425212 100644 (file)
@@ -194,7 +194,6 @@ static NTSTATUS pvfs_unix_path(struct pvfs_state *pvfs, const char *cifs_name,
 {
        char *ret, *p, *p_start;
        size_t len;
-       int num_components=0;
 
        name->original_name = talloc_strdup(name, cifs_name);
        name->stream_name = NULL;
@@ -242,7 +241,6 @@ static NTSTATUS pvfs_unix_path(struct pvfs_state *pvfs, const char *cifs_name,
                                return NT_STATUS_ILLEGAL_CHARACTER;
                        }
                        *p = '/';
-                       num_components++;
                        break;
                case ':':
                        if (!(flags & PVFS_RESOLVE_STREAMS)) {
@@ -268,14 +266,19 @@ static NTSTATUS pvfs_unix_path(struct pvfs_state *pvfs, const char *cifs_name,
                case '|':
                        return NT_STATUS_ILLEGAL_CHARACTER;
                case '.':
-                       if (p[1] != '.' ||
-                           (p[2] != '\\' && p[2] != 0) ||
-                           (p != p_start && p[-1] != '/')) {
-                               break;
+                       /* see if it is definately a .. or
+                          . component. If it is then fail here, and
+                          let the next layer up try again after
+                          pvfs_reduce_name() if it wants to. This is
+                          much more efficient on average than always
+                          scanning for these separately */
+                       if (p[1] == '.' && 
+                           (p[2] == 0 || p[2] == '\\') &&
+                           (p == p_start || p[-1] == '/')) {
+                               return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
                        }
-                       /* its definately a .. component */
-                       num_components--;
-                       if (num_components <= 0) {
+                       if ((p[1] == 0 || p[1] == '\\') &&
+                           (p == p_start || p[-1] == '/')) {
                                return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
                        }
                        break;
@@ -290,6 +293,92 @@ static NTSTATUS pvfs_unix_path(struct pvfs_state *pvfs, const char *cifs_name,
 }
 
 
+/*
+  reduce a name that contains .. components or repeated \ separators
+  return NULL if it can't be reduced
+*/
+const char *pvfs_reduce_name(TALLOC_CTX *mem_ctx, const char *fname)
+{
+       codepoint_t c;
+       size_t c_size, len;
+       int i, num_components;
+       char **components;
+       char *p, *s, *ret;
+
+       s = talloc_strdup(mem_ctx, fname);
+       if (s == NULL) return NULL;
+
+       for (num_components=1, p=s; *p; p += c_size) {
+               c = next_codepoint(p, &c_size);
+               if (c == '\\') num_components++;
+       }
+       if (num_components < 2) {
+               talloc_free(s);
+               return NULL;
+       }
+
+       components = talloc_array_p(s, char *, num_components+1);
+       if (components == NULL) {
+               talloc_free(s);
+               return NULL;
+       }
+
+       components[0] = s;
+       for (i=0, p=s; *p; p += c_size) {
+               c = next_codepoint(p, &c_size);
+               if (c == '\\') {
+                       *p = 0;
+                       components[++i] = p+1;
+               }
+       }
+       components[i+1] = NULL;
+
+       /* remove any null components */
+       for (i=0;components[i];i++) {
+               if (strcmp(components[i], "") == 0 ||
+                   strcmp(components[i], ".") == 0) {
+                       memmove(&components[i], &components[i+1], 
+                               sizeof(char *)*(num_components-i));
+                       i--;
+               }
+               if (strcmp(components[i], "..") == 0) {
+                       if (i < 1) return NULL;
+                       memmove(&components[i-1], &components[i+1], 
+                               sizeof(char *)*(num_components-(i+1)));
+                       i -= 2;
+               }
+       }
+
+       if (components[0] == NULL) {
+               talloc_free(s);
+               return talloc_strdup(mem_ctx, "\\");
+       }
+
+       for (len=i=0;components[i];i++) {
+               len += strlen(components[i]) + 1;
+       }
+
+       /* rebuild the name */
+       ret = talloc(mem_ctx, len+1);
+       if (ret == NULL) {
+               talloc_free(s);
+               return NULL;
+       }
+
+       for (len=0,i=0;components[i];i++) {
+               size_t len1 = strlen(components[i]);
+               ret[len] = '\\';
+               memcpy(ret+len+1, components[i], len1);
+               len += len1 + 1;
+       }       
+       ret[len] = 0;
+
+       talloc_free(s);
+       
+       return ret;
+}
+
+
 /*
   resolve a name from relative client format to a struct pvfs_filename
   the memory for the filename is made as a talloc child of 'name'
@@ -316,6 +405,15 @@ NTSTATUS pvfs_resolve_name(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
        /* do the basic conversion to a unix formatted path,
           also checking for allowable characters */
        status = pvfs_unix_path(pvfs, cifs_name, flags, *name);
+
+       if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD)) {
+               /* it might contain .. components which need to be reduced */
+               cifs_name = pvfs_reduce_name(*name, cifs_name);
+               if (cifs_name) {
+                       status = pvfs_unix_path(pvfs, cifs_name, flags, *name);
+               }
+       }
+
        if (!NT_STATUS_IS_OK(status)) {
                return status;
        }
index ff9ad20b4310c04c2ee9db7d125745bfe977eb67..61e4651c5f56b457864b12e22a5e01cc97c42668 100644 (file)
@@ -54,8 +54,9 @@ static NTSTATUS fill_search_info(struct pvfs_state *pvfs,
                return status;
        }
 
-       if (!pvfs_match_attrib(pvfs, name, search->search_attrib, search->must_attrib)) {
-               return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+       status = pvfs_match_attrib(pvfs, name, search->search_attrib, search->must_attrib);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
        }
 
        switch (level) {
index 6a9e2003f47f71b8bab6030cf590d223f34403f7..71a9ce12665304a549fd96617b87e7859090a424 100644 (file)
@@ -34,37 +34,64 @@ NTSTATUS pvfs_setfileinfo(struct ntvfs_module_context *ntvfs,
        struct utimbuf unix_times;
        struct pvfs_file *f;
        uint32_t create_options;
+       struct pvfs_filename newstats;
+       NTSTATUS status;
 
        f = pvfs_find_fd(pvfs, req, info->generic.file.fnum);
        if (!f) {
                return NT_STATUS_INVALID_HANDLE;
        }
 
+       /* update the file information */
+       status = pvfs_resolve_name_fd(pvfs, f->fd, f->name);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       /* we take a copy of the current file stats, then update
+          newstats in each of the elements below. At the end we
+          compare, and make any changes needed */
+       newstats = *f->name;
+
        switch (info->generic.level) {
-       case RAW_SFILEINFO_END_OF_FILE_INFO:
-       case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
-               if (ftruncate(f->fd,
-                             info->end_of_file_info.in.size) == -1) {
-                       return pvfs_map_errno(pvfs, errno);
+       case RAW_SFILEINFO_SETATTR:
+               if (!null_time(info->setattr.in.write_time)) {
+                       unix_to_nt_time(&newstats.dos.write_time, info->setattr.in.write_time);
                }
-               break;
+               if (info->setattr.in.attrib != FILE_ATTRIBUTE_NORMAL) {
+                       newstats.dos.attrib = info->setattr.in.attrib;
+               }
+               break;
 
        case RAW_SFILEINFO_SETATTRE:
-               unix_times.actime = info->setattre.in.access_time;
-               unix_times.modtime = info->setattre.in.write_time;
-       
-               if (unix_times.actime == 0 && unix_times.modtime == 0) {
-                       break;
-               } 
-
-               /* set modify time = to access time if modify time was 0 */
-               if (unix_times.actime != 0 && unix_times.modtime == 0) {
-                       unix_times.modtime = unix_times.actime;
+       case RAW_SFILEINFO_STANDARD:
+               if (!null_time(info->setattre.in.create_time)) {
+                       unix_to_nt_time(&newstats.dos.create_time, info->setattre.in.create_time);
+               }
+               if (!null_time(info->setattre.in.access_time)) {
+                       unix_to_nt_time(&newstats.dos.access_time, info->setattre.in.access_time);
                }
+               if (!null_time(info->setattre.in.write_time)) {
+                       unix_to_nt_time(&newstats.dos.write_time, info->setattre.in.write_time);
+               }
+               break;
 
-               /* Set the date on this file */
-               if (utime(f->name->full_name, &unix_times) == -1) {
-                       return pvfs_map_errno(pvfs, errno);
+       case RAW_SFILEINFO_BASIC_INFO:
+       case RAW_SFILEINFO_BASIC_INFORMATION:
+               if (info->basic_info.in.create_time) {
+                       newstats.dos.create_time = info->basic_info.in.create_time;
+               }
+               if (info->basic_info.in.access_time) {
+                       newstats.dos.access_time = info->basic_info.in.access_time;
+               }
+               if (info->basic_info.in.write_time) {
+                       newstats.dos.write_time = info->basic_info.in.write_time;
+               }
+               if (info->basic_info.in.change_time) {
+                       newstats.dos.change_time = info->basic_info.in.change_time;
+               }
+               if (info->basic_info.in.attrib != FILE_ATTRIBUTE_NORMAL) {
+                       newstats.dos.attrib = info->basic_info.in.attrib;
                }
                break;
 
@@ -81,11 +108,54 @@ NTSTATUS pvfs_setfileinfo(struct ntvfs_module_context *ntvfs,
                }
                return pvfs_change_create_options(pvfs, req, f, create_options);
 
+       case RAW_SFILEINFO_ALLOCATION_INFO:
+       case RAW_SFILEINFO_ALLOCATION_INFORMATION:
+               newstats.dos.alloc_size = info->allocation_info.in.alloc_size;
+               break;
+
+       case RAW_SFILEINFO_END_OF_FILE_INFO:
+       case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
+               newstats.st.st_size = info->end_of_file_info.in.size;
+               break;
+
        case RAW_SFILEINFO_POSITION_INFORMATION:
                f->position = info->position_information.in.position;
                break;
+
+       default:
+               return NT_STATUS_INVALID_LEVEL;
+       }
+
+       /* possibly change the file size */
+       if (newstats.st.st_size != f->name->st.st_size) {
+               if (ftruncate(f->fd, newstats.st.st_size) == -1) {
+                       return pvfs_map_errno(pvfs, errno);
+               }
+       }
+
+       /* possibly change the file timestamps */
+       ZERO_STRUCT(unix_times);
+       if (newstats.dos.access_time != f->name->dos.access_time) {
+               unix_times.actime = nt_time_to_unix(newstats.dos.access_time);
+       }
+       if (newstats.dos.write_time != f->name->dos.write_time) {
+               unix_times.modtime = nt_time_to_unix(newstats.dos.write_time);
+       }
+       if (unix_times.actime != 0 || unix_times.modtime != 0) {
+               if (utime(f->name->full_name, &unix_times) == -1) {
+                       return pvfs_map_errno(pvfs, errno);
+               }
+       }
+
+       /* possibly change the attribute */
+       if (newstats.dos.attrib != f->name->dos.attrib) {
+               mode_t mode = pvfs_fileperms(pvfs, newstats.dos.attrib);
+               if (fchmod(f->fd, mode) == -1) {
+                       return pvfs_map_errno(pvfs, errno);
+               }
        }
 
+
        return NT_STATUS_OK;
 }
 
@@ -98,6 +168,7 @@ NTSTATUS pvfs_setpathinfo(struct ntvfs_module_context *ntvfs,
 {
        struct pvfs_state *pvfs = ntvfs->private_data;
        struct pvfs_filename *name;
+       struct pvfs_filename newstats;
        NTSTATUS status;
        struct utimbuf unix_times;
 
@@ -112,42 +183,102 @@ NTSTATUS pvfs_setpathinfo(struct ntvfs_module_context *ntvfs,
                return NT_STATUS_OBJECT_NAME_NOT_FOUND;
        }
 
+
+       /* we take a copy of the current file stats, then update
+          newstats in each of the elements below. At the end we
+          compare, and make any changes needed */
+       newstats = *name;
+
        switch (info->generic.level) {
-       case RAW_SFILEINFO_END_OF_FILE_INFO:
-       case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
-               if (truncate(name->full_name,
-                             info->end_of_file_info.in.size) == -1) {
-                       return pvfs_map_errno(pvfs, errno);
+       case RAW_SFILEINFO_SETATTR:
+               if (!null_time(info->setattr.in.write_time)) {
+                       unix_to_nt_time(&newstats.dos.write_time, info->setattr.in.write_time);
                }
-               break;
+               if (info->setattr.in.attrib != FILE_ATTRIBUTE_NORMAL) {
+                       newstats.dos.attrib = info->setattr.in.attrib;
+               }
+               break;
 
        case RAW_SFILEINFO_SETATTRE:
-               unix_times.actime = info->setattre.in.access_time;
-               unix_times.modtime = info->setattre.in.write_time;
-       
-               if (unix_times.actime == 0 && unix_times.modtime == 0) {
-                       break;
-               } 
-
-               /* set modify time = to access time if modify time was 0 */
-               if (unix_times.actime != 0 && unix_times.modtime == 0) {
-                       unix_times.modtime = unix_times.actime;
+       case RAW_SFILEINFO_STANDARD:
+               if (!null_time(info->setattre.in.create_time)) {
+                       unix_to_nt_time(&newstats.dos.create_time, info->setattre.in.create_time);
+               }
+               if (!null_time(info->setattre.in.access_time)) {
+                       unix_to_nt_time(&newstats.dos.access_time, info->setattre.in.access_time);
                }
+               if (!null_time(info->setattre.in.write_time)) {
+                       unix_to_nt_time(&newstats.dos.write_time, info->setattre.in.write_time);
+               }
+               break;
 
-               /* Set the date on this file */
-               if (utime(name->full_name, &unix_times) == -1) {
-                       return NT_STATUS_ACCESS_DENIED;
+       case RAW_SFILEINFO_BASIC_INFO:
+       case RAW_SFILEINFO_BASIC_INFORMATION:
+               if (info->basic_info.in.create_time) {
+                       newstats.dos.create_time = info->basic_info.in.create_time;
+               }
+               if (info->basic_info.in.access_time) {
+                       newstats.dos.access_time = info->basic_info.in.access_time;
+               }
+               if (info->basic_info.in.write_time) {
+                       newstats.dos.write_time = info->basic_info.in.write_time;
+               }
+               if (info->basic_info.in.change_time) {
+                       newstats.dos.change_time = info->basic_info.in.change_time;
+               }
+               if (info->basic_info.in.attrib != FILE_ATTRIBUTE_NORMAL) {
+                       newstats.dos.attrib = info->basic_info.in.attrib;
                }
                break;
 
+       case RAW_SFILEINFO_ALLOCATION_INFO:
+       case RAW_SFILEINFO_ALLOCATION_INFORMATION:
+               newstats.dos.alloc_size = info->allocation_info.in.alloc_size;
+               break;
+
+       case RAW_SFILEINFO_END_OF_FILE_INFO:
+       case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
+               newstats.st.st_size = info->end_of_file_info.in.size;
+               break;
+
        case RAW_SFILEINFO_DISPOSITION_INFO:
        case RAW_SFILEINFO_DISPOSITION_INFORMATION:
-               return NT_STATUS_INVALID_PARAMETER;
-
        case RAW_SFILEINFO_POSITION_INFORMATION:
                return NT_STATUS_OK;
+
+       default:
+               return NT_STATUS_INVALID_LEVEL;
        }
 
-       return NT_STATUS_INVALID_LEVEL;
+       /* possibly change the file size */
+       if (newstats.st.st_size != name->st.st_size) {
+               if (truncate(name->full_name, newstats.st.st_size) == -1) {
+                       return pvfs_map_errno(pvfs, errno);
+               }
+       }
+
+       /* possibly change the file timestamps */
+       ZERO_STRUCT(unix_times);
+       if (newstats.dos.access_time != name->dos.access_time) {
+               unix_times.actime = nt_time_to_unix(newstats.dos.access_time);
+       }
+       if (newstats.dos.write_time != name->dos.write_time) {
+               unix_times.modtime = nt_time_to_unix(newstats.dos.write_time);
+       }
+       if (unix_times.actime != 0 || unix_times.modtime != 0) {
+               if (utime(name->full_name, &unix_times) == -1) {
+                       return pvfs_map_errno(pvfs, errno);
+               }
+       }
+
+       /* possibly change the attribute */
+       if (newstats.dos.attrib != name->dos.attrib) {
+               mode_t mode = pvfs_fileperms(pvfs, newstats.dos.attrib);
+               if (chmod(name->full_name, mode) == -1) {
+                       return pvfs_map_errno(pvfs, errno);
+               }
+       }
+
+       return NT_STATUS_OK;
 }
 
index 12ab583082ba0dc5a023a918211399476d6d5bde..432481a88a158f1fefa66013e54e9b5e90b54417 100644 (file)
@@ -42,16 +42,23 @@ static NTSTATUS pvfs_unlink_one(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
        }
 
        /* make sure its matches the given attributes */
-       if (!pvfs_match_attrib(pvfs, name, attrib, 0)) {
+       status = pvfs_match_attrib(pvfs, name, attrib, 0);
+       if (!NT_STATUS_IS_OK(status)) {
                talloc_free(name);
-               return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+               return status;
        }
 
        status = pvfs_can_delete(pvfs, name);
        if (!NT_STATUS_IS_OK(status)) {
+               talloc_free(name);
                return status;
        }
 
+       if (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) {
+               talloc_free(name);
+               return NT_STATUS_FILE_IS_A_DIRECTORY;
+       }
+
        /* finally try the actual unlink */
        if (unlink(name->full_name) == -1) {
                status = pvfs_map_errno(pvfs, errno);
@@ -85,6 +92,11 @@ NTSTATUS pvfs_unlink(struct ntvfs_module_context *ntvfs,
                return NT_STATUS_OBJECT_NAME_NOT_FOUND;
        }
 
+       if (name->exists && 
+           (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
+               return NT_STATUS_FILE_IS_A_DIRECTORY;
+       }
+
        dir = talloc_p(req, struct pvfs_dir);
        if (dir == NULL) {
                return NT_STATUS_NO_MEMORY;
@@ -97,10 +109,18 @@ NTSTATUS pvfs_unlink(struct ntvfs_module_context *ntvfs,
        }
 
        if (dir->count == 0) {
-               return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+               return NT_STATUS_NO_SUCH_FILE;
        }
 
        for (i=0;i<dir->count;i++) {
+
+               /* this seems to be a special case */
+               if ((unl->in.attrib & FILE_ATTRIBUTE_DIRECTORY) &&
+                   (strcmp(dir->names[i], ".") == 0 ||
+                   strcmp(dir->names[i], "..") == 0)) {
+                       return NT_STATUS_OBJECT_NAME_INVALID;
+               }
+
                status = pvfs_unlink_one(pvfs, req, dir->unix_path, 
                                         dir->names[i], unl->in.attrib);
                if (NT_STATUS_IS_OK(status)) {
index ae1dc6236d6a7a06b1e38435abc870a6a57a3357..50a78c296537228fef13bd4765a87a2d2dc04452 100644 (file)
@@ -49,15 +49,17 @@ NTSTATUS pvfs_map_errno(struct pvfs_state *pvfs, int unix_errno)
   this is used by calls like unlink and search which take an attribute
   and only include special files if they match the given attribute
 */
-BOOL pvfs_match_attrib(struct pvfs_state *pvfs, struct pvfs_filename *name, 
-                      uint32_t attrib, uint32_t must_attrib)
+NTSTATUS pvfs_match_attrib(struct pvfs_state *pvfs, struct pvfs_filename *name, 
+                          uint32_t attrib, uint32_t must_attrib)
 {
-       if ((name->dos.attrib & ~attrib) & 
-           (FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_SYSTEM)) {
-               return False;
+       if ((name->dos.attrib & ~attrib) & FILE_ATTRIBUTE_DIRECTORY) {
+               return NT_STATUS_FILE_IS_A_DIRECTORY;
+       }
+       if ((name->dos.attrib & ~attrib) & (FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM)) {
+               return NT_STATUS_NO_SUCH_FILE;
        }
        if (must_attrib & ~name->dos.attrib) {
-               return False;
+               return NT_STATUS_OBJECT_NAME_NOT_FOUND;
        }
-       return True;
+       return NT_STATUS_OK;
 }