[kdbus] KDBUS_ITEM_PAYLOAD_OFF items are (once again) relative to msg header
[platform/upstream/glib.git] / glib / gmappedfile.c
index 48081dc..7c8702d 100644 (file)
@@ -14,9 +14,7 @@
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
  */
 
 #include "config.h"
 #include <sys/types.h> 
 #include <sys/stat.h> 
 #include <fcntl.h>
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
 #ifdef HAVE_MMAP
 #include <sys/mman.h>
 #endif
 
 #include "glibconfig.h"
 
+#ifdef G_OS_UNIX
+#include <unistd.h>
+#endif
+
 #ifdef G_OS_WIN32
 #include <windows.h>
 #include <io.h>
 
+#undef fstat
 #define fstat(a,b) _fstati64(a,b)
+#undef stat
 #define stat _stati64
 
+#ifndef S_ISREG
+#define S_ISREG(m) (((m) & _S_IFMT) == _S_IFREG)
+#endif
+
 #endif
 
 #include "gconvert.h"
@@ -52,7 +57,6 @@
 #include "gstdio.h"
 #include "gstrfuncs.h"
 #include "gatomic.h"
-#include "gbufferprivate.h"
 
 #include "glibintl.h"
 
 #define MAP_FAILED ((void *) -1)
 #endif
 
-struct _GMappedFile 
+/**
+ * GMappedFile:
+ *
+ * The #GMappedFile represents a file mapping created with
+ * g_mapped_file_new(). It has only private members and should
+ * not be accessed directly.
+ */
+
+struct _GMappedFile
 {
   gchar *contents;
   gsize  length;
@@ -76,16 +88,6 @@ struct _GMappedFile
 #endif
 };
 
-/* Make sure the layout of GMappedFile is the same as GBuffer's */
-G_STATIC_ASSERT (G_STRUCT_OFFSET (GMappedFile, contents) ==
-                 G_STRUCT_OFFSET (GBuffer, data));
-G_STATIC_ASSERT (G_STRUCT_OFFSET (GMappedFile, length) ==
-                 G_STRUCT_OFFSET (GBuffer, size));
-G_STATIC_ASSERT (G_STRUCT_OFFSET (GMappedFile, ref_count) ==
-                 G_STRUCT_OFFSET (GBuffer, ref_count));
-G_STATIC_ASSERT (G_STRUCT_OFFSET (GMappedFile, free_func) ==
-                 G_STRUCT_OFFSET (GBuffer, free_func));
-
 static void
 g_mapped_file_destroy (GMappedFile *file)
 {
@@ -103,57 +105,15 @@ g_mapped_file_destroy (GMappedFile *file)
   g_slice_free (GMappedFile, file);
 }
 
-/**
- * g_mapped_file_new:
- * @filename: The path of the file to load, in the GLib filename encoding
- * @writable: whether the mapping should be writable
- * @error: return location for a #GError, or %NULL
- *
- * Maps a file into memory. On UNIX, this is using the mmap() function.
- *
- * If @writable is %TRUE, the mapped buffer may be modified, otherwise
- * it is an error to modify the mapped buffer. Modifications to the buffer 
- * are not visible to other processes mapping the same file, and are not 
- * written back to the file.
- *
- * Note that modifications of the underlying file might affect the contents
- * of the #GMappedFile. Therefore, mapping should only be used if the file 
- * will not be modified, or if all modifications of the file are done
- * atomically (e.g. using g_file_set_contents()). 
- *
- * Return value: a newly allocated #GMappedFile which must be unref'd
- *    with g_mapped_file_unref(), or %NULL if the mapping failed.
- *
- * Since: 2.8
- */
-GMappedFile *
-g_mapped_file_new (const gchar  *filename,
-                  gboolean      writable,
-                  GError      **error)
+static GMappedFile*
+mapped_file_new_from_fd (int           fd,
+                        gboolean      writable,
+                         const gchar  *filename,
+                         GError      **error)
 {
   GMappedFile *file;
-  int fd;
   struct stat st;
 
-  g_return_val_if_fail (filename != NULL, NULL);
-  g_return_val_if_fail (!error || *error == NULL, NULL);
-
-  fd = g_open (filename, (writable ? O_RDWR : O_RDONLY) | _O_BINARY, 0);
-  if (fd == -1)
-    {
-      int save_errno = errno;
-      gchar *display_filename = g_filename_display_name (filename);
-      
-      g_set_error (error,
-                   G_FILE_ERROR,
-                   g_file_error_from_errno (save_errno),
-                   _("Failed to open file '%s': open() failed: %s"),
-                   display_filename, 
-                  g_strerror (save_errno));
-      g_free (display_filename);
-      return NULL;
-    }
-
   file = g_slice_new0 (GMappedFile);
   file->ref_count = 1;
   file->free_func = g_mapped_file_destroy;
@@ -161,23 +121,29 @@ g_mapped_file_new (const gchar  *filename,
   if (fstat (fd, &st) == -1)
     {
       int save_errno = errno;
-      gchar *display_filename = g_filename_display_name (filename);
+      gchar *display_filename = filename ? g_filename_display_name (filename) : NULL;
 
       g_set_error (error,
                    G_FILE_ERROR,
                    g_file_error_from_errno (save_errno),
-                   _("Failed to get attributes of file '%s': fstat() failed: %s"),
-                   display_filename, 
+                   _("Failed to get attributes of file '%s%s%s%s': fstat() failed: %s"),
+                  display_filename ? display_filename : "fd",
+                  display_filename ? "' " : "",
+                  display_filename ? display_filename : "",
+                  display_filename ? "'" : "",
                   g_strerror (save_errno));
       g_free (display_filename);
       goto out;
     }
 
-  if (st.st_size == 0)
+  /* mmap() on size 0 will fail with EINVAL, so we avoid calling mmap()
+   * in that case -- but only if we have a regular file; we still want
+   * attempts to mmap a character device to fail, for example.
+   */
+  if (st.st_size == 0 && S_ISREG (st.st_mode))
     {
       file->length = 0;
       file->contents = NULL;
-      close (fd);
       return file;
     }
 
@@ -221,29 +187,124 @@ g_mapped_file_new (const gchar  *filename,
   if (file->contents == MAP_FAILED)
     {
       int save_errno = errno;
-      gchar *display_filename = g_filename_display_name (filename);
-      
+      gchar *display_filename = filename ? g_filename_display_name (filename) : NULL;
+
       g_set_error (error,
                   G_FILE_ERROR,
                   g_file_error_from_errno (save_errno),
-                  _("Failed to map file '%s': mmap() failed: %s"),
-                  display_filename,
+                  _("Failed to map %s%s%s%s: mmap() failed: %s"),
+                  display_filename ? display_filename : "fd",
+                  display_filename ? "' " : "",
+                  display_filename ? display_filename : "",
+                  display_filename ? "'" : "",
                   g_strerror (save_errno));
       g_free (display_filename);
       goto out;
     }
 
-  close (fd);
   return file;
 
  out:
-  close (fd);
   g_slice_free (GMappedFile, file);
 
   return NULL;
 }
 
 /**
+ * g_mapped_file_new:
+ * @filename: The path of the file to load, in the GLib filename encoding
+ * @writable: whether the mapping should be writable
+ * @error: return location for a #GError, or %NULL
+ *
+ * Maps a file into memory. On UNIX, this is using the mmap() function.
+ *
+ * If @writable is %TRUE, the mapped buffer may be modified, otherwise
+ * it is an error to modify the mapped buffer. Modifications to the buffer
+ * are not visible to other processes mapping the same file, and are not
+ * written back to the file.
+ *
+ * Note that modifications of the underlying file might affect the contents
+ * of the #GMappedFile. Therefore, mapping should only be used if the file
+ * will not be modified, or if all modifications of the file are done
+ * atomically (e.g. using g_file_set_contents()).
+ *
+ * If @filename is the name of an empty, regular file, the function
+ * will successfully return an empty #GMappedFile. In other cases of
+ * size 0 (e.g. device files such as /dev/null), @error will be set
+ * to the #GFileError value #G_FILE_ERROR_INVAL.
+ *
+ * Returns: a newly allocated #GMappedFile which must be unref'd
+ *    with g_mapped_file_unref(), or %NULL if the mapping failed.
+ *
+ * Since: 2.8
+ */
+GMappedFile *
+g_mapped_file_new (const gchar  *filename,
+                  gboolean      writable,
+                  GError      **error)
+{
+  GMappedFile *file;
+  int fd;
+
+  g_return_val_if_fail (filename != NULL, NULL);
+  g_return_val_if_fail (!error || *error == NULL, NULL);
+
+  fd = g_open (filename, (writable ? O_RDWR : O_RDONLY) | _O_BINARY, 0);
+  if (fd == -1)
+    {
+      int save_errno = errno;
+      gchar *display_filename = g_filename_display_name (filename);
+
+      g_set_error (error,
+                   G_FILE_ERROR,
+                   g_file_error_from_errno (save_errno),
+                   _("Failed to open file '%s': open() failed: %s"),
+                   display_filename,
+                  g_strerror (save_errno));
+      g_free (display_filename);
+      return NULL;
+    }
+
+  file = mapped_file_new_from_fd (fd, writable, filename, error);
+
+  close (fd);
+
+  return file;
+}
+
+
+/**
+ * g_mapped_file_new_from_fd:
+ * @fd: The file descriptor of the file to load
+ * @writable: whether the mapping should be writable
+ * @error: return location for a #GError, or %NULL
+ *
+ * Maps a file into memory. On UNIX, this is using the mmap() function.
+ *
+ * If @writable is %TRUE, the mapped buffer may be modified, otherwise
+ * it is an error to modify the mapped buffer. Modifications to the buffer
+ * are not visible to other processes mapping the same file, and are not
+ * written back to the file.
+ *
+ * Note that modifications of the underlying file might affect the contents
+ * of the #GMappedFile. Therefore, mapping should only be used if the file
+ * will not be modified, or if all modifications of the file are done
+ * atomically (e.g. using g_file_set_contents()).
+ *
+ * Returns: a newly allocated #GMappedFile which must be unref'd
+ *    with g_mapped_file_unref(), or %NULL if the mapping failed.
+ *
+ * Since: 2.32
+ */
+GMappedFile *
+g_mapped_file_new_from_fd (gint          fd,
+                          gboolean      writable,
+                          GError      **error)
+{
+  return mapped_file_new_from_fd (fd, writable, NULL, error);
+}
+
+/**
  * g_mapped_file_get_length:
  * @file: a #GMappedFile
  *
@@ -307,7 +368,7 @@ g_mapped_file_free (GMappedFile *file)
  * Increments the reference count of @file by one.  It is safe to call
  * this function from any thread.
  *
- * Return value: the passed in #GMappedFile.
+ * Returns: the passed in #GMappedFile.
  *
  * Since: 2.22
  **/
@@ -340,3 +401,27 @@ g_mapped_file_unref (GMappedFile *file)
   if (g_atomic_int_dec_and_test (&file->ref_count))
     g_mapped_file_destroy (file);
 }
+
+/**
+ * g_mapped_file_get_bytes:
+ * @file: a #GMappedFile
+ *
+ * Creates a new #GBytes which references the data mapped from @file.
+ * The mapped contents of the file must not be modified after creating this
+ * bytes object, because a #GBytes should be immutable.
+ *
+ * Returns: (transfer full): A newly allocated #GBytes referencing data
+ *     from @file
+ *
+ * Since: 2.34
+ **/
+GBytes *
+g_mapped_file_get_bytes (GMappedFile *file)
+{
+  g_return_val_if_fail (file != NULL, NULL);
+
+  return g_bytes_new_with_free_func (file->contents,
+                                    file->length,
+                                    (GDestroyNotify) g_mapped_file_unref,
+                                    g_mapped_file_ref (file));
+}