On my encrypted Nexus S I use a temporary tmpfs mount on /sdcard in CWM. It has enough RAM to hold the new ROM in memory during the update:
Download your ROM to /tmp/update.zip and boot into recovery. Then log in via 'adb shell':
## on the host machine do:
me@workstation:/tmp$ adb shell
## now on the device in 'adb shell' mode...
~ # mount -t tmpfs none /sdcard/
## the following command is not needed, it only shows the newly created mount point
~ # df -h
Filesystem Size Used Available Use% Mounted on
[...]
none 172.4M 0 172.4M 0% /sdcard
~ # exit
## now back on the host machine again
me@workstation:/tmp$ adb push update.zip /sdcard/
5567 KB/s (131676307 bytes in 23.097s)
Then do the usual update steps 'install zip from sdcard'.
EDIT: Starting with ICS/Jelly Bean there's the new adb sideload <filename-of-update.zip> method
It works with CWM from version 6.0.1.5 onwards and you need the Android SDK platform-tools v16 or better. If you're in CWM you can see a new entry install zip from sideload if it's supported.
The old method still works:
If sideload doesn't work, you can still use the tmpfs method. CWM expects /data/media as the location for the update.zip now, the mountpoint has however to be /data so you have to do this now:
me@workstation$ adb shell
~ # mount -t tmpfs none /data
~ # mkdir /data/media
## Go on with 'adb push update.zip /data/media' and then like above
Reason:
Starting with ICS+ the proposed partition layout has changed. There should be no FAT formatted sdcard partition any more but the external storage now resides within /data/ (/data/media). To remain compatible, a FUSE mount emulates the old FAT properties (access rights and such). You can see this when there's a fuse mount on /storage/sdcard0, it looks similar to this:
shell@android:/ $ mount | grep fuse
[...]
/dev/fuse /storage/sdcard0 fuse rw,nosuid,nodev,relatime,user_id=1023,group_id=1023,... 0 0
[...]