简体   繁体   中英

Equivalent of open(O_WRONLY | O_CREAT) in GLib?

I need to open a file for writing. If the file already exists, I don't want to truncate it.

In other words, in plain C I'd do:

int fd = open("output.bin", O_WRONLY | O_CREAT, 0666);
// I don't mind using O_RDWR, btw.

I'm trying to do something similar with GLib's GFile (part of GIO). I first tried:

g_file_create(gfile, G_FILE_CREATE_NONE, NULL, NULL);

But this fails if the file already exists.

I see that there are about 5 other functions that return GFileOutputStream or GFileIOStream , but I don't quite see one that does what I want.

Am I missing something?

Do I need to split this simple task into several small ones? (checking for file existence; if exists, create, otherwise open; all wrapped somehow in a lock.)

(BTW, if it matters: my file will reside on the local filesystem, not a networked one. Also, I'm working in Vala, which is why I don't simply use open() (maybe I could find bindings for it, but I prefer to learn the GIO way of doing things).)

I ended up doing this in two steps. I used @usr's suggestion of checking for G_IO_ERROR_EXISTS . My code:

public class Downloader {

  public FileIOStream iostream;

  public OutputStream output { get { return iostream.output_stream; } }

  public void create_output_file() throws Error
  {
    File file = File.new_for_path("output.bin");
    try {
      // If file doesn't exist.
      iostream = file.create_readwrite(NONE);
    } catch (Error e) {
      if (e is IOError.EXISTS)
        // It exists.
        iostream = file.open_readwrite();
      else
        throw e;
    }
  }

}

(There could be a race condition here, but it's irrelevant in my case.)

The Valadoc.org documentation has good examples so I won't code up a working example here. I think you need GLib.File.new_for_path () and GLib.File.append_to () . The C documentation for g_file_append_to () advises 'Gets an output stream for appending data to the file. If the file doesn't already exist it is created.' Note that append_to () takes a FileCreateFlags argument. There is also an asynchronous version of append_to () for Vala, append_to_async () .

By the way the Vala binding for open () is in the Posix VAPI. See open () , O_WRONLY and O_CREAT at Valadoc.org.

The official upstream answer is that GIO, as a high-level abstraction layer, was not originally designed to give guarantees about atomic create-or-open operations (as with O_CREAT ), so you have two options:

  • Try creating the file and, if that fails with G_IO_ERROR_EXISTS , try opening it instead; as in your answer .
  • Create/Open the file using open() with O_CREAT (as in the question), and pass the FD to g_unix_output_stream_new() to create a new GOutputStream which wraps the FD and you can write to as if it were returned by g_file_create() .

Note that g_unix_output_stream_new() isn't portable to non-Unix platforms, but that doesn't appear to be relevant in your case. This second option is the recommended one if you do care about eliminating race conditions.

Note that there are other reasons to avoid the race condition: less code, fewer syscalls (which means better performance), and more readable code (if you ignore the abrupt naming of the open() APIs).

Doing this directly with a method on GFile may be supported in future: there's an open issue about supporting it in GLib.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM