Write data to OTG device,how can I authorize for writing

When OTG is operated as a U disk, how to make the device write data? I can access the device from windows, but can’t write data.

1 Like

That function is just an example. It was set up with a very tiny file, which under loopback, looks like a read only partition. The actual utility which is customized to provide different device mode behaviors (when devices are a standardized class and not custom) is known as the “gadget” API. See:

Basically, there are many different USB devices, and some use a standardized driver so custom drivers are not needed. A series of details about your “virtual” device have to be filled in, and then the gadget API does the rest and in theory “it just works and looks like something standardized”. The location of the files for setting this up as sample code is here:

The particular micro-USB port has more than one virtual device set up. One is the filesystem you know of, the other is the USB virtual ethernet device. You can leave the ethernet device as is, and simply edit some of the filesystem device content. The file at that location which has this is “nv-l4t-device-mode-config.sh”.

Most of the other files are to stop and start the service, either manually (the “.sh” files), or through systemctl (the “service” files). You might want to stop, edit, and then start.

The file which is the actual filesystem is “filesystem.img”. This is just a file which was formatted under loopback (which makes the file look like a partition). You’d have to keep the file as an even multiple of 512 bytes, and format it again if you change the size, but basically you could change to read-write and alter that file’s size. If you want to read/write the actual Jetson filesystem, then you’re in for a lot more of a learning curve due to permissions.

I can read the OTG file, but i hope can write file to it ,I use root user to login the ubuntu ,and mount filesytem.img to /mnt ,I have not authorize for writing,

Logging in as root is not what it seems when using device mode bulk storage emulation (well, it could be if set up right, but there is long story to security for gadget filesystems).

The first requirement for a loopback file would be that the file itself is writable. Then the loopback mount operation would need to be read-write. The gadget setup may also need some options for writing. If you are talking about the actual filesystem, and not a loopback mounted file, then you would probably set up the gadget to “pretend” you are a particular user.

The real complication comes when you want to read/write to gadget storage from both inside the Jetson and via USB. Normally, for storage only accessed by one computer, the partition is mounted. Then different users can randomly work on different files of the partition. If two users collide by trying to write the same file at the same time, or where one user is writing while another user is reading the same file, then there can be operating system supported locks to tell one user he/she has to wait.

However, if the remote system sees a USB mass storage device, then it will be mounted by that system, and not merely access one file at a time. For a local user on the Jetson to access this, the same filesystem would have to be mounted on the Jetson as well. Herein is a conflict of interest, as the two systems do not talk to each other about rules of what happens when both of them operate on the same thing at the same time. Something will probably crash and burn. This would be true for VFAT, NTFS, and ext4 filesystem types.

To deal with this there are other filesystem types designed for export to multiple computers, and the host itself is aware of the sharing and deals with it (usually at a cost i performance). In Windows there is “network neighborhood” sharing (Linux uses the Samba to talk over a network within network neighborhood), but this is a network sharing and not a USB sharing. For *NIX systems there are a couple of versions of Network File System (NFS), and is much like network neighborhood, but designed for *NIX security and permissions.

Looking to your specific case, note that only root can write to “/opt/nvidia/l4t-usb-device-mode/filesystem.img”. This is ok since it is root running the gadget system for that file…no need to change that unless a different user is operating on that file.

The setup for loopback and related does need changes for making the filesystem writable. Take a look at this file in the same directory:

The command which actually tells the operating system to treat the file as a partition is losetup. The above named file contains this content near the end:

# Create a local disk device that exposes the same filesystem image that's
# exported over USB. This will allow local users to see the files too.
/sbin/losetup -f -r "${fs_img}"

The “-r” option to losetup says to pretend this is a read-only device. You can remove this to make the loopback device writable, but I’m not sure if perhaps other changes would also be needed. This would be the first change. Keep in mind that you can comment out the original line to avoid forgetting what was there after you experiment, and copy that line for edit. For example, you could edit like this:

# Create a local disk device that exposes the same filesystem image that's
# exported over USB. This will allow local users to see the files too.
#/sbin/losetup -f -r "${fs_img}"
/sbin/losetup -f "${fs_img}"

Following that change you’d probably need to reboot or restart the gadget content. This is where the scripts for stop and start come in handy (the operating system usees the “service” files to do the same thing in a format designed for systemd, but using sudo for the stop file and then the start file does the same thing…it might refuse or have an error if another system is accessing the content during the stop/start).

If this works for you, then we could talk about ways of enlarging the file.

When I mount the /opt/nvidia/l4t-usb-device-mode/filesystem.img to / MNT, I can write / read the file system on the nano side, but in the PC side,when recognizes the storage device, I can’t write, I can only read;
how shoud I modify the OTG config?
For small files, I hope I can read and write like accessing USB flash disk

Understand that different users can have different permissions. Permissions are not for the computer, they are for the account doing the read or write. Your user when directly logged in is quite likely different from the user which runs the OTG port.

Part of the issue might be if the script says to access OTG content read-only. Did you remove the read-only part in the “/opt/nvidia/l4t-usb-device-mode/” scripts (mainly “nv-l4t-usb-device-mode-start.sh”)? Note that it is losetup which causes the file to pretend to be a partition, and that the “-r” option would restrict to read-only for everyone.

Once everyone has read-write access to the loopback content (assuming you are using loopback) it still looks at security of who owns content in the partition, who is accessing it, and whether those permissions allow the access. I’m not sure of which effective user ID actually accesses the content when connected via remote USB gadget mode…but if your regular user has access, but the remote user does not, then it implies they are different users and permissions prohibit one from accessing the other’s content.

You could change the permissions of individual saved files to allow someone other than the user which created the files to read and write. If you access from a remote computer, what do you see from “ls -l” in that directory? I am assuming the losetup is already without the “-r” read-only option, otherwise not even the local user could write.

When Windows accesses the OTG device, the OTG (nano team) is only a device of windows. The windows user is certainly not the root user of nano board, but we need to read and write the OTG device as we can access the U disk
how cai I modify?

i have already modify the nv-l4t-usb-device-mode-start.sh,the following
#Create a local disk device that exposes the same filesystem image that’s
#exported over USB. This will allow local users to see the files too.
#/sbin/losetup -f -r “${fs_img}”

/sbin/losetup -f “${fs_img}”

Who the user is on the PC is completely unknown and irrelevant to the Jetson’s security. Security will in no way believe a Windows computer saying “I’m ok to log in”. The question is who the OTG port device mode code is running as while accessing the loopback filesystem in combination with what the filesystem permissions are.

I am not saying that this is necessarily wrong and causing your issues, but I want to emphasize that the code running on the Jetson depends on permissions being set up correctly, and that the user performing the access may not be who you think it is.

Combine this with the losetup command and you get more points of where it might refuse writing.

  • Is your losetup modified to not use the “-r” option?
  • Is the filesystem.img writable when using “ls -l filesystem.img” to see its permissions?
    • Be sure to examine permissions of “other”, not just user and group.
  • Is the directory where your filesystem.img writable by anyone? Probably not, and you might want to create a new location with permissions wide open:
sudo mkdir /otg
sudo cp /opt/nvidia/l4t-usb-device-mode/filesystem.img /otg/
# Be careful of who you let log in, because this opens it up to everyone:
sudo chmod ugo+rw /otg
sudo chmod ugo+rw /otg/filesystem.img
# Edit nv-l4t-usb-device-mode-start.sh:
/sbin/losetup -f "${fs_img}"
# Edit nv-l4t-usb-device-mode-config.sh:
# Reboot and try again.

What the above does is to move the image file to a new partition, and that partition is read-write to all, along with the actual file also being read-write for all. See if that works.

I modified it according to your method, but I still can’t write it

nano@nano-desktop:~/otg$ ls -l
total 16384
-rwxrwxrwx 1 nano nano 16777216 1月 4 10:42 filesystem.img

modify the file: /opt/nvidia/l4t-usb-device-mode/nv-l4t-usb-device-mode-config.sh

modify the file:/opt/nvidia/l4t-usb-device-mode/nv-l4t-usb-device-mode-start.sh
/sbin/losetup -f “${fs_img}”

nano@nano-desktop:~/otg$ mount -o loop /home/nano/otg/filesystem.img /mnt
mount: only root can use “–options” option
nano@nano-desktop:~/otg$ sudo mount -o loop /home/nano/otg/filesystem.img /mnt
[sudo] password for nano:
nano@nano-desktop:~/otg$ cd /mnt/
nano@nano-desktop:/mnt$ ls
INDEX.txt l4t-serial.inf README-usb-dev-mode.txt README-vnc.txt README-wifi.txt version
nano@nano-desktop:/mnt$ mkdir aa
mkdir: cannot create directory ‘aa’: Permission denied
nano@nano-desktop:/mnt$ sudo mkdir aa
nano@nano-desktop:/mnt$ ls
aa INDEX.txt l4t-serial.inf README-usb-dev-mode.txt README-vnc.txt README-wifi.txt version

And I look at the properties under windows. The right-click menu doesn’t even have the delete menu, which means that the file properties can’t be edited

Good for file permission, but does not necessarily say if the directory holding the file is writable:

(also note that this should not be “x” executable permission)

As a test consider copying “/opt/nvidia/l4t-usb-device-mode/filesystem.img” to a location known to have full access. It is possible not everyone has write permission within the “nano” home directory. Example giving everyone permission for test purposes:

sudo mkdir /testing
sudo chmod ugo+rwx /testing
sudo cp /opt/nvidia/l4t-usb-device-mode/filesystem.img /testing/
sudo chmod ugo+rw /testing/filesystem.img
# Edit /opt/nvidia/l4t-usb-device-mode/nv-l4t-usb-device-mode-config.sh to point there...
# Be sure the losetup command does not use "`-r`".
# Restart the service or reboot.

The reason for this is that there are no parent directories of other content which might not be accessible to users you have not planned for. Quite possibly what you’ve already done would work, but I can’t guarantee it. In the case of directory “/testing” I can guarantee everyone can write there and that the filesystem.img of that location can be written to.

If the filesystem still cannot be written to, then consider that the loopback filesystem itself has permissions. Those permissions do not change just because the loopback device itself has been modified to be writable (to not use “-r” in “losetup”). If those files are owned by root, and not writable, then that too must be fixed. As an example, here is what I see looking at permissions of one of my Jetsons when mounting “/dev/loop0”:

drwxr-xr-x  3 root root 16384 Dec 31  1969 ./
drwxr-xr-x 22 root root  4096 Jun 23  2020 ../
-rwxr-xr-x  1 root root  1104 Apr  8  2020 INDEX.txt*
-rwxr-xr-x  1 root root  4078 Apr  8  2020 l4t-serial.inf*
-rwxr-xr-x  1 root root 10291 Apr  8  2020 README-usb-dev-mode.txt*
-rwxr-xr-x  1 root root  3587 Apr  8  2020 README-vnc.txt*
-rwxr-xr-x  1 root root  1940 Apr  8  2020 README-wifi.txt*
drwxr-xr-x  3 root root  2048 Jan  1 11:27 version/

Note that this filesystem within a filesystem is writable only if you are root. If you’ve mounted the loop device read-write, then root should be able to change permissions (when losetup used “-r” not even root could change permissions). Assuming you are not currently using a micro-B USB cable, and thus the loop0 device is not yet mounted (such as after a fresh reboot when no micro-B USB cable was inserted), you could mount at some alternate location and change permissions:

sudo mount /dev/loop0 /mnt
cd /mnt
sudo chmod ugo+w *
sudo chmod ugo+w .
ls -l

Once this is done, then you’ll notice that “ls -l” now says the files are writable, and so is the current directory (the “.”, when used as a file name, implies the directory you are currently in…"." is an alias for “where you are now”…we’re adding permissions for both the files and the directory).

Possibly this might work read-write now…give it a test run.


  • Be sure loopback is not read-only.
  • Be sure the loopback file itself, and the directory it is in, are writable by all…just don’t make your home directory writable to all, that’s a huge security hole (I suggest put filesystem.img somewhere it has all to itself).
  • Be sure that when the loop device is mounted, that the permissions of whatever is inside of the device are writable by everyone, and not just root. This includes both files and the directory of the files.

I’ve done this process before;
The following is my operation process, still invalid;
Attached is the modified file:
It should Attribute error,
nano@nano-desktop:/ sudo mkdir /otg nano@nano-desktop:/ sudo chmod ugo+rwx /
/ano@nano-desktop:/ sudo cp /opt/nvidia/l4t-usb-device-mode/filesystem.img /otg/ nano@nano-desktop:/ sudo chmod ugo+rw /otg


nano@nano-desktop:/ sudo mount /dev/loop0 /mnt [ 864.129928] blk_update_request: I/O error, dev loop0, sector 2 [ 864.135843] EXT4-fs (loop0): unable to read superblock [ 864.141226] blk_update_request: I/O error, dev loop0, sector 2 [ 864.147441] EXT4-fs (loop0): unable to read superblock [ 864.153053] blk_update_request: I/O error, dev loop0, sector 2 [ 864.159352] EXT4-fs (loop0): unable to read superblock [ 864.164861] blk_update_request: I/O error, dev loop0, sector 0 [ 864.170802] SQUASHFS error: squashfs_read_data failed to read block 0x0 [ 864.177481] squashfs: SQUASHFS error: unable to read squashfs_super_block mount: /mnt: can't read superblock on /dev/loop0. nano@nano-desktop:/ mount -o loop /otg/filesystem.img /mnt
mount: only root can use “–options” option
nano@nano-desktop:/ sudo mount -o loop /otg/filesystem.img /mnt nano@nano-desktop:/ cd /mnt/
nano@nano-desktop:/mnt$ ls
INDEX.txt README-usb-dev-mode.txt README-wifi.txt
l4t-serial.inf README-vnc.txt version
nano@nano-desktop:/mnt$ ll -a
total 48
drwxr-xr-x 3 root root 16384 1月 1 1970 ./
drwxrwxrwx 26 root root 4096 1月 5 15:18 …/
-rwxr-xr-x 1 root root 7 1月 5 14:42 aa.txt*
-rwxr-xr-x 1 root root 1104 10月 16 20:44 INDEX.txt*
-rwxr-xr-x 1 root root 4078 10月 16 20:44 l4t-serial.inf*
-rwxr-xr-x 1 root root 10291 10月 16 20:44 README-usb-dev-mode.txt*
-rwxr-xr-x 1 root root 3590 10月 16 20:44 README-vnc.txt*
-rwxr-xr-x 1 root root 1940 10月 16 20:44 README-wifi.txt*
drwxr-xr-x 3 root root 2048 1月 5 11:45 version/
nano@nano-desktop:/mnt$ sudo chmod ugo+w *
nano@nano-desktop:/mnt$ sudo chmod ugo+w .
nano@nano-desktop:/mnt$ ll -a
total 48
drwxr-xr-x 3 root root 16384 1月 1 1970 ./
drwxrwxrwx 26 root root 4096 1月 5 15:18 …/
-rwxr-xr-x 1 root root 7 1月 5 14:42 aa.txt*
-rwxr-xr-x 1 root root 1104 10月 16 20:44 INDEX.txt*
-rwxr-xr-x 1 root root 4078 10月 16 20:44 l4t-serial.inf*
-rwxr-xr-x 1 root root 10291 10月 16 20:44 README-usb-dev-mode.txt*
-rwxr-xr-x 1 root root 3590 10月 16 20:44 README-vnc.txt*
-rwxr-xr-x 1 root root 1940 10月 16 20:44 README-wifi.txt*
drwxr-xr-x 3 root root 2048 1月 5 11:45 version/
nano@nano-desktop:/mnt$ touch q.txt
touch: cannot touch ‘q.txt’: Permission denied
nano@nano-desktop:/mnt$ sudo touch q.txt
nano@nano-desktop:/mnt$ ll -a
total 48
drwxr-xr-x 3 root root 16384 1月 5 19:51 ./
drwxrwxrwx 26 root root 4096 1月 5 15:18 …/
-rwxr-xr-x 1 root root 7 1月 5 14:42 aa.txt*
-rwxr-xr-x 1 root root 1104 10月 16 20:44 INDEX.txt*
-rwxr-xr-x 1 root root 4078 10月 16 20:44 l4t-serial.inf*
-rwxr-xr-x 1 root root 0 1月 5 19:51 q.txt*
-rwxr-xr-x 1 root root 10291 10月 16 20:44 README-usb-dev-mode.txt*
-rwxr-xr-x 1 root root 3590 10月 16 20:44 README-vnc.txt*
-rwxr-xr-x 1 root root 1940 10月 16 20:44 README-wifi.txt*
drwxr-xr-x 3 root root 2048 1月 5 11:45 version/
nv-l4t-usb-device-mode-config.sh (6.0 KB) nv-l4t-usb-device-mode-start.sh (9.1 KB)

I do suggest that whenever you post “code” (such as output from a terminal) that you add the “code” tag above and below what you’ve quoted. The “</>” is just a single line quote, but you’ll want to use block quote instead, and there is no visible shortcut in the forums for this. You can edit an existing post or add correct quotes while writing a new post. The block quote would be three back quote characters on their own line, once above the code block, and once below the code block (and nothing else in the line above and below the code block). The back quote is to the left of the “1” key on most keyboards. Example, but don’t use my backslash escape character:

This gets rather long, and the start is the experiment that leads up to what is probably the solution. You can skip the parts which are just experiment and go down to the actual summary at the end if you want to.

What I am finding is that you might be running into the limitations of the VFAT filesystem on Linux, which is what the filesystem.img is. Windows has no equivalent to some of the permissions and ownerships. For example, Windows has no concept of “group” or “suid” or “sticky bit”. When trying to alter those attributes on a Windows VFAT filesystem the command must fail. One cannot change the “group” of a file in VFAT because VFAT does not have groups.

This seems to be more of a problem when using VFAT with loopback. I experimented with that same file copied elsewhere and explicitly mounted as writable on a wide open directory. Operations to chown were refused. chmod did not show an error when using wildcards (such as “*”), but also did not actually chmod anything. Explicitly naming one of the files for chmod resulted in an I/O error where the command was being refused. I’m not positive, but this might be due to a need to map ownership between two separate filesystem times in combination with some limitation of loopback.

As an experiment I created an ext4 formatted version of that exact file. I copied filesystem.img to a new name, covered it with loopback, formatted as ext4, and then recursively copied the filesystem.img files into the ext4.img mount point. This worked, and I could run any chmod or chown operation I wanted without fail.

The above does not help much if you want this device to work from non-Linux systems. For example, Windows has no way to mount or use an ext4 filesystem. Windows would simply want to format the device, which might actually be useful at times, but only if you do so prior to adding any useful content. I don’t know if this would actually work since I don’t want to reboot my system to Windows while testing.

So then I repeated this, but using NTFS. Not all embedded devices understand this (e.g., some Android phone or iPhone might not understand NTFS), but a Windows PC would understand this. I copied all of those files in recursively, and they were all writable! So NTFS could work if you don’t use this with some small device which requires VFAT/fat32.

I also noticed that on the VFAT version (what the original filesystem.img has) that despite not being able to change existing files I could create new files here, which seems odd. So I specifically mounted this with the UID and GID of my user “ubuntu”. This fixed the problem!

It seems that due to the way users are mapped between Linux and Windows type filesystems that it is the mount command itself which must be used to change to another user’s ID. You will probably need to pick a non-root user’s ID to correctly mount this read-write.

If you run this command as your regular user it will print uid and gid (along with some supplemental groups, which you can probably ignore):
…or, if you want to see the uid/gid of another user, using user name “ubuntu” as an example:
id ubuntu

Suppose the uid and gid turn out to be “1000”. Also, assume that that user name is “ubuntu”. This mount command will mount a loopback VFAT partition as writable by that user (and also to root):
sudo mount -o loop,rw,uid=1000,gid=1000 ./filesystem.img /otg/mnt
(I’m pretending you already created “/otg/mnt” and that your regular user can write there, and that the filesystem.img is itself writable)

Then if you cd /otg/mnt you should see this from ls -l (or something similar):

drwxr-xr-x 3 ubuntu ubuntu 16384 Dec 31  1969 ./
drwxrwxrwx 5 root   root    4096 Jan  5 14:19 ../
-rwxr-xr-x 1 ubuntu ubuntu  1104 Jan  5 14:12 INDEX.txt*
-rwxr-xr-x 1 ubuntu ubuntu  4078 Jan  5 14:12 l4t-serial.inf*
-rwxr-xr-x 1 ubuntu ubuntu 10173 Jan  5 14:12 log.txt*
-rwxr-xr-x 1 ubuntu ubuntu 10291 Jan  5 14:12 README-usb-dev-mode.txt*
-rwxr-xr-x 1 ubuntu ubuntu  3587 Jan  5 14:12 README-vnc.txt*
-rwxr-xr-x 1 ubuntu ubuntu  1940 Jan  5 14:12 README-wifi.txt*
drwxr-xr-x 3 ubuntu ubuntu  2048 Jan  5 14:12 version/

Notice that since the files are owned by user ID 1000, “ubuntu” on my system, that the “group” and “other” permissions don’t matter. User “ubuntu” can alter those files.

As an experiment create “/otg” and “/otg/mnt”. Make “/otg/mnt” writable by your regular user. For example:

# We want all of the commands to be run sudo, so drop into an sudo shell:
sudo -s
mkdir /otg
mkdir /otg/mnt
cp /opt/nvidia/l4t-usb-device-mode/filesystem.img /otg
# Under Linux we will make this group "ubuntu".
chown root.ubuntu /otg/filesysystem.img
# We will make the underlying file writable by group, which happens to be "ubuntu":
chmod g+w /otg/filesystem.img
# Make sure your uid and gid are correct for your non-root user, I will assume "1000" and user name "ubuntu":
id ubuntu
# Now mount with uid and gid of user "ubuntu" instead of defaulting to root:
mount -o loop,rw,uid=1000,gid=1000 /otg/filesystem.img /otg/mnt
# Log in to your regular user by exiting the suid root shell:
# Now verify you can read and write:
cd /otg/mnt
# Do things, e.g., edit a file
# Now you can umount if done:
sudo umount /otg/mnt

A summary:

  • A VFAT partition does not support chown, the mount itself must contain the uid/gid of who to mount as.
  • Use “id” or “id username” to see a gid or uid.
  • The loopback setup option to force the loopback device itself is not enough to make everything writable, but it is still required. To do so this is the new line in the USB setup script (I leave out “-r”):
    /sbin/losetup -f "${fs_img}"
  • The actual image I assume is moved to a new location for security reasons, e.g., to “/otg”, and this is specified by adjusting “fs_img”, e.g., edit nv-l4t-usb-device-mode-config.sh:
  • The mount command itself would specify both rw and user ID in “nv-l4t-usb-device-mode-start.sh”, and the new mntpoint and fs_img point to the correct locations:
    mount -o loop,rw,uid=1000,gid=1000 "${fs_img}" "${mntpoint}"

Give that a try. Keep in mind that I don’t know yet who the OTG software is running as (other than probably root), so there might still be adjustments. See how far that gets.

and I connect ubuntu PC and nano borad OTG port by usb cable;
I process the OTG following:

sudo mkdir /otg

sudo cp /opt/nvidia/l4t-usb-device-mode/filesystem.img /otg/

sudo chmod ugo+rw /otg

sudo chmod ugo+rw /otg/filesystem.img

#modify nv-l4t-usb-device-mode-start.sh:

/sbin/losetup -f "${fs_img}"

#modify nv-l4t-usb-device-mode-config.sh:



then I can not write the OTG device though the OTG;

What UID and GID did you use? Remember that for VFAT it seems that you must name a specific non-root user. Most often that would be UID and GID of 1000 (which is the first account ID).

The mount command itself is in “nv-l4t-usb-device-mode-start.sh”, and this would require:
mount -o loop,rw,uid=1000,gid=1000 "${fs_img}" "${mntpoint}"
(you can use a different uid/gid, but leaving this out marks it as owned by root)

At some time prior to ever inserting the micro-B USB cable you might want to verify the filesystem itself accepts write. For example, if not already covered by loopback:

sudo mkdir /otg/mnt
sudo chmod go+rw /otg/mnt
sudo mount -o loop,rw,uid=1000,gid=1000 /otg/filesystem.img /otg/mnt
cd /otg/mnt
# ...see if you can edit or change files...
sudo umount /otg/mnt

Notice that I changed the options in the “-o” of the mount command. Without that write will probably fail.

I can edit and write the file according to your operation in the nano borad;
but I can not chage the file in the PC

we hope that we can write the file in the PC ,throght the OTG

In the file nv-l4t-usb-device-mode-start.sh, it is suceess the following state
umount “{mntpoint}" rm -rf "{mntpoint}”

but OTG,in the pc, I can not control the dir “/otg/mnt”, so in pc ,I can not modify the OTG,That is, the file(/otg/filesystem.image or /otg/mnt) cannot be written,

nv-l4t-usb-device-mode-start.sh (9.5 KB)

I have some observations, but not a fix. Your mount command was correct in the shell script, and so the user with ID 1000 (your first created user) can read/write.

If you ran this as root I think by default it would be forced read-only (but UID/GID 1000 implies it is run as that user owning the filesystem).

Has anyone here set up a VFAT loopback USB filesystem for writing to under the USB gadget code?

sudo dd if=/dev/zero of=/otg/filesystem.img bs=1M count=20
sudo mkfs.vfat -F 32 /otg/filesystem.img

Those steps are almost correct:

The problem is that you must first cover filesystem.img with “sudo losetup --find --show /otg/filesystem.img”, and then name the loopback device as an argument to mkfs.vfat instead of naming the file. For example, if the loop device is “/dev/loop0”, then you’d alter the mkfs.vfat line as follows:
sudo mkfs.vfat -F 32 /dev/loop0