Add get_ea_names_from_file to sanely list posix xattrs
authorVolker Lendecke <vl@samba.org>
Sat, 19 Jan 2008 15:07:56 +0000 (16:07 +0100)
committerVolker Lendecke <vl@samba.org>
Sat, 19 Jan 2008 15:07:56 +0000 (16:07 +0100)
Refactor get_ea_list_from_file to use that.
(This used to be commit aec357a456798050abe565d2a744ed5f17ad5901)

source3/smbd/trans2.c

index 12b0f20879953a5b267e5deca172d747baf1f9b0..53eff65b548f03ed8a2dfd9dab51a87e41ec86b1 100644 (file)
@@ -163,86 +163,179 @@ NTSTATUS get_ea_value(TALLOC_CTX *mem_ctx, connection_struct *conn,
        return NT_STATUS_OK;
 }
 
-/****************************************************************************
- Return a linked list of the total EA's. Plus the total size
-****************************************************************************/
-
-static struct ea_list *get_ea_list_from_file(TALLOC_CTX *mem_ctx, connection_struct *conn, files_struct *fsp,
-                                       const char *fname, size_t *pea_total_len)
+NTSTATUS get_ea_names_from_file(TALLOC_CTX *mem_ctx, connection_struct *conn,
+                               files_struct *fsp, const char *fname,
+                               char ***pnames, size_t *pnum_names)
 {
        /* Get a list of all xattrs. Max namesize is 64k. */
        size_t ea_namelist_size = 1024;
-       char *ea_namelist;
+       char *ea_namelist = NULL;
+
        char *p;
+       char **names, **tmp;
+       size_t num_names;
        ssize_t sizeret;
-       int i;
-       struct ea_list *ea_list_head = NULL;
-
-       *pea_total_len = 0;
 
        if (!lp_ea_support(SNUM(conn))) {
-               return NULL;
+               *pnames = NULL;
+               *pnum_names = 0;
+               return NT_STATUS_OK;
        }
 
-       for (i = 0, ea_namelist = TALLOC_ARRAY(mem_ctx, char, ea_namelist_size); i < 6;
-            ea_namelist = TALLOC_REALLOC_ARRAY(mem_ctx, ea_namelist, char, ea_namelist_size), i++) {
+       /*
+        * TALLOC the result early to get the talloc hierarchy right.
+        */
 
-               if (!ea_namelist) {
-                       return NULL;
+       names = TALLOC_ARRAY(mem_ctx, char *, 1);
+       if (names == NULL) {
+               DEBUG(0, ("talloc failed\n"));
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       while (ea_namelist_size <= 65536) {
+
+               ea_namelist = TALLOC_REALLOC_ARRAY(
+                       names, ea_namelist, char, ea_namelist_size);
+               if (ea_namelist == NULL) {
+                       DEBUG(0, ("talloc failed\n"));
+                       TALLOC_FREE(names);
+                       return NT_STATUS_NO_MEMORY;
                }
 
                if (fsp && fsp->fh->fd != -1) {
-                       sizeret = SMB_VFS_FLISTXATTR(fsp, ea_namelist, ea_namelist_size);
+                       sizeret = SMB_VFS_FLISTXATTR(fsp, ea_namelist,
+                                                    ea_namelist_size);
                } else {
-                       sizeret = SMB_VFS_LISTXATTR(conn, fname, ea_namelist, ea_namelist_size);
+                       sizeret = SMB_VFS_LISTXATTR(conn, fname, ea_namelist,
+                                                   ea_namelist_size);
                }
 
-               if (sizeret == -1 && errno == ERANGE) {
+               if ((sizeret == -1) && (errno = ERANGE)) {
                        ea_namelist_size *= 2;
-               } else {
+               }
+               else {
                        break;
                }
        }
 
-       if (sizeret == -1)
-               return NULL;
+       if (sizeret == -1) {
+               TALLOC_FREE(names);
+               return map_nt_error_from_unix(errno);
+       }
 
-       DEBUG(10,("get_ea_list_from_file: ea_namelist size = %u\n", (unsigned int)sizeret ));
+       DEBUG(10, ("get_ea_list_from_file: ea_namelist size = %u\n",
+                  (unsigned int)sizeret));
 
-       if (sizeret) {
-               for (p = ea_namelist; p - ea_namelist < sizeret; p += strlen(p) + 1) {
-                       struct ea_list *listp;
+       if (sizeret == 0) {
+               TALLOC_FREE(names);
+               *pnames = NULL;
+               *pnum_names = 0;
+               return NT_STATUS_OK;
+       }
 
-                       if (strnequal(p, "system.", 7) || samba_private_attr_name(p))
-                               continue;
+       /*
+        * Ensure the result is 0-terminated
+        */
 
-                       listp = TALLOC_P(mem_ctx, struct ea_list);
-                       if (!listp)
-                               return NULL;
+       if (ea_namelist[sizeret-1] != '\0') {
+               TALLOC_FREE(names);
+               return NT_STATUS_INTERNAL_ERROR;
+       }
 
-                       if (!NT_STATUS_IS_OK(get_ea_value(mem_ctx, conn, fsp,
-                                                         fname, p,
-                                                         &listp->ea))) {
-                               return NULL;
-                       }
+       /*
+        * count the names
+        */
+       num_names = 0;
 
-                       {
-                               fstring dos_ea_name;
-                               push_ascii_fstring(dos_ea_name, listp->ea.name);
-                               *pea_total_len += 4 + strlen(dos_ea_name) + 1 + listp->ea.value.length;
-                               DEBUG(10,("get_ea_list_from_file: total_len = %u, %s, val len = %u\n",
-                                       (unsigned int)*pea_total_len, dos_ea_name,
-                                       (unsigned int)listp->ea.value.length ));
-                       }
-                       DLIST_ADD_END(ea_list_head, listp, struct ea_list *);
+       for (p = ea_namelist; p - ea_namelist < sizeret; p += strlen(p)+1) {
+               num_names += 1;
+       }
+
+       tmp = TALLOC_REALLOC_ARRAY(mem_ctx, names, char *, num_names);
+       if (tmp == NULL) {
+               DEBUG(0, ("talloc failed\n"));
+               TALLOC_FREE(names);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       names = tmp;
+       num_names = 0;
+
+       for (p = ea_namelist; p - ea_namelist < sizeret; p += strlen(p)+1) {
+               names[num_names++] = p;
+       }
+
+       *pnames = names;
+       *pnum_names = num_names;
+       return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Return a linked list of the total EA's. Plus the total size
+****************************************************************************/
+
+static struct ea_list *get_ea_list_from_file(TALLOC_CTX *mem_ctx, connection_struct *conn, files_struct *fsp,
+                                       const char *fname, size_t *pea_total_len)
+{
+       /* Get a list of all xattrs. Max namesize is 64k. */
+       size_t i, num_names;
+       char **names;
+       struct ea_list *ea_list_head = NULL;
+       NTSTATUS status;
+
+       *pea_total_len = 0;
+
+       if (!lp_ea_support(SNUM(conn))) {
+               return NULL;
+       }
+
+       status = get_ea_names_from_file(talloc_tos(), conn, fsp, fname,
+                                       &names, &num_names);
+
+       if (!NT_STATUS_IS_OK(status) || (num_names == 0)) {
+               return NULL;
+       }
+
+       for (i=0; i<num_names; i++) {
+               struct ea_list *listp;
+               fstring dos_ea_name;
+
+               if (strnequal(names[i], "system.", 7)
+                   || samba_private_attr_name(names[i]))
+                       continue;
+
+               listp = TALLOC_P(mem_ctx, struct ea_list);
+               if (listp == NULL) {
+                       return NULL;
                }
-               /* Add on 4 for total length. */
-               if (*pea_total_len) {
-                       *pea_total_len += 4;
+
+               if (!NT_STATUS_IS_OK(get_ea_value(mem_ctx, conn, fsp,
+                                                 fname, names[i],
+                                                 &listp->ea))) {
+                       return NULL;
                }
+
+               push_ascii_fstring(dos_ea_name, listp->ea.name);
+
+               *pea_total_len +=
+                       4 + strlen(dos_ea_name) + 1 + listp->ea.value.length;
+
+               DEBUG(10,("get_ea_list_from_file: total_len = %u, %s, val len "
+                         "= %u\n", (unsigned int)*pea_total_len, dos_ea_name,
+                         (unsigned int)listp->ea.value.length));
+
+               DLIST_ADD_END(ea_list_head, listp, struct ea_list *);
+
        }
 
-       DEBUG(10,("get_ea_list_from_file: total_len = %u\n", (unsigned int)*pea_total_len));
+       /* Add on 4 for total length. */
+       if (*pea_total_len) {
+               *pea_total_len += 4;
+       }
+
+       DEBUG(10, ("get_ea_list_from_file: total_len = %u\n",
+                  (unsigned int)*pea_total_len));
+
        return ea_list_head;
 }